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 packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
58     private val permGroupLiveData = PermGroupLiveData[permGroupName]
59     private val permissionStateLiveData = PermStateLiveData[packageName, permGroupName, user]
60 
61     init {
62         isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app,
63             permGroupName, packageName) ||
64             LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
65 
66         addSource(packageInfoLiveData) {
67             updateIfActive()
68         }
69 
70         addSource(permGroupLiveData) {
71             updateIfActive()
72         }
73 
74         addSource(permissionStateLiveData) {
75             updateIfActive()
76         }
77     }
78 
79     override fun onUpdate() {
80         val packageInfo = packageInfoLiveData.value
81         val permissionGroup = permGroupLiveData.value
82         val permissionState = permissionStateLiveData.value
83 
84         if (packageInfo == null || permissionGroup == null || permissionState == null) {
85             if (packageInfoLiveData.isInitialized && permGroupLiveData.isInitialized &&
86                 permissionStateLiveData.isInitialized) {
87                 invalidateSingle(Triple(packageName, permGroupName, user))
88                 value = null
89             }
90             return
91         }
92 
93         value = getAppPermGroupUiInfo(packageInfo, permissionGroup.groupInfo,
94             permissionGroup.permissionInfos, permissionState)
95     }
96 
97     /**
98      * Determines if the UI should show a given package, if that package is a system app, and
99      * if it has granted permissions in this LiveData's permission group.
100      *
101      * @param packageInfo The PackageInfo of the package we wish to examine
102      * @param groupInfo The groupInfo of the permission group we wish to examine
103      * @param allPermInfos All of the PermissionInfos in the permission group
104      * @param permissionState The flags and grant state for all permissions in the permission
105      * group that this package requests
106      */
107     private fun getAppPermGroupUiInfo(
108         packageInfo: LightPackageInfo,
109         groupInfo: LightPermGroupInfo,
110         allPermInfos: Map<String, LightPermInfo>,
111         permissionState: Map<String, PermState>
112     ): AppPermGroupUiInfo {
113         /*
114          * Filter out any permission infos in the permission group that this package
115          * does not request.
116          */
117         val requestedPermissionInfos =
118             allPermInfos.filter { permissionState.containsKey(it.key) }.values
119 
120         val shouldShow = packageInfo.enabled && isGrantableAndNotLegacyPlatform(packageInfo,
121             groupInfo, requestedPermissionInfos)
122 
123         val isSystemApp = !isUserSensitive(permissionState)
124 
125         val isGranted = getGrantedIncludingBackground(permissionState, allPermInfos)
126 
127         return AppPermGroupUiInfo(shouldShow, isGranted, isSystemApp)
128     }
129 
130     /**
131      * Determines if a package permission group is able to be granted, and whether or not it is a
132      * legacy system permission group.
133      *
134      * @param packageInfo The PackageInfo of the package we are examining
135      * @param groupInfo The Permission Group Info of the permission group we are examining
136      * @param permissionInfos The LightPermInfos corresponding to the permissions in the
137      * permission group that this package requests
138      *
139      * @return True if the app permission group is grantable, and is not a legacy system permission,
140      * false otherwise.
141      */
142     private fun isGrantableAndNotLegacyPlatform(
143         packageInfo: LightPackageInfo,
144         groupInfo: LightPermGroupInfo,
145         permissionInfos: Collection<LightPermInfo>
146     ): Boolean {
147         var hasPreRuntime = false
148 
149         for (permissionInfo in permissionInfos) {
150             if (permissionInfo.protectionFlags and
151                 PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) {
152                 hasPreRuntime = true
153                 break
154             }
155         }
156 
157         val isGrantingAllowed = !packageInfo.isInstantApp &&
158             (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime)
159         if (!isGrantingAllowed) {
160             return false
161         }
162 
163         if (groupInfo.packageName == Utils.OS_PKG &&
164             !isModernPermissionGroup(groupInfo.name)) {
165             return false
166         }
167         return true
168     }
169 
170     /**
171      * Determines if an app's permission group is user-sensitive. If an app is not user sensitive,
172      * then it is considered a system app, and hidden in the UI by default.
173      *
174      * @param permissionState The permission flags and grant state corresponding to the permissions
175      * in this group requested by a given app
176      *
177      * @return Whether or not this package requests a user sensitive permission in the given
178      * permission group
179      */
180     private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean {
181         if (!isModernPermissionGroup(permGroupName)) {
182             return true
183         }
184 
185         for (permissionName in permissionState.keys) {
186             val flags = permissionState[permissionName]?.permFlags ?: return true
187             val granted = permissionState[permissionName]?.granted ?: return true
188             if ((granted &&
189                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) ||
190                 (!granted &&
191                     flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) {
192                 return true
193             }
194         }
195         return false
196     }
197 
198     /**
199      * Determines if this app permission group is granted, granted in foreground only, or denied.
200      * It is granted if it either requests no background permissions, and has at least one requested
201      * permission that is granted, or has granted at least one requested background permission.
202      * It is granted in foreground only if it has at least one non-background permission granted,
203      * and has denied all requested background permissions. It is denied if all requested
204      * permissions are denied.
205      *
206      * @param permissionState The permission flags and grant state corresponding to the permissions
207      * in this group requested by a given app
208      * @param allPermInfos All of the permissionInfos in the permission group of this app
209      * permission group
210      *
211      * @return The int code corresponding to the app permission group state, either allowed, allowed
212      * in foreground only, or denied.
213      */
214     private fun getGrantedIncludingBackground(
215         permissionState: Map<String, PermState>,
216         allPermInfos: Map<String, LightPermInfo>
217     ): PermGrantState {
218         val specialLocationState = getIsSpecialLocationState()
219 
220         var hasPermWithBackground = false
221         var isUserFixed = false
222         var isOneTime = false
223         for ((permName, permState) in permissionState) {
224             val permInfo = allPermInfos[permName] ?: continue
225             permInfo.backgroundPermission?.let { backgroundPerm ->
226                 hasPermWithBackground = true
227                 if (permissionState[backgroundPerm]?.granted == true &&
228                     specialLocationState != false) {
229                     return PermGrantState.PERMS_ALLOWED_ALWAYS
230                 }
231             }
232             isUserFixed = isUserFixed ||
233                     permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0
234             isOneTime = isOneTime ||
235                     permState.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0
236         }
237 
238         val anyAllowed = specialLocationState ?: permissionState.any { it.value.granted }
239         if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) {
240             if (isOneTime) {
241                 return PermGrantState.PERMS_ASK
242             } else {
243                 if (Utils.couldHaveForegroundCapabilities(
244                                 Utils.getUserContext(app, user), packageName) ||
245                         Utils.isEmergencyApp(Utils.getUserContext(app, user), packageName)) {
246                     return PermGrantState.PERMS_ALLOWED_ALWAYS
247                 } else {
248                     return PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
249                 }
250             }
251         } else if (anyAllowed) {
252             if (isOneTime) {
253                 return PermGrantState.PERMS_ASK
254             } else {
255                 return PermGrantState.PERMS_ALLOWED
256             }
257         }
258         if (isUserFixed) {
259             return PermGrantState.PERMS_DENIED
260         }
261         if (isOneTime) {
262             return PermGrantState.PERMS_ASK
263         }
264         return PermGrantState.PERMS_DENIED
265     }
266 
267     private fun getIsSpecialLocationState(): Boolean? {
268         if (!isSpecialLocation) {
269             return null
270         }
271 
272         val userContext = Utils.getUserContext(app, user)
273         if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
274             return LocationUtils.isLocationEnabled(userContext)
275         }
276         // The permission of the extra location controller package is determined by the
277         // status of the controller package itself.
278         if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext,
279                 permGroupName, packageName)) {
280             return LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
281         }
282         return null
283     }
284 
285     // TODO moltmann-team: Actually change mic/camera to be a foreground only permission
286     private fun shouldShowAsForegroundGroup(): Boolean {
287         return permGroupName.equals(Manifest.permission_group.CAMERA) ||
288                 permGroupName.equals(Manifest.permission_group.MICROPHONE)
289     }
290 
291     override fun onLocationStateChange(enabled: Boolean) {
292         updateIfActive()
293     }
294 
295     override fun onActive() {
296         super.onActive()
297 
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 }