• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 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.permissioncontroller.permission.data
18 
19 import android.Manifest
20 import android.app.Application
21 import android.content.pm.PackageManager
22 import android.content.pm.PermissionInfo
23 import android.os.Build
24 import android.os.UserHandle
25 import com.android.permissioncontroller.PermissionControllerApplication
26 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
27 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
28 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
29 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo
30 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo
31 import com.android.permissioncontroller.permission.model.livedatatypes.PermState
32 import com.android.permissioncontroller.permission.utils.LocationUtils
33 import com.android.permissioncontroller.permission.utils.Utils
34 import com.android.permissioncontroller.permission.utils.Utils.isModernPermissionGroup
35 
36 /**
37  * A LiveData representing UI properties of an App Permission Group:
38  * <ul>
39  *     <li>shouldShow</li>
40  *     <li>isSystem</li>
41  *     <li>isGranted</li>
42  * </ul>
43  *
44  * @param app The current application
45  * @param packageName The name of the package
46  * @param permGroupName The name of the permission group whose permissions are observed
47  * @param user The user of the package
48  */
49 class AppPermGroupUiInfoLiveData private constructor(
50     private val app: Application,
51     private val packageName: String,
52     private val permGroupName: String,
53     private val user: UserHandle
54 ) : SmartUpdateMediatorLiveData<AppPermGroupUiInfo>(), LocationUtils.LocationListener {
55 
56     private var isSpecialLocation = false
57     private val isMicrophone = permGroupName == Manifest.permission_group.MICROPHONE
58     private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
59     private val permGroupLiveData = PermGroupLiveData[permGroupName]
60     private val permissionStateLiveData = PermStateLiveData[packageName, permGroupName, user]
61 
62     init {
63         isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app,
64             permGroupName, packageName) ||
65             LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
66 
67         addSource(packageInfoLiveData) {
68             updateIfActive()
69         }
70 
71         addSource(permGroupLiveData) {
72             updateIfActive()
73         }
74 
75         addSource(permissionStateLiveData) {
76             updateIfActive()
77         }
78     }
79 
80     override fun onUpdate() {
81         val packageInfo = packageInfoLiveData.value
82         val permissionGroup = permGroupLiveData.value
83         val permissionState = permissionStateLiveData.value
84 
85         if (packageInfo == null || permissionGroup == null || permissionState == null) {
86             if (packageInfoLiveData.isInitialized && permGroupLiveData.isInitialized &&
87                 permissionStateLiveData.isInitialized) {
88                 invalidateSingle(Triple(packageName, permGroupName, user))
89                 value = null
90             }
91             return
92         }
93 
94         value = getAppPermGroupUiInfo(packageInfo, permissionGroup.groupInfo,
95             permissionGroup.permissionInfos, permissionState)
96     }
97 
98     /**
99      * Determines if the UI should show a given package, if that package is a system app, and
100      * if it has granted permissions in this LiveData's permission group.
101      *
102      * @param packageInfo The PackageInfo of the package we wish to examine
103      * @param groupInfo The groupInfo of the permission group we wish to examine
104      * @param allPermInfos All of the PermissionInfos in the permission group
105      * @param permissionState The flags and grant state for all permissions in the permission
106      * group that this package requests
107      */
108     private fun getAppPermGroupUiInfo(
109         packageInfo: LightPackageInfo,
110         groupInfo: LightPermGroupInfo,
111         allPermInfos: Map<String, LightPermInfo>,
112         permissionState: Map<String, PermState>
113     ): AppPermGroupUiInfo {
114         /*
115          * Filter out any permission infos in the permission group that this package
116          * does not request.
117          */
118         val requestedPermissionInfos =
119             allPermInfos.filter { permissionState.containsKey(it.key) }.values
120 
121         val shouldShow = packageInfo.enabled && isGrantableAndNotLegacyPlatform(packageInfo,
122             groupInfo, requestedPermissionInfos)
123 
124         val isSystemApp = !isUserSensitive(permissionState)
125 
126         val isGranted = getGrantedIncludingBackground(permissionState, allPermInfos)
127 
128         return AppPermGroupUiInfo(shouldShow, isGranted, isSystemApp)
129     }
130 
131     /**
132      * Determines if a package permission group is able to be granted, and whether or not it is a
133      * legacy system permission group.
134      *
135      * @param packageInfo The PackageInfo of the package we are examining
136      * @param groupInfo The Permission Group Info of the permission group we are examining
137      * @param permissionInfos The LightPermInfos corresponding to the permissions in the
138      * permission group that this package requests
139      *
140      * @return True if the app permission group is grantable, and is not a legacy system permission,
141      * false otherwise.
142      */
143     private fun isGrantableAndNotLegacyPlatform(
144         packageInfo: LightPackageInfo,
145         groupInfo: LightPermGroupInfo,
146         permissionInfos: Collection<LightPermInfo>
147     ): Boolean {
148         var hasPreRuntime = false
149 
150         for (permissionInfo in permissionInfos) {
151             if (permissionInfo.protectionFlags and
152                 PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) {
153                 hasPreRuntime = true
154                 break
155             }
156         }
157 
158         val isGrantingAllowed = !packageInfo.isInstantApp &&
159             (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime)
160         if (!isGrantingAllowed) {
161             return false
162         }
163 
164         if (groupInfo.packageName == Utils.OS_PKG &&
165             !isModernPermissionGroup(groupInfo.name)) {
166             return false
167         }
168         return true
169     }
170 
171     /**
172      * Determines if an app's permission group is user-sensitive. If an app is not user sensitive,
173      * then it is considered a system app, and hidden in the UI by default.
174      *
175      * @param permissionState The permission flags and grant state corresponding to the permissions
176      * in this group requested by a given app
177      *
178      * @return Whether or not this package requests a user sensitive permission in the given
179      * permission group
180      */
181     private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean {
182         if (!isModernPermissionGroup(permGroupName)) {
183             return true
184         }
185 
186         for (permissionName in permissionState.keys) {
187             val flags = permissionState[permissionName]?.permFlags ?: return true
188             val granted = permissionState[permissionName]?.granted ?: return true
189             if ((granted &&
190                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) ||
191                 (!granted &&
192                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) {
193                 return true
194             }
195         }
196         return false
197     }
198 
199     /**
200      * Determines if this app permission group is granted, granted in foreground only, or denied.
201      * It is granted if it either requests no background permissions, and has at least one requested
202      * permission that is granted, or has granted at least one requested background permission.
203      * It is granted in foreground only if it has at least one non-background permission granted,
204      * and has denied all requested background permissions. It is denied if all requested
205      * permissions are denied.
206      *
207      * @param permissionState The permission flags and grant state corresponding to the permissions
208      * in this group requested by a given app
209      * @param allPermInfos All of the permissionInfos in the permission group of this app
210      * permission group
211      *
212      * @return The int code corresponding to the app permission group state, either allowed, allowed
213      * in foreground only, or denied.
214      */
215     private fun getGrantedIncludingBackground(
216         permissionState: Map<String, PermState>,
217         allPermInfos: Map<String, LightPermInfo>
218     ): PermGrantState {
219         val specialLocationState = getIsSpecialLocationState()
220 
221         var hasPermWithBackground = false
222         var isUserFixed = false
223         var isOneTime = false
224         for ((permName, permState) in permissionState) {
225             val permInfo = allPermInfos[permName] ?: continue
226             permInfo.backgroundPermission?.let { backgroundPerm ->
227                 hasPermWithBackground = true
228                 if (permissionState[backgroundPerm]?.granted == true &&
229                     specialLocationState != false) {
230                     return PermGrantState.PERMS_ALLOWED_ALWAYS
231                 }
232             }
233             isUserFixed = isUserFixed ||
234                     permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0
235             isOneTime = isOneTime ||
236                     permState.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0
237         }
238 
239         val anyAllowed = specialLocationState ?: permissionState.any { it.value.granted }
240         if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) {
241             if (isOneTime) {
242                 return PermGrantState.PERMS_ASK
243             } else {
244                 if (Utils.couldHaveForegroundCapabilities(
245                                 Utils.getUserContext(app, user), packageName) ||
246                         Utils.isEmergencyApp(Utils.getUserContext(app, user), packageName)) {
247                     return PermGrantState.PERMS_ALLOWED_ALWAYS
248                 } else {
249                     return PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
250                 }
251             }
252         } else if (anyAllowed) {
253             if (isOneTime) {
254                 return PermGrantState.PERMS_ASK
255             } else {
256                 return PermGrantState.PERMS_ALLOWED
257             }
258         }
259         if (isUserFixed) {
260             return PermGrantState.PERMS_DENIED
261         }
262         if (isOneTime) {
263             return PermGrantState.PERMS_ASK
264         }
265         return PermGrantState.PERMS_DENIED
266     }
267 
268     private fun getIsSpecialLocationState(): Boolean? {
269         if (!isSpecialLocation) {
270             return null
271         }
272 
273         val userContext = Utils.getUserContext(app, user)
274         if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
275             return LocationUtils.isLocationEnabled(userContext)
276         }
277         // The permission of the extra location controller package is determined by the
278         // status of the controller package itself.
279         if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext,
280                 permGroupName, packageName)) {
281             return LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
282         }
283         return null
284     }
285 
286     // TODO moltmann-team: Actually change mic/camera to be a foreground only permission
287     private fun shouldShowAsForegroundGroup(): Boolean {
288         return permGroupName.equals(Manifest.permission_group.CAMERA) ||
289                 permGroupName.equals(Manifest.permission_group.MICROPHONE)
290     }
291 
292     override fun onLocationStateChange(enabled: Boolean) {
293         updateIfActive()
294     }
295 
296     override fun onActive() {
297         super.onActive()
298         if (isSpecialLocation) {
299             LocationUtils.addLocationListener(this)
300             updateIfActive()
301         }
302     }
303 
304     override fun onInactive() {
305         super.onInactive()
306 
307         if (isSpecialLocation) {
308             LocationUtils.removeLocationListener(this)
309         }
310     }
311 
312     /**
313      * Repository for AppPermGroupUiInfoLiveDatas.
314      * <p> Key value is a triple of string package name, string permission group name, and UserHandle,
315      * value is its corresponding LiveData.
316      */
317     companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
318             AppPermGroupUiInfoLiveData>() {
319         override fun newValue(key: Triple<String, String, UserHandle>):
320                 AppPermGroupUiInfoLiveData {
321             return AppPermGroupUiInfoLiveData(PermissionControllerApplication.get(),
322                     key.first, key.second, key.third)
323         }
324     }
325 }