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

Commit 2a33f633 authored by Kazuki Takise's avatar Kazuki Takise Committed by Android (Google) Code Review
Browse files

Merge "Clean up invisible tasks in task repo when they are removed" into main

parents 51311c5a bf2c11dc
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -868,10 +868,11 @@ public abstract class WMShellModule {
            @NonNull ShellCommandHandler shellCommandHandler,
            @NonNull ShellTaskOrganizer shellTaskOrganizer,
            @NonNull LaunchAdjacentController launchAdjacentController,
            @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
    ) {
            @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            @NonNull Optional<TaskChangeListener> taskChangeListener) {
        return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer,
                launchAdjacentController, rootTaskDisplayAreaOrganizer);
                launchAdjacentController, rootTaskDisplayAreaOrganizer,
                taskChangeListener);
    }

    @WMSingleton
+27 −0
Original line number Diff line number Diff line
@@ -176,6 +176,33 @@ class DesktopTaskChangeListener(
        )
    }

    // This method should only be used for scenarios where the task close events are not propagated
    // to [DesktopTaskChangeListener#onTaskClosing] via [TransitionsObserver].
    // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
    // of race conditions and possible duplications with [onTaskClosing].
    override fun onNonTransitionTaskClosing(taskInfo: RunningTaskInfo) {
        logD(
            "onNonTransitionTaskClosing for taskId=%d, displayId=%d",
            taskInfo.taskId,
            taskInfo.displayId,
        )

        if (DesktopExperienceFlags.ENABLE_DESKTOP_INVISIBLE_TASK_REMOVAL_CLEANUP_BUGFIX.isTrue) {
            // Removing an invisible task is an invisible->invisible change, so no shell transition
            // runs for this, and DesktopRepository misses cleaning up task data, which could lead
            // to DesktopTasksController incorrectly trying to restore the tasks when the desk is
            // reactivated next time. See b/361419732.
            val repository: DesktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
            if (
                repository.getDeskIdForTask(taskInfo.taskId) != null &&
                    !repository.isVisibleTask(taskInfo.taskId)
            ) {
                repository.removeClosingTask(taskInfo.taskId)
                repository.removeTask(taskInfo.taskId)
            }
        }
    }

    override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
+13 −2
Original line number Diff line number Diff line
@@ -41,10 +41,12 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.LaunchAdjacentController
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
import com.android.wm.shell.freeform.TaskChangeListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import java.io.PrintWriter
import java.util.Optional

/**
 * A [DesksOrganizer] that uses root tasks as the container of each desk.
@@ -59,6 +61,7 @@ class RootTaskDesksOrganizer(
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val launchAdjacentController: LaunchAdjacentController,
    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
    private val taskChangeListener: Optional<TaskChangeListener>,
) : DesksOrganizer, ShellTaskOrganizer.TaskListener {

    private val createDeskRootRequests = mutableListOf<CreateDeskRequest>()
@@ -619,7 +622,7 @@ class RootTaskDesksOrganizer(
        deskRootsByDeskId.forEach { deskId, deskRoot ->
            if (deskRoot.children.remove(taskInfo.taskId)) {
                logV("Task #${taskInfo.taskId} vanished from desk #$deskId")
                childLeashes.remove(taskInfo.taskId)
                cleanUpChildTask(taskInfo)
                return
            }
        }
@@ -628,12 +631,20 @@ class RootTaskDesksOrganizer(
            val taskId = taskInfo.taskId
            if (root.children.remove(taskId)) {
                logV("Task #$taskId vanished from minimization root of desk #${root.deskId}")
                childLeashes.remove(taskInfo.taskId)
                cleanUpChildTask(taskInfo)
                return
            }
        }
    }

    private fun cleanUpChildTask(taskInfo: RunningTaskInfo) {
        childLeashes.remove(taskInfo.taskId)

        // Notify task close events to the [TaskChangeListener] since [TransitionsObserver]
        // does not trigger them when invisible tasks are removed.
        taskChangeListener.ifPresent { listener -> listener.onNonTransitionTaskClosing(taskInfo) }
    }

    private fun createDeskMinimizationRoot(
        displayId: Int,
        deskId: Int,
+9 −0
Original line number Diff line number Diff line
@@ -39,6 +39,15 @@ interface TaskChangeListener {
     */
    fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo)

    /**
     * Notifies a task close event on the given task from [RootTaskDesksOrganizer].
     *
     * This is used to propagate task close signals since not all task close events are propagated
     * from [TransitionObserver] in [onTaskClosing]. It is recommended to use [onTaskClosing]
     * instead of this method where possible.
     */
    fun onNonTransitionTaskClosing(taskInfo: RunningTaskInfo)

    /** Notifies a task moving to the front. */
    fun onTaskMovingToFront(taskInfo: RunningTaskInfo)

+15 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.server.am.Flags.FLAG_PERCEPTIBLE_TASKS
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_INVISIBLE_TASK_REMOVAL_CLEANUP_BUGFIX
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
import com.android.window.flags.Flags.FLAG_MOVE_TO_NEXT_DISPLAY_SHORTCUT_WITH_PROJECTED_MODE
@@ -46,6 +47,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@@ -530,6 +532,19 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
        assertThat(desktopTaskChangeListener.isTaskPerceptible(task.taskId)).isFalse()
    }

    @Test
    @EnableFlags(FLAG_ENABLE_DESKTOP_INVISIBLE_TASK_REMOVAL_CLEANUP_BUGFIX)
    fun onNonTransitionTaskClosing_invisibleFreeformTask_removesTaskFromRepo() {
        val task = createFreeformTask().apply { isVisible = false }
        desktopTaskChangeListener.onTaskOpening(task)

        whenever(desktopUserRepositories.current.isVisibleTask(task.taskId)).thenReturn(false)
        desktopTaskChangeListener.onNonTransitionTaskClosing(task)

        verify(desktopUserRepositories.current, times(1)).removeClosingTask(task.taskId)
        verify(desktopUserRepositories.current, times(1)).removeTask(task.taskId)
    }

    private fun createWallpaperTaskInfo(windowingMode: Int): RunningTaskInfo =
        TestRunningTaskInfoBuilder()
            .setBaseIntent(
Loading