• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.shared.desktopmode
18 
19 import android.Manifest.permission.SYSTEM_ALERT_WINDOW
20 import android.app.TaskInfo
21 import android.content.Context
22 import android.content.pm.PackageManager
23 import android.window.DesktopModeFlags
24 import com.android.internal.R
25 import com.android.internal.policy.DesktopModeCompatUtils
26 import java.util.function.Supplier
27 
28 /**
29  * Class to decide whether to apply app compat policies in desktop mode.
30  */
31 // TODO(b/347289970): Consider replacing with API
32 class DesktopModeCompatPolicy(private val context: Context) {
33 
34     private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
35     private val pkgManager: PackageManager
36         get() = context.getPackageManager()
37     private val defaultHomePackage: String?
38         get() = defaultHomePackageSupplier?.get()
39             ?: pkgManager.getHomeActivities(ArrayList())?.packageName
40     private val packageInfoCache = mutableMapOf<String, Boolean>()
41 
42     var defaultHomePackageSupplier: Supplier<String?>? = null
43 
44     /**
45      * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
46      * Currently includes all system ui, default home and transparent stack activities. However if
47      * the top activity is not being displayed, regardless of its configuration, we will not exempt
48      * it as to remain in the desktop windowing environment.
49      */
isTopActivityExemptFromDesktopWindowingnull50     fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
51         isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
52             task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
53             task.userId)
54 
55     fun isTopActivityExemptFromDesktopWindowing(
56         packageName: String?,
57         numActivities: Int,
58         isTopActivityNoDisplay: Boolean,
59         isActivityStackTransparent: Boolean,
60         userId: Int
61     ) =
62         DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
63                 ((isSystemUiTask(packageName) ||
64                         isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
65                         (isTransparentTask(isActivityStackTransparent, numActivities) &&
66                                 hasFullscreenTransparentPermission(packageName, userId))) &&
67                         !isTopActivityNoDisplay)
68 
69     /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
70     fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean =
71         taskInfo.topActivityInfo?.let {
72             DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds(it, taskInfo.isResizeable,
73                 taskInfo.appCompatTaskInfo.hasOptOutEdgeToEdge())
74         } ?: false
75 
76     /**
77      * Returns true if all activities in a tasks stack are transparent. If there are no activities
78      * will return false.
79      */
isTransparentTasknull80     fun isTransparentTask(task: TaskInfo): Boolean =
81         isTransparentTask(task.isActivityStackTransparent, task.numActivities)
82 
83     private fun isTransparentTask(isActivityStackTransparent: Boolean, numActivities: Int) =
84         isActivityStackTransparent && numActivities > 0
85 
86     private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
87 
88     // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
89     private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
90         if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
91             if (packageName == null) {
92                 return false
93             }
94             return packageInfoCache.getOrPut("$userId@$packageName") {
95                 try {
96                     val packageInfo = pkgManager.getPackageInfoAsUser(
97                         packageName,
98                         PackageManager.GET_PERMISSIONS,
99                         userId
100                     )
101                     packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
102                 } catch (e: PackageManager.NameNotFoundException) {
103                     false // Package not found
104                 }
105             }
106         }
107         // If the flag is disabled we make this condition neutral.
108         return true
109     }
110 
111     /**
112      * Returns true if the tasks base activity is part of the default home package, or there is
113      * currently no default home package available.
114      */
isPartOfDefaultHomePackageOrNoHomeAvailablenull115     private fun isPartOfDefaultHomePackageOrNoHomeAvailable(packageName: String?) =
116         defaultHomePackage == null || (packageName != null && packageName == defaultHomePackage)
117 }
118