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

Commit 9399d145 authored by Merissa Mitchell's avatar Merissa Mitchell
Browse files

[PiP on Desktop] Check App Ops before minimizing to PiP.

Recall: http://recall/clips/b3121879-70fc-4d51-a326-57a5480298d9

When swiping up to enter PiP, AbsSwipeUpHandler on the Launcher side
checks AppOps to see if the app is allowed to enter PiP. This CL adds
this check for Desktop PiP as well (when minimizing to PiP) so it
respects what is set in Settings.

Bug: 370061716
Test: atest DesktopTasksControllerTest
Flag: com.android.window.flags.enable_desktop_windowing_pip
Change-Id: I1451ac8d9723839283b221113a83720212fa15f9
parent 9b19b8f9
Loading
Loading
Loading
Loading
+35 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.ActivityManager
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.TaskInfo
@@ -33,6 +34,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -1015,10 +1017,11 @@ class DesktopTasksController(
            } else {
                taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
            }

        val isMinimizingToPip =
            DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
                (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)

                (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false) &&
                isPipAllowedInAppOps(taskInfo)
        // If task is going to PiP, start a PiP transition instead of a minimize transition
        if (isMinimizingToPip) {
            val requestInfo =
@@ -1091,6 +1094,36 @@ class DesktopTasksController(
        )
    }

    /** Checks whether the given [taskInfo] is allowed to enter PiP in AppOps. */
    private fun isPipAllowedInAppOps(taskInfo: RunningTaskInfo): Boolean {
        val packageName =
            taskInfo.baseActivity?.packageName
                ?: taskInfo.topActivity?.packageName
                ?: taskInfo.origActivity?.packageName
                ?: taskInfo.realActivity?.packageName
                ?: return false

        val appOpsManager =
            checkNotNull(context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager)
        try {
            val appInfo =
                context.packageManager.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId)
            return appOpsManager.checkOpNoThrow(
                AppOpsManager.OP_PICTURE_IN_PICTURE,
                appInfo.uid,
                packageName,
            ) == AppOpsManager.MODE_ALLOWED
        } catch (_: PackageManager.NameNotFoundException) {
            logW(
                "isPipAllowedInAppOps: Failed to find applicationInfo for packageName=%s " +
                    "and userId=%d",
                packageName,
                userId,
            )
        }
        return false
    }

    /** Move or launch a task with given [taskId] to fullscreen */
    @JvmOverloads
    fun moveToFullscreen(
+55 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.PictureInPictureParams
@@ -271,6 +272,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    @Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
    @Mock
    private lateinit var moveToDisplayTransitionHandler: DesktopModeMoveToDisplayTransitionHandler
    @Mock private lateinit var mockAppOpsManager: AppOpsManager

    private lateinit var controller: DesktopTasksController
    private lateinit var shellInit: ShellInit
@@ -3633,10 +3635,23 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
            .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
    }

    private fun minimizePipTask(task: RunningTaskInfo) {
    private fun minimizePipTask(task: RunningTaskInfo, appOpsAllowed: Boolean = true) {
        val handler = mock(TransitionHandler::class.java)
        whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
            .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
        mContext.addMockSystemService(Context.APP_OPS_SERVICE, mockAppOpsManager)
        mContext.setMockPackageManager(packageManager)

        whenever(
                mockAppOpsManager.checkOpNoThrow(
                    eq(AppOpsManager.OP_PICTURE_IN_PICTURE),
                    any(),
                    any(),
                )
            )
            .thenReturn(
                if (appOpsAllowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_IGNORED
            )

        controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
    }
@@ -3673,6 +3688,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
    fun onPipTaskMinimize_pipNotAllowedInAppOps_startMinimizeTransition() {
        val task = setUpPipTask(autoEnterEnabled = true)
        whenever(
                freeformTaskTransitionStarter.startMinimizedModeTransition(
                    any(),
                    anyInt(),
                    anyBoolean(),
                )
            )
            .thenReturn(Binder())
        whenever(
                mockAppOpsManager.checkOpNoThrow(
                    eq(AppOpsManager.OP_PICTURE_IN_PICTURE),
                    any(),
                    any(),
                )
            )
            .thenReturn(AppOpsManager.MODE_IGNORED)

        minimizePipTask(task, appOpsAllowed = false)

        verify(freeformTaskTransitionStarter)
            .startMinimizedModeTransition(any(), eq(task.taskId), anyBoolean())
        verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
    fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
@@ -8434,10 +8477,16 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        autoEnterEnabled: Boolean,
        displayId: Int = DEFAULT_DISPLAY,
        deskId: Int = DEFAULT_DISPLAY,
    ): RunningTaskInfo =
    ): RunningTaskInfo {
        val task =
            setUpFreeformTask(displayId = displayId, deskId = deskId).apply {
                pictureInPictureParams =
                    PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
                baseActivity = ComponentName("com.test.dummypackage", "TestClass")
            }
        whenever(packageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt()))
            .thenReturn(task.topActivityInfo?.applicationInfo)
        return task
    }

    private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {