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

Commit a598b204 authored by Massimo Carli's avatar Massimo Carli
Browse files

[2/n] Handle permission for transparent activities

Integrates logic in DesktopModeCompatPolicy to allow only apps
with SYSTEM_ALERT_WINDOW permission to reach fully
transparent fullscreen.

Flag: com.android.window.flags.enable_modals_fullscreen_with_permission
Bug: 394714626
Test: atest WMShellUnitTests:DesktopModeCompatPolicyTest

Change-Id: Ie47bb6b412e94dd842914517c37f1c63d8ee69c3
parent 82468707
Loading
Loading
Loading
Loading
+31 −6
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.wm.shell.shared.desktopmode

import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED
import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION
import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
import android.content.pm.PackageManager
import android.window.DesktopModeFlags
import com.android.internal.R

@@ -32,8 +34,10 @@ import com.android.internal.R
class DesktopModeCompatPolicy(private val context: Context) {

    private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
    private val pkgManager: PackageManager
        get() = context.getPackageManager()
    private val defaultHomePackage: String?
        get() = context.getPackageManager().getHomeActivities(ArrayList())?.packageName
        get() = pkgManager.getHomeActivities(ArrayList())?.packageName

    /**
     * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -47,11 +51,12 @@ class DesktopModeCompatPolicy(private val context: Context) {

    fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
        numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
        DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
                && ((isSystemUiTask(packageName)
                || isPartOfDefaultHomePackageOrNoHomeAvailable(packageName)
                || isTransparentTask(isActivityStackTransparent, numActivities))
                && !isTopActivityNoDisplay)
        DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
                ((isSystemUiTask(packageName) ||
                        isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
                        (isTransparentTask(isActivityStackTransparent, numActivities) &&
                                hasFullscreenTransparentPermission(packageName))) &&
                        !isTopActivityNoDisplay)

    /**
     * Whether the caption insets should be excluded from configuration for system to handle.
@@ -83,6 +88,26 @@ class DesktopModeCompatPolicy(private val context: Context) {

    private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage

    // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
    private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
        if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
            if (packageName == null) {
                return false
            }
            return try {
                val packageInfo = pkgManager.getPackageInfo(
                    packageName,
                    PackageManager.GET_PERMISSIONS
                )
                packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
            } catch (e: PackageManager.NameNotFoundException) {
                false // Package not found
            }
        }
        // If the flag is disabled we make this condition neutral.
        return true
    }

    /**
     * Returns true if the tasks base activity is part of the default home package, or there is
     * currently no default home package available.
+47 −0
Original line number Diff line number Diff line
@@ -16,13 +16,16 @@

package com.android.wm.shell.shared.desktopmode

import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.compat.testing.PlatformCompatChangeRule
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Process
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -39,7 +42,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@@ -55,6 +60,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
    private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
    private val packageManager: PackageManager = mock()
    private val homeActivities = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "")
    private val baseActivityTest = ComponentName("com.test.dummypackage", "TestClass")

    @Before
    fun setUp() {
@@ -64,6 +70,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
    fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
        assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
            createFreeformTask(/* displayId */ 0)
@@ -71,6 +78,35 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
                        isActivityStackTransparent = true
                        isTopActivityNoDisplay = false
                        numActivities = 1
                        baseActivity = baseActivityTest
                    }))
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
    fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
        allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
        assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
            createFreeformTask(/* displayId */ 0)
                .apply {
                    isActivityStackTransparent = true
                    isTopActivityNoDisplay = false
                    numActivities = 1
                    baseActivity = baseActivityTest
                }))
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
    fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
        allowOverlayPermission(arrayOf())
        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
            createFreeformTask(/* displayId */ 0)
                .apply {
                    isActivityStackTransparent = true
                    isTopActivityNoDisplay = false
                    numActivities = 1
                    baseActivity = baseActivityTest
                }))
    }

@@ -219,4 +255,15 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
                }
            }
        }

    fun allowOverlayPermission(permissions: Array<String>) {
        val packageInfo = mock<PackageInfo>()
        packageInfo.requestedPermissions = permissions
        whenever(
            packageManager.getPackageInfo(
                anyString(),
                eq(PackageManager.GET_PERMISSIONS)
            )
        ).thenReturn(packageInfo)
    }
}