Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08000947 authored by Jorge Gil's avatar Jorge Gil
Browse files

[34/N] Desks: Implement task-limit policy

Adds the missing task-limit and minimization policies needed when a task
is being added to a desk that weren't functional with multiple desks
enabled.

Instead of relying on the task to minimize being left behind Home,
DesksOrganizer#minimizeTask is used to send the task to the minimization
container of its desk.

Flag: com.android.window.flags.enable_multiple_desktops_backend
Bug: 391485148
Test: Move a task to a desk that's at the limit, verify it minimizes the
one at the back.

Change-Id: Id4773618dc62a667b2deb9268f7d6d9c11287d20
parent 7572fb79
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -873,6 +873,7 @@ public abstract class WMShellModule {
            Transitions transitions,
            @DynamicOverride DesktopUserRepositories desktopUserRepositories,
            ShellTaskOrganizer shellTaskOrganizer,
            DesksOrganizer desksOrganizer,
            InteractionJankMonitor interactionJankMonitor,
            @ShellMainThread Handler handler) {
        int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
@@ -885,6 +886,7 @@ public abstract class WMShellModule {
                        transitions,
                        desktopUserRepositories,
                        shellTaskOrganizer,
                        desksOrganizer,
                        maxTaskLimit <= 0 ? null : maxTaskLimit,
                        interactionJankMonitor,
                        context,
+1 −1
Original line number Diff line number Diff line
@@ -485,7 +485,7 @@ class DesktopRepository(
    fun getExpandedTasksOrdered(displayId: Int): List<Int> =
        getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }

    @VisibleForTesting
    /** Returns all active non-minimized tasks for [deskId] ordered from top to bottom. */
    fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> =
        getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) }

+26 −9
Original line number Diff line number Diff line
@@ -1009,11 +1009,14 @@ class DesktopTasksController(
        displayId: Int = DEFAULT_DISPLAY,
        unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
    ): IBinder {
        val deskId =
            launchingTaskId?.let { taskId -> taskRepository.getDeskIdForTask(taskId) }
                ?: getDefaultDeskId(displayId)
        // TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
        var launchTransaction = wct
        val taskIdToMinimize =
            addAndGetMinimizeChanges(
                displayId,
                deskId,
                launchTransaction,
                newTaskId = launchingTaskId,
                launchingNewIntent = launchingTaskId == null,
@@ -2224,6 +2227,7 @@ class DesktopTasksController(
            logV("skip keyguard is locked")
            return null
        }
        val deskId = getDefaultDeskId(task.displayId)
        val wct = WindowContainerTransaction()
        if (shouldFreeformTaskLaunchSwitchToFullscreen(task)) {
            logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId)
@@ -2246,7 +2250,6 @@ class DesktopTasksController(
                runOnTransitStart?.invoke(transition)
                return wct
            }
            val deskId = getDefaultDeskId(task.displayId)
            val runOnTransitStart = addDeskActivationChanges(deskId, wct, task)
            runOnTransitStart?.invoke(transition)
            wct.reorder(task.token, true)
@@ -2288,7 +2291,7 @@ class DesktopTasksController(
            reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
        )
        // 2) minimize a Task if needed.
        val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
        val taskIdToMinimize = addAndGetMinimizeChanges(deskId, wct, task.taskId)
        addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
        if (taskIdToMinimize != null) {
            addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
@@ -2334,7 +2337,7 @@ class DesktopTasksController(
                            // The desk was already showing and we're launching a new Task - we
                            // might need to minimize another Task.
                            val taskIdToMinimize =
                                addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
                                addAndGetMinimizeChanges(deskId, wct, task.taskId)
                            taskIdToMinimize?.let { minimizingTaskId ->
                                addPendingMinimizeTransition(
                                    transition,
@@ -2670,7 +2673,7 @@ class DesktopTasksController(

    /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
    private fun addAndGetMinimizeChanges(
        displayId: Int,
        deskId: Int,
        wct: WindowContainerTransaction,
        newTaskId: Int?,
        launchingNewIntent: Boolean = false,
@@ -2679,7 +2682,7 @@ class DesktopTasksController(
        require(newTaskId == null || !launchingNewIntent)
        return desktopTasksLimiter
            .get()
            .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
            .addAndGetMinimizeTaskChanges(deskId, wct, newTaskId, launchingNewIntent)
    }

    private fun addPendingMinimizeTransition(
@@ -2782,9 +2785,20 @@ class DesktopTasksController(
        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
            doesAnyTaskRequireTaskbarRounding(displayId)
        )
        // TODO: b/362720497 - activating a desk with the intention to move a new task to
        //  it means we may need to minimize something in the activating desk. Do so here
        //  similar to how it's done in #bringDesktopAppsToFront.
        val expandedTasksOrderedFrontToBack =
            taskRepository.getExpandedTasksIdsInDeskOrdered(deskId = deskId)
        // If we're adding a new Task we might need to minimize an old one
        val taskIdToMinimize =
            desktopTasksLimiter
                .getOrNull()
                ?.getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
        if (taskIdToMinimize != null) {
            val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
            // TODO(b/365725441): Handle non running task minimization
            if (taskToMinimize != null) {
                desksOrganizer.minimizeTask(wct, deskId, taskToMinimize)
            }
        }
        return { transition ->
            val activateDeskTransition =
                if (newTaskIdInFront != null) {
@@ -2802,6 +2816,9 @@ class DesktopTasksController(
                    )
                }
            desksTransitionObserver.addPendingTransition(activateDeskTransition)
            taskIdToMinimize?.let { minimizingTask ->
                addPendingMinimizeTransition(transition, minimizingTask, MinimizeReason.TASK_LIMIT)
            }
        }
    }

+12 −5
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
@@ -31,6 +32,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.UserChangeListener
@@ -48,6 +50,7 @@ class DesktopTasksLimiter(
    transitions: Transitions,
    private val desktopUserRepositories: DesktopUserRepositories,
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val desksOrganizer: DesksOrganizer,
    private val maxTasksLimit: Int?,
    private val interactionJankMonitor: InteractionJankMonitor,
    private val context: Context,
@@ -258,7 +261,7 @@ class DesktopTasksLimiter(
     * returning the task to minimize.
     */
    fun addAndGetMinimizeTaskChanges(
        displayId: Int,
        deskId: Int,
        wct: WindowContainerTransaction,
        newFrontTaskId: Int?,
        launchingNewIntent: Boolean = false,
@@ -267,15 +270,19 @@ class DesktopTasksLimiter(
        val taskRepository = desktopUserRepositories.current
        val taskIdToMinimize =
            getTaskIdToMinimize(
                taskRepository.getExpandedTasksOrdered(displayId),
                taskRepository.getExpandedTasksIdsInDeskOrdered(deskId),
                newFrontTaskId,
                launchingNewIntent,
            )
        // If it's a running task, reorder it to back.
        taskIdToMinimize
            ?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
            // TODO: b/391485148 - this won't really work with multi-desks enabled.
            ?.let { wct.reorder(it.token, /* onTop= */ false) }
            ?.let { task ->
                if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
                    wct.reorder(task.token, /* onTop= */ false)
                } else {
                    desksOrganizer.minimizeTask(wct, deskId, task)
                }
            }
        return taskIdToMinimize
    }

+293 −12
Original line number Diff line number Diff line
@@ -323,6 +323,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                transitions,
                userRepositories,
                shellTaskOrganizer,
                desksOrganizer,
                MAX_TASK_LIMIT,
                mockInteractionJankMonitor,
                mContext,
@@ -2385,9 +2386,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveTaskToFront_bringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
        setUpHomeTask()
        val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
        val freeformTasks =
            (1..MAX_TASK_LIMIT + 1).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
            }
        whenever(
                desktopMixedTransitionHandler.startLaunchTransition(
                    eq(TRANSIT_TO_FRONT),
@@ -2407,6 +2412,32 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        wct.assertReorderAt(1, freeformTasks[1], toTop = false)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveTaskToFront_bringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
        val deskId = 0
        setUpHomeTask()
        val freeformTasks =
            (1..MAX_TASK_LIMIT + 1).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        whenever(
                desktopMixedTransitionHandler.startLaunchTransition(
                    eq(TRANSIT_TO_FRONT),
                    any(),
                    eq(freeformTasks[0].taskId),
                    anyOrNull(),
                    anyOrNull(),
                )
            )
            .thenReturn(Binder())

        controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)

        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[1])
    }

    @Test
    fun moveTaskToFront_minimizedTask_marksTaskAsUnminimized() {
        val transition = Binder()
@@ -2496,8 +2527,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        val task = createRecentTaskInfo(1001)
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
        whenever(
@@ -2519,6 +2555,33 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        wct.assertReorderAt(1, freeformTasks[0], toTop = false)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        val task = createRecentTaskInfo(1001)
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
        whenever(
                desktopMixedTransitionHandler.startLaunchTransition(
                    eq(TRANSIT_OPEN),
                    any(),
                    eq(task.taskId),
                    anyOrNull(),
                    anyOrNull(),
                )
            )
            .thenReturn(Binder())

        controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)

        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
    }

    @Test
    fun moveToNextDisplay_noOtherDisplays() {
        whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
@@ -3530,7 +3593,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksDisabled_dontMinimize() {
        val freeformTask = setUpFreeformTask()
        markTaskVisible(freeformTask)
        val fullscreenTask = createFullscreenTask()
@@ -3543,7 +3607,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksEnabled_dontMinimize() {
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        val freeformTask =
            setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
                markTaskVisible(it)
            }

        // Launch a fullscreen task while in the desk.
        val fullscreenTask = createFullscreenTask()
        val transition = Binder()
        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))

        assertNotNull(wct)
        verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(deskId), any())
        assertNull(desktopTasksLimiter.getMinimizingTask(transition))
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
        freeformTasks.forEach { markTaskVisible(it) }
        val fullscreenTask = createFullscreenTask()
@@ -3557,7 +3643,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
                    markTaskVisible(it)
                }
            }

        // Launch a fullscreen task while in the desk.
        setUpHomeTask()
        val fullscreenTask = createFullscreenTask()
        val transition = Binder()
        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))

        assertNotNull(wct)
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
        val minimizingTask =
            assertNotNull(desktopTasksLimiter.getMinimizingTask(transition)?.taskId)
        assertThat(minimizingTask).isEqualTo(freeformTasks[0].taskId)
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
        freeformTasks.forEach { markTaskVisible(it) }
        val fullscreenTask = createFullscreenTask()
@@ -3578,7 +3691,31 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        freeformTasks.forEach { markTaskVisible(it) }
        val fullscreenTask = createFullscreenTask()
        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)

        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))

        assertNotNull(wct)
        // The launching task is moved to the desk.
        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
        // The bottom-most task is minimized.
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksDisabled_existingAndNewTasksAreMinimized() {
        val minimizedTask = setUpFreeformTask()
        taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
@@ -3603,6 +3740,88 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksEnabled_existingAndNewTasksAreMinimized() {
        // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        val minimizedTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.minimizeTaskInDesk(
            displayId = DEFAULT_DISPLAY,
            deskId = deskId,
            taskId = minimizedTask.taskId,
        )
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
                    markTaskVisible(it)
                }
            }

        // Launch a fullscreen task that brings Home to front with it.
        setUpHomeTask()
        val fullscreenTask = createFullscreenTask()
        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))

        assertNotNull(wct)
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_fullscreenTaskWithTaskOnHome_taskAddedToDesk() {
        // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)

        // Launch a fullscreen task that brings Home to front with it.
        setUpHomeTask()
        val fullscreenTask = createFullscreenTask()
        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))

        assertNotNull(wct)
        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
        Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
    )
    fun handleRequest_fullscreenTaskWithTaskOnHome_activatesDesk() {
        val deskId = 5
        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)

        // Launch a fullscreen task that brings Home to front with it.
        val homeTask = setUpHomeTask()
        val fullscreenTask = createFullscreenTask()
        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
        val transition = Binder()
        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))

        assertNotNull(wct)
        wct.assertReorder(homeTask, toTop = true)
        wct.assertReorder(wallpaperToken, toTop = true)
        verify(desksOrganizer).activateDesk(wct, deskId)
        verify(desksTransitionsObserver)
            .addPendingTransition(
                DeskTransition.ActiveDeskWithTask(
                    token = transition,
                    displayId = DEFAULT_DISPLAY,
                    deskId = deskId,
                    enterTaskId = fullscreenTask.taskId,
                )
            )
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
    fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
