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