• 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 && isGrantableAndNotLegacyPlatform(packageInfo,
128             groupInfo, requestedPermissionInfos)
129 
130         val isSystemApp = !isUserSensitive(permissionState)
131 
132         val isUserSet = isUserSet(permissionState)
133 
134         val isGranted = getGrantedIncludingBackground(permissionState, allPermInfos, packageInfo)
135 
136         return AppPermGroupUiInfo(shouldShow, isGranted, isSystemApp, isUserSet)
137     }
138 
139     /**
140      * Determines if a package permission group is able to be granted, and whether or not it is a
141      * legacy system permission group.
142      *
143      * @param packageInfo The PackageInfo of the package we are examining
144      * @param groupInfo The Permission Group Info of the permission group we are examining
145      * @param permissionInfos The LightPermInfos corresponding to the permissions in the
146      * permission group that this package requests
147      *
148      * @return True if the app permission group is grantable, and is not a legacy system permission,
149      * false otherwise.
150      */
151     private fun isGrantableAndNotLegacyPlatform(
152         packageInfo: LightPackageInfo,
153         groupInfo: LightPermGroupInfo,
154         permissionInfos: Collection<LightPermInfo>
155     ): Boolean {
156         if (groupInfo.packageName == Utils.OS_PKG &&
157             !isModernPermissionGroup(groupInfo.name)) {
158             return false
159         }
160 
161         var hasInstantPerm = false
162         var hasPreRuntime = false
163 
164         for (permissionInfo in permissionInfos) {
165             if (permissionInfo.protectionFlags and
166                 PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) {
167                 hasPreRuntime = true
168             }
169 
170             if (permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT != 0) {
171                 hasInstantPerm = true
172             }
173         }
174 
175         val isGrantingAllowed = (!packageInfo.isInstantApp || hasInstantPerm) &&
176             (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime)
177         if (!isGrantingAllowed) {
178             return false
179         }
180 
181         return true
182     }
183 
184     /**
185      * Determines if an app's permission group is user-sensitive. If an app is not user sensitive,
186      * then it is considered a system app, and hidden in the UI by default.
187      *
188      * @param permissionState The permission flags and grant state corresponding to the permissions
189      * in this group requested by a given app
190      *
191      * @return Whether or not this package requests a user sensitive permission in the given
192      * permission group
193      */
194     private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean {
195         if (!isModernPermissionGroup(permGroupName)) {
196             return true
197         }
198 
199         for (permissionName in permissionState.keys) {
200             val flags = permissionState[permissionName]?.permFlags ?: return true
201             val granted = permissionState[permissionName]?.granted ?: return true
202             if ((granted &&
203                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) ||
204                 (!granted &&
205                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) {
206                 return true
207             }
208         }
209         return false
210     }
211 
212     /**
213      * Determines if the app permission group is user set
214      *
215      * @param permissionState The permission flags and grant state corresponding to the permissions
216      * in this group requested by a given app
217      *
218      * @return Whether or not any of the permissions in this group have been set or fixed by the
219      * user
220      */
221     private fun isUserSet(permissionState: Map<String, PermState>): Boolean {
222         val flagMask = PackageManager.FLAG_PERMISSION_USER_SET or
223                 PackageManager.FLAG_PERMISSION_USER_FIXED
224         return permissionState.any { (it.value.permFlags and flagMask) != 0 }
225     }
226 
227     /**
228      * Determines if this app permission group is granted, granted in foreground only, or denied.
229      * It is granted if it either requests no background permissions, and has at least one requested
230      * permission that is granted, or has granted at least one requested background permission.
231      * It is granted in foreground only if it has at least one non-background permission granted,
232      * and has denied all requested background permissions. It is denied if all requested
233      * permissions are denied.
234      *
235      * @param permissionState The permission flags and grant state corresponding to the permissions
236      * in this group requested by a given app
237      * @param allPermInfos All of the permissionInfos in the permission group of this app
238      * permission group
239      *
240      * @return The int code corresponding to the app permission group state, either allowed, allowed
241      * in foreground only, or denied.
242      */
243     private fun getGrantedIncludingBackground(
244         permissionState: Map<String, PermState>,
245         allPermInfos: Map<String, LightPermInfo>,
246         pkg: LightPackageInfo
247     ): PermGrantState {
248         val specialLocationState = getIsSpecialLocationState()
249         if (isStorage && isFullFilesAccessGranted(pkg)) {
250             return PermGrantState.PERMS_ALLOWED
251         }
252 
253         var hasPermWithBackground = false
254         var isUserFixed = false
255         for ((permName, permState) in permissionState) {
256             val permInfo = allPermInfos[permName] ?: continue
257             permInfo.backgroundPermission?.let { backgroundPerm ->
258                 hasPermWithBackground = true
259                 if (permissionState[backgroundPerm]?.granted == true &&
260                     specialLocationState != false) {
261                     return PermGrantState.PERMS_ALLOWED_ALWAYS
262                 }
263             }
264             isUserFixed = isUserFixed ||
265                     permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0
266         }
267         // isOneTime indicates whether permission states contain any one-time permission and
268         // none of the permissions are granted (not one-time)
269         val isOneTime = permissionState.any {
270             it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 } &&
271                 !permissionState.any {
272                     it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME == 0 &&
273                             it.value.granted }
274 
275         val anyAllowed = specialLocationState ?: permissionState.any { it.value.granted }
276         if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) {
277             return if (isOneTime) {
278                 PermGrantState.PERMS_ASK
279             } else {
280                 PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
281             }
282         } else if (anyAllowed) {
283             return if (isOneTime) {
284                 PermGrantState.PERMS_ASK
285             } else {
286                 PermGrantState.PERMS_ALLOWED
287             }
288         }
289         if (isUserFixed) {
290             return PermGrantState.PERMS_DENIED
291         }
292         if (isOneTime) {
293             return PermGrantState.PERMS_ASK
294         }
295         return PermGrantState.PERMS_DENIED
296     }
297 
298     private fun getIsSpecialLocationState(): Boolean? {
299         if (!isSpecialLocation) {
300             return null
301         }
302 
303         val userContext = Utils.getUserContext(app, user)
304         if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
305             return LocationUtils.isLocationEnabled(userContext)
306         }
307         // The permission of the extra location controller package is determined by the
308         // status of the controller package itself.
309         if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext,
310                 permGroupName, packageName)) {
311             return LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
312         }
313         return null
314     }
315 
316     private fun isFullFilesAccessGranted(pkg: LightPackageInfo): Boolean {
317         val packageState = if (!FullStoragePermissionAppsLiveData.isStale) {
318             val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false
319             fullStoragePackages.find {
320                 it.packageName == packageName && it.user == user
321             } ?: return false
322         } else {
323             val appOpsManager = Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid))
324                 .getSystemService(AppOpsManager::class.java)!!
325             FullStoragePermissionAppsLiveData.getFullStorageStateForPackage(
326                 appOpsManager, pkg) ?: return false
327         }
328         return !packageState.isLegacy && packageState.isGranted
329     }
330 
331     // TODO moltmann-team: Actually change mic/camera to be a foreground only permission
332     private fun shouldShowAsForegroundGroup(): Boolean {
333         return permGroupName.equals(Manifest.permission_group.CAMERA) ||
334                 permGroupName.equals(Manifest.permission_group.MICROPHONE)
335     }
336 
337     override fun onLocationStateChange(enabled: Boolean) {
338         update()
339     }
340 
341     override fun onActive() {
342         super.onActive()
343         if (isSpecialLocation) {
344             LocationUtils.addLocationListener(this)
345             update()
346         }
347     }
348 
349     override fun onInactive() {
350         super.onInactive()
351 
352         if (isSpecialLocation) {
353             LocationUtils.removeLocationListener(this)
354         }
355     }
356 
357     /**
358      * Repository for AppPermGroupUiInfoLiveDatas.
359      * <p> Key value is a triple of string package name, string permission group name, and UserHandle,
360      * value is its corresponding LiveData.
361      */
362     companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>,
363             AppPermGroupUiInfoLiveData>() {
364         override fun newValue(key: Triple<String, String, UserHandle>):
365                 AppPermGroupUiInfoLiveData {
366             return AppPermGroupUiInfoLiveData(PermissionControllerApplication.get(),
367                     key.first, key.second, key.third)
368         }
369     }
370 }
371