@@ -3663,8 +3882,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }

    @Test
    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksDisabled_minimize() {
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        freeformTasks.forEach { markTaskVisible(it) }
        val newFreeformTask = createFreeformTask()

@@ -3675,6 +3899,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksEnabled_minimize() {
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        freeformTasks.forEach { markTaskVisible(it) }
        val newFreeformTask = createFreeformTask()

        val wct =
            controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))

        assertNotNull(wct)
        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() {
@@ -5602,9 +5844,14 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
    fun openInstance_fromFreeform_minimizesIfNeeded() {
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun openInstance_fromFreeform_multiDesksDisabled_minimizesIfNeeded() {
        setUpLandscapeDisplay()
        val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT + 1).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        val oldestTask = freeformTasks.first()
        val newestTask = freeformTasks.last()

@@ -5629,6 +5876,40 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        wct.assertReorderAt(1, oldestTask, toTop = false)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    fun openInstance_fromFreeform_multiDesksEnabled_minimizesIfNeeded() {
        setUpLandscapeDisplay()
        val deskId = 0
        val freeformTasks =
            (1..MAX_TASK_LIMIT + 1).map { _ ->
                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
            }
        val oldestTask = freeformTasks.first()
        val newestTask = freeformTasks.last()

        val transition = Binder()
        val wctCaptor = argumentCaptor<WindowContainerTransaction>()
        whenever(
                desktopMixedTransitionHandler.startLaunchTransition(
                    anyInt(),
                    wctCaptor.capture(),
                    anyInt(),
                    anyOrNull(),
                    anyOrNull(),
                )
            )
            .thenReturn(transition)

        runOpenInstance(newestTask, freeformTasks[1].taskId)

        val wct = wctCaptor.firstValue
        verify(desksOrganizer).minimizeTask(wct, deskId, oldestTask)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
    fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
Loading