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

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

Desks: Exclude freeform tasks from desktop in unsupported display

CTS may force launch a freeform task into an overlay display that does
not currently support desktop windowing. In such cases,
DesktopTasksChangeListener should refrain from adding them to the
DesktopRepository (i.e. track the as "desktop" tasks). When multiple
desks are enabled, there is an expectation that a desk will exist prior
to adding freeform tasks to it, and because a desk is already correctly
not created in unsupported displays, this expectation was failing and
leading to a crash during the CTS run.

Flag: com.android.window.flags.enable_multiple_desktops_backend
Fix: 409147885
Test: atest FreeformWindowingModeTests DesktopTaskChangeListenerTest
Change-Id: I2f3c37d47c566b44b055a2ce402fe1ab3afd7655
parent fbd99f24
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -923,7 +923,8 @@ public abstract class WMShellModule {
            DesktopState desktopState) {
        if (ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()
                && desktopState.canEnterDesktopMode()) {
            return Optional.of(new DesktopTaskChangeListener(desktopUserRepositories));
            return Optional.of(new DesktopTaskChangeListener(
                    desktopUserRepositories, desktopState));
        }
        return Optional.empty();
    }
+37 −3
Original line number Diff line number Diff line
@@ -18,17 +18,27 @@ package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.freeform.TaskChangeListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopState

/** Manages tasks handling specific to Android Desktop Mode. */
class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUserRepositories) :
    TaskChangeListener {
class DesktopTaskChangeListener(
    private val desktopUserRepositories: DesktopUserRepositories,
    private val desktopState: DesktopState,
) : TaskChangeListener {

    override fun onTaskOpening(taskInfo: RunningTaskInfo) {
        logD("onTaskOpening for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            return
        }
        val desktopRepository: DesktopRepository =
            desktopUserRepositories.getProfile(taskInfo.userId)
        if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
@@ -42,6 +52,12 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser

    override fun onTaskChanging(taskInfo: RunningTaskInfo) {
        logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            return
        }
        val desktopRepository: DesktopRepository =
            desktopUserRepositories.getProfile(taskInfo.userId)
        // TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
@@ -78,6 +94,12 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser

    override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
        logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            return
        }
        val desktopRepository: DesktopRepository =
            desktopUserRepositories.getProfile(taskInfo.userId)
        // When the task change is from a task in the desktop repository which is now fullscreen,
@@ -93,15 +115,27 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
    }

    override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
        logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            return
        }
        val desktopRepository: DesktopRepository =
            desktopUserRepositories.getProfile(taskInfo.userId)
        if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
        logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */ false)
    }

    override fun onTaskClosing(taskInfo: RunningTaskInfo) {
        logD("onTaskClosing for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
        if (
            !desktopState.isDesktopModeSupportedOnDisplay(taskInfo.displayId) &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            return
        }
        val desktopRepository: DesktopRepository =
            desktopUserRepositories.getProfile(taskInfo.userId)
        if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+76 −1
Original line number Diff line number Diff line
@@ -21,13 +21,17 @@ import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
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.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -46,10 +50,15 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {

    private val desktopUserRepositories = mock<DesktopUserRepositories>()
    private val desktopRepository = mock<DesktopRepository>()
    private val desktopState =
        FakeDesktopState().apply {
            canEnterDesktopMode = true
            overrideDesktopModeSupportPerDisplay[UNSUPPORTED_DISPLAY_ID] = false
        }

    @Before
    fun setUp() {
        desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
        desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories, desktopState)

        whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
        whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
@@ -98,6 +107,18 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
    }

    @Test
    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onTaskOpening_desktopModeNotSupportedInDisplay_noOp() {
        val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)
        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)

        desktopTaskChangeListener.onTaskOpening(task)

        verify(desktopUserRepositories.current, never())
            .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
    }

    @Test
    fun onTaskChanging_fullscreenTask_activeInDesktopRepository_removesTaskFromRepo() {
        val task = createFullscreenTask().apply { isVisible = true }
@@ -127,6 +148,17 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
    }

    @Test
    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onTaskChanging_desktopModeNotSupportedInDisplay_noOp() {
        val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)

        desktopTaskChangeListener.onTaskChanging(task)

        verify(desktopUserRepositories.current, never())
            .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
    }

    @Test
    fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
        val task = createFullscreenTask().apply { isVisible = true }
@@ -156,6 +188,17 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
        verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
    }

    @Test
    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onTaskMovingToFront_desktopModeNotSupportedInDisplay_noOp() {
        val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID).apply { isVisible = true }

        desktopTaskChangeListener.onTaskMovingToFront(task)

        verify(desktopUserRepositories.current, never())
            .addTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
    }

    @Test
    fun onTaskMovingToBack_activeTaskInRepo_updatesTask() {
        val task = createFreeformTask().apply { isVisible = true }
@@ -178,6 +221,18 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
            .updateTask(task.displayId, task.taskId, /* isVisible= */ false)
    }

    @Test
    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onTaskMovingToBack_desktopModeNotSupportedInDisplay_noOp() {
        val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID)
        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)

        desktopTaskChangeListener.onTaskMovingToBack(task)

        verify(desktopUserRepositories.current, never())
            .updateTask(displayId = eq(UNSUPPORTED_DISPLAY_ID), taskId = any(), isVisible = any())
    }

    @Test
    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
@@ -218,4 +273,24 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
        verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
        verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
    }

    @Test
    @EnableFlags(
        FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
        FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    fun onTaskClosing_desktopModeNotSupportedInDisplay_noOp() {
        val task = createFreeformTask(UNSUPPORTED_DISPLAY_ID).apply { isVisible = true }
        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
        whenever(desktopUserRepositories.current.isClosingTask(task.taskId)).thenReturn(true)

        desktopTaskChangeListener.onTaskClosing(task)

        verify(desktopUserRepositories.current, never()).removeClosingTask(task.taskId)
        verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
    }

    companion object {
        private const val UNSUPPORTED_DISPLAY_ID = 3
    }
}