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

Commit 39ddc96c authored by Eghosa Ewansiha-Vlachavas's avatar Eghosa Ewansiha-Vlachavas
Browse files

[1/n] Isolate desktop entry point disabling heuristic from window exemptions

The windowing exemptions logic is used for both exempting apps from
opening in desktop but also hiding the entry point so apps cannot be
moved into desktop. However, disabling desktop entry points is not
constrained by the same security restrictions as exempting apps from
opening as freeform in desktop. Instead we should have a separate
heuristic for disabling entry points and forcing to fullscreen.

Flag: EXEMPT Refactoring
Fixes: 407704088
Test: atest WMShellUnitTests:DesktopModeCompatPolicyTest,
      atest WMShellUnitTests:DesktopTasksControllerTest,
      atest WMShellUnitTests:DesktopModeWindowDecorViewModelTests
Change-Id: I802c0b676271fea9c88ace28619e429a6f9311e0
parent 6649bbc5
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class DesktopModeCompatPolicy(private val context: Context) {
            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
            task.userId)

    fun isTopActivityExemptFromDesktopWindowing(
    private fun isTopActivityExemptFromDesktopWindowing(
        packageName: String?,
        numActivities: Int,
        isTopActivityNoDisplay: Boolean,
@@ -74,6 +74,29 @@ class DesktopModeCompatPolicy(private val context: Context) {
        else -> false
    }

    fun shouldDisableDesktopEntryPoints(task: TaskInfo) = shouldDisableDesktopEntryPoints(
        task.baseActivity?.packageName, task.numActivities, task.isTopActivityNoDisplay,
        task.isActivityStackTransparent)

    fun shouldDisableDesktopEntryPoints(
        packageName: String?,
        numActivities: Int,
        isTopActivityNoDisplay: Boolean,
        isActivityStackTransparent: Boolean,
    ) = when {
        // Activity will not be displayed, no need to show desktop entry point.
        isTopActivityNoDisplay -> true
        // If activity belongs to system ui package, hide desktop entry point.
        isSystemUiTask(packageName) -> true
        // If activity belongs to default home package, safe to force out of desktop.
        isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) -> true
        // If all activities in task stack are transparent AND package has the relevant fullscreen
        // transparent permission, safe to force out of desktop.
        isTransparentTask(isActivityStackTransparent, numActivities) -> true
        else -> false
    }


    /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
    fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean =
        taskInfo.topActivityInfo?.let {
+16 −7
Original line number Diff line number Diff line
@@ -380,18 +380,31 @@ class DesktopTasksController(
        when (allFocusedTasks.size) {
            0 -> return
            // Full screen case
            1 ->
            1 -> {
                if (
                    desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
                        allFocusedTasks.single()
                    )
                ) {
                    return
                }
                moveTaskToDefaultDeskAndActivate(
                    allFocusedTasks.single().taskId,
                    transitionSource = transitionSource,
                )
            }
            // Split-screen case where there are two focused tasks, then we find the child
            // task to move to desktop.
            2 ->
            2 -> {
                val focusedTask = getSplitFocusedTask(allFocusedTasks[0], allFocusedTasks[1])
                if (desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(focusedTask)) {
                    return
                }
                moveTaskToDefaultDeskAndActivate(
                    getSplitFocusedTask(allFocusedTasks[0], allFocusedTasks[1]).taskId,
                    focusedTask.taskId,
                    transitionSource = transitionSource,
                )
            }
            else ->
                logW(
                    "DesktopTasksController: Cannot enter desktop, expected less " +
@@ -634,10 +647,6 @@ class DesktopTasksController(
        remoteTransition: RemoteTransition? = null,
        callback: IMoveToDesktopCallback? = null,
    ): Boolean {
        if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)) {
            logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
            return false
        }
        val displayId = taskRepository.getDisplayForDesk(deskId)
        logV(
            "moveRunningTaskToDesk taskId=%d deskId=%d displayId=%d",
+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ class AppHandleAndHeaderVisibilityHelper (
            return false
        }

        if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
        if (desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(taskInfo)) {
            return false
        }

+46 −161
Original line number Diff line number Diff line
@@ -1749,157 +1749,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop_multiDesksDisabled() {
        val task =
            setUpFullscreenTask().apply {
                isActivityStackTransparent = true
                isTopActivityNoDisplay = true
                numActivities = 1
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
        val wct = getLatestEnterDesktopWct()
        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
        verify(desktopModeEnterExitTransitionListener)
            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop_multiDesksEnabled() {
        val task =
            setUpFullscreenTask().apply {
                isActivityStackTransparent = true
                isTopActivityNoDisplay = true
                numActivities = 1
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
        val wct = getLatestEnterDesktopWct()
        verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task = task)
        verify(desktopModeEnterExitTransitionListener)
            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
    fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
        val task =
            setUpFullscreenTask().apply {
                isActivityStackTransparent = true
                isTopActivityNoDisplay = false
                numActivities = 1
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
        verify(desktopModeEnterExitTransitionListener, times(0))
            .onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
        // Set task as systemUI package
        val systemUIPackageName =
            context.resources.getString(com.android.internal.R.string.config_systemUi)
        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = baseComponent
                isTopActivityNoDisplay = false
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing_multiDesksDisabled() {
        // Set task as systemUI package
        val systemUIPackageName =
            context.resources.getString(com.android.internal.R.string.config_systemUi)
        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = baseComponent
                isTopActivityNoDisplay = true
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)

        val wct = getLatestEnterDesktopWct()
        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
    }

    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveRunningTaskToDesktop_defaultHomePackageWithDisplay_doesNothing() {
        val packageManager: PackageManager = org.mockito.kotlin.mock()
        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = homeActivities
                isTopActivityNoDisplay = false
            }
        mContext.setMockPackageManager(packageManager)
        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveRunningTaskToDesktop_defaultHomePackageWithoutDisplay_doesNothing() {
        val packageManager: PackageManager = org.mockito.kotlin.mock()
        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = homeActivities
                isTopActivityNoDisplay = false
            }
        mContext.setMockPackageManager(packageManager)
        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)

        val wct = getLatestEnterDesktopWct()
        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing_multiDesksEnabled() {
        // Set task as systemUI package
        val systemUIPackageName =
            context.resources.getString(com.android.internal.R.string.config_systemUi)
        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = baseComponent
                isTopActivityNoDisplay = true
            }

        controller.moveTaskToDefaultDeskAndActivate(task.taskId, transitionSource = UNKNOWN)

        val wct = getLatestEnterDesktopWct()
        verify(desksOrganizer).moveTaskToDesk(wct, deskId = 0, task = task)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
    fun moveBackgroundTaskToDesktop_remoteTransition_usesOneShotHandler() {
@@ -4968,15 +4817,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        val freeformTask = setUpFreeformTask()
        markTaskVisible(freeformTask)

        val packageManager: PackageManager = org.mockito.kotlin.mock()
        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = homeActivities
                baseActivity = homeComponentName
                isTopActivityNoDisplay = false
            }
        mContext.setMockPackageManager(packageManager)
        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)

        val result = controller.handleRequest(Binder(), createTransition(task))
        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
@@ -4988,15 +4833,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        val freeformTask = setUpFreeformTask()
        markTaskVisible(freeformTask)

        val packageManager: PackageManager = org.mockito.kotlin.mock()
        val homeActivities = ComponentName("defaultHomePackage", /* class */ "")
        val task =
            setUpFullscreenTask().apply {
                baseActivity = homeActivities
                baseActivity = homeComponentName
                isTopActivityNoDisplay = false
            }
        mContext.setMockPackageManager(packageManager)
        whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities)

        val result = controller.handleRequest(Binder(), createTransition(task))
        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
@@ -5393,6 +5234,50 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        assertNull(result, "Should not handle request")
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveFocusedTaskToDesktop_noDisplayActivity_doesNothing() {
        val task = setUpFullscreenTask().apply { isTopActivityNoDisplay = true }

        controller.moveFocusedTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveFocusedTaskToDesktop_transparentTask_doesNothing() {
        val task =
            setUpFullscreenTask().apply {
                isActivityStackTransparent = true
                numActivities = 1
            }

        controller.moveFocusedTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveFocusedTaskToDesktop_systemUIActivity_doesNothing() {
        // Set task as systemUI package
        val systemUIPackageName =
            context.resources.getString(com.android.internal.R.string.config_systemUi)
        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
        val task = setUpFullscreenTask().apply { baseActivity = baseComponent }

        controller.moveFocusedTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
    fun moveFocusedTaskToDesktop_defaultHomePackage_doesNothing() {
        val task = setUpFullscreenTask().apply { baseActivity = homeComponentName }

        controller.moveFocusedTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
        verifyEnterDesktopWCTNotExecuted()
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop_multiDesksDisabled() {
+52 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.internal.R
import com.android.window.flags.Flags
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.windowdecor.DesktopModeWindowDecorViewModelTestsBase.Companion.HOME_LAUNCHER_PACKAGE_NAME
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges
@@ -248,6 +249,57 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
                }))
    }

    @Test
    fun testShouldDisableDesktopEntryPoints_noDisplayActivity() {
        assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
            createFullscreenTask(/* displayId */ 0)
                .apply {
                    isTopActivityNoDisplay = true
                }))
    }

    @Test
    fun testShouldDisableDesktopEntryPoints_transparentTask() {
        assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
            createFullscreenTask(/* displayId */ 0)
                .apply {
                    isActivityStackTransparent = true
                    numActivities = 1
                }))
    }

    @Test
    fun testShouldDisableDesktopEntryPoints_defaultHomePackage() {
        assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
            createFullscreenTask(/* displayId */ 0)
                .apply {
                    baseActivity = homeActivities
                }))
    }

    @Test
    fun testShouldDisableDesktopEntryPoints_defaultHomePackage_notYetAvailable() {
        val emptyHomeActivities: ComponentName = mock()
        mContext.setMockPackageManager(packageManager)

        whenever(emptyHomeActivities.packageName).thenReturn(null)
        whenever(packageManager.getHomeActivities(any())).thenReturn(emptyHomeActivities)

        assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
            createFullscreenTask(/* displayId */ 0)))
    }

    @Test
    fun testShouldDisableDesktopEntryPoints_systemUiTask() {
        val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
        assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
            createFreeformTask(/* displayId */ 0)
                .apply {
                    baseActivity = baseComponent
                }))
    }

    @Test
    @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
    @DisableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
Loading