• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 @file:Suppress("DEPRECATION")
17 
18 package com.android.permissioncontroller.permission.utils
19 
20 import android.Manifest
21 import android.app.AppOpsManager
22 import android.content.pm.PackageManager
23 import android.content.pm.PermissionInfo
24 import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
25 import android.permission.flags.Flags
26 import android.util.Log
27 import com.android.modules.utils.build.SdkLevel
28 import com.android.permission.safetylabel.DataCategoryConstants
29 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
30 import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils
31 
32 /**
33  * This file contains the canonical mapping of permission to permission group, used in the
34  * Permission settings screens and grant dialog. It also includes methods related to that mapping.
35  */
36 object PermissionMapping {
37 
38     private const val LOG_TAG = "PermissionMapping"
39 
40     private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> =
41         mapOf(Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION))
42 
43     @JvmField
44     val SENSOR_DATA_PERMISSIONS: List<String> =
45         listOf(
46             Manifest.permission_group.LOCATION,
47             Manifest.permission_group.CAMERA,
48             Manifest.permission_group.MICROPHONE,
49         )
50 
51     @JvmField
52     val STORAGE_SUPERGROUP_PERMISSIONS: List<String> =
53         if (!SdkLevel.isAtLeastT()) listOf()
54         else
55             listOf(
56                 Manifest.permission_group.STORAGE,
57                 Manifest.permission_group.READ_MEDIA_AURAL,
58                 Manifest.permission_group.READ_MEDIA_VISUAL,
59             )
60 
61     val PARTIAL_MEDIA_PERMISSIONS: MutableSet<String> = mutableSetOf()
62 
63     /** Mapping permission -> group for all dangerous platform permissions */
64     private val PLATFORM_PERMISSIONS: MutableMap<String, String> = mutableMapOf()
65 
66     /** Mapping group -> permissions for all dangerous platform permissions */
67     private val PLATFORM_PERMISSION_GROUPS: MutableMap<String, MutableList<String>> = mutableMapOf()
68 
69     /** Set of groups that will be able to receive one-time grant */
70     private val ONE_TIME_PERMISSION_GROUPS: MutableSet<String> = mutableSetOf()
71 
72     private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf()
73 
74     init {
75         PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS
76         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] =
77             Manifest.permission_group.CONTACTS
78         PLATFORM_PERMISSIONS[Manifest.permission.GET_ACCOUNTS] = Manifest.permission_group.CONTACTS
79 
80         PLATFORM_PERMISSIONS[Manifest.permission.READ_CALENDAR] = Manifest.permission_group.CALENDAR
81         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALENDAR] =
82             Manifest.permission_group.CALENDAR
83 
84         // Any updates to the permissions for the SMS permission group must also be made in
85         // Permissions {@link com.android.role.controller.model.Permissions} in the role
86         // library
87         PLATFORM_PERMISSIONS[Manifest.permission.SEND_SMS] = Manifest.permission_group.SMS
88         PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_SMS] = Manifest.permission_group.SMS
89         PLATFORM_PERMISSIONS[Manifest.permission.READ_SMS] = Manifest.permission_group.SMS
90         PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_MMS] = Manifest.permission_group.SMS
91         PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_WAP_PUSH] = Manifest.permission_group.SMS
92         PLATFORM_PERMISSIONS[Manifest.permission.READ_CELL_BROADCASTS] =
93             Manifest.permission_group.SMS
94 
95         // If permissions are added to the Storage group, they must be added to the
96         // STORAGE_PERMISSIONS list in PermissionManagerService in frameworks/base
97         PLATFORM_PERMISSIONS[Manifest.permission.READ_EXTERNAL_STORAGE] =
98             Manifest.permission_group.STORAGE
99         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_EXTERNAL_STORAGE] =
100             Manifest.permission_group.STORAGE
101         if (!SdkLevel.isAtLeastT()) {
102             PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_MEDIA_LOCATION] =
103                 Manifest.permission_group.STORAGE
104         }
105 
106         if (SdkLevel.isAtLeastT()) {
107             PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_AUDIO] =
108                 Manifest.permission_group.READ_MEDIA_AURAL
109             PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_IMAGES] =
110                 Manifest.permission_group.READ_MEDIA_VISUAL
111             PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VIDEO] =
112                 Manifest.permission_group.READ_MEDIA_VISUAL
113             PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_MEDIA_LOCATION] =
114                 Manifest.permission_group.READ_MEDIA_VISUAL
115         }
116 
117         if (SdkLevel.isAtLeastU()) {
118             PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED] =
119                 Manifest.permission_group.READ_MEDIA_VISUAL
120         }
121 
122         PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_FINE_LOCATION] =
123             Manifest.permission_group.LOCATION
124         PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_COARSE_LOCATION] =
125             Manifest.permission_group.LOCATION
126         PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_BACKGROUND_LOCATION] =
127             Manifest.permission_group.LOCATION
128 
129         if (SdkLevel.isAtLeastS()) {
130             PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_ADVERTISE] =
131                 Manifest.permission_group.NEARBY_DEVICES
132             PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_CONNECT] =
133                 Manifest.permission_group.NEARBY_DEVICES
134             PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_SCAN] =
135                 Manifest.permission_group.NEARBY_DEVICES
136             PLATFORM_PERMISSIONS[Manifest.permission.UWB_RANGING] =
137                 Manifest.permission_group.NEARBY_DEVICES
138         }
139         if (SdkLevel.isAtLeastT()) {
140             PLATFORM_PERMISSIONS[Manifest.permission.NEARBY_WIFI_DEVICES] =
141                 Manifest.permission_group.NEARBY_DEVICES
142         }
143         // Ranging permission will be supported from Android B+, update this when isAtLeastB()
144         // is available.
145         if (SdkLevel.isAtLeastV() && Flags.rangingPermissionEnabled()) {
146             PLATFORM_PERMISSIONS[Manifest.permission.RANGING] =
147                 Manifest.permission_group.NEARBY_DEVICES
148         }
149         // Android XR permissions
150         if (android.xr.Flags.xrManifestEntries()) {
151             PLATFORM_PERMISSIONS[Manifest.permission.EYE_TRACKING_COARSE] =
152                 Manifest.permission_group.XR_TRACKING
153             PLATFORM_PERMISSIONS[Manifest.permission.FACE_TRACKING] =
154                 Manifest.permission_group.XR_TRACKING
155             PLATFORM_PERMISSIONS[Manifest.permission.HAND_TRACKING] =
156                 Manifest.permission_group.XR_TRACKING
157             PLATFORM_PERMISSIONS[Manifest.permission.SCENE_UNDERSTANDING_COARSE] =
158                 Manifest.permission_group.XR_TRACKING
159 
160             PLATFORM_PERMISSIONS[Manifest.permission.EYE_TRACKING_FINE] =
161                 Manifest.permission_group.XR_TRACKING_SENSITIVE
162             PLATFORM_PERMISSIONS[Manifest.permission.HEAD_TRACKING] =
163                 Manifest.permission_group.XR_TRACKING_SENSITIVE
164             PLATFORM_PERMISSIONS[Manifest.permission.SCENE_UNDERSTANDING_FINE] =
165                 Manifest.permission_group.XR_TRACKING_SENSITIVE
166         }
167 
168         // Any updates to the permissions for the CALL_LOG permission group must also be made in
169         // Permissions {@link com.android.role.controller.model.Permissions} in the role
170         // library
171         PLATFORM_PERMISSIONS[Manifest.permission.READ_CALL_LOG] = Manifest.permission_group.CALL_LOG
172         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALL_LOG] =
173             Manifest.permission_group.CALL_LOG
174         PLATFORM_PERMISSIONS[Manifest.permission.PROCESS_OUTGOING_CALLS] =
175             Manifest.permission_group.CALL_LOG
176 
177         PLATFORM_PERMISSIONS[Manifest.permission.READ_PHONE_STATE] = Manifest.permission_group.PHONE
178         PLATFORM_PERMISSIONS[Manifest.permission.READ_PHONE_NUMBERS] =
179             Manifest.permission_group.PHONE
180         PLATFORM_PERMISSIONS[Manifest.permission.CALL_PHONE] = Manifest.permission_group.PHONE
181         PLATFORM_PERMISSIONS[Manifest.permission.ADD_VOICEMAIL] = Manifest.permission_group.PHONE
182         PLATFORM_PERMISSIONS[Manifest.permission.USE_SIP] = Manifest.permission_group.PHONE
183         PLATFORM_PERMISSIONS[Manifest.permission.ANSWER_PHONE_CALLS] =
184             Manifest.permission_group.PHONE
185         PLATFORM_PERMISSIONS[Manifest.permission.ACCEPT_HANDOVER] = Manifest.permission_group.PHONE
186 
187         PLATFORM_PERMISSIONS[Manifest.permission.RECORD_AUDIO] =
188             Manifest.permission_group.MICROPHONE
189         if (SdkLevel.isAtLeastS()) {
190             PLATFORM_PERMISSIONS[Manifest.permission.RECORD_BACKGROUND_AUDIO] =
191                 Manifest.permission_group.MICROPHONE
192         }
193 
194         PLATFORM_PERMISSIONS[Manifest.permission.ACTIVITY_RECOGNITION] =
195             Manifest.permission_group.ACTIVITY_RECOGNITION
196 
197         PLATFORM_PERMISSIONS[Manifest.permission.CAMERA] = Manifest.permission_group.CAMERA
198         if (SdkLevel.isAtLeastS()) {
199             PLATFORM_PERMISSIONS[Manifest.permission.BACKGROUND_CAMERA] =
200                 Manifest.permission_group.CAMERA
201         }
202 
203         if (SdkLevel.isAtLeastT()) {
204             PLATFORM_PERMISSIONS[Manifest.permission.POST_NOTIFICATIONS] =
205                 Manifest.permission_group.NOTIFICATIONS
206         }
207 
208         if (!Flags.replaceBodySensorPermissionEnabled()) {
209             PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS] =
210                 Manifest.permission_group.SENSORS
211             if (SdkLevel.isAtLeastT()) {
212                 PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS_BACKGROUND] =
213                     Manifest.permission_group.SENSORS
214             }
215         }
216 
217         for ((permission, permissionGroup) in PLATFORM_PERMISSIONS) {
<lambda>null218             PLATFORM_PERMISSION_GROUPS.getOrPut(permissionGroup) { mutableListOf() }.add(permission)
219         }
220 
221         ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.LOCATION)
222         ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.CAMERA)
223         ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.MICROPHONE)
224 
225         if (SdkLevel.isAtLeastU()) {
226             PARTIAL_MEDIA_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
227             PARTIAL_MEDIA_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION)
228         }
229     }
230 
231     /**
232      * Get permission group a platform permission belongs to, or null if the permission is not a
233      * platform permission.
234      *
235      * @param permission the permission to resolve
236      * @return The group the permission belongs to
237      */
238     @JvmStatic
getGroupOfPlatformPermissionnull239     fun getGroupOfPlatformPermission(permission: String): String? {
240         return PLATFORM_PERMISSIONS[permission]
241     }
242 
243     /**
244      * Get name of the permission group a permission belongs to.
245      *
246      * @param permission the [info][PermissionInfo] of the permission to resolve
247      * @return The group the permission belongs to
248      */
249     @JvmStatic
getGroupOfPermissionnull250     fun getGroupOfPermission(permission: PermissionInfo): String? {
251         var groupName = getGroupOfPlatformPermission(permission.name)
252         if (groupName == null) {
253             groupName = permission.group
254         }
255         return groupName
256     }
257 
258     /**
259      * Get the names for all platform permissions belonging to a group.
260      *
261      * @param group the group
262      * @return The permission names or an empty list if the group does not have platform runtime
263      *   permissions
264      */
265     @JvmStatic
getPlatformPermissionNamesOfGroupnull266     fun getPlatformPermissionNamesOfGroup(group: String): List<String> {
267         val permissions = PLATFORM_PERMISSION_GROUPS[group]
268         return permissions ?: emptyList()
269     }
270 
271     /**
272      * Get the [infos][PermissionInfo] for all platform permissions belonging to a group.
273      *
274      * @param pm Package manager to use to resolve permission infos
275      * @param group the group
276      * @return The infos for platform permissions belonging to the group or an empty list if the
277      *   group does not have platform runtime permissions
278      */
279     @JvmStatic
getPlatformPermissionsOfGroupnull280     fun getPlatformPermissionsOfGroup(pm: PackageManager, group: String): List<PermissionInfo> {
281         val permInfos = mutableListOf<PermissionInfo>()
282         for (permName in PLATFORM_PERMISSION_GROUPS[group] ?: emptyList()) {
283             val permInfo: PermissionInfo =
284                 try {
285                     pm.getPermissionInfo(permName, 0)
286                 } catch (e: PackageManager.NameNotFoundException) {
287                     throw IllegalStateException("$permName not defined by platform", e)
288                 }
289             permInfos.add(permInfo)
290         }
291         return permInfos
292     }
293 
294     @JvmStatic
isPlatformPermissionGroupnull295     fun isPlatformPermissionGroup(name: String?): Boolean {
296         return PLATFORM_PERMISSION_GROUPS.containsKey(name)
297     }
298 
299     /**
300      * Get the names of the platform permission groups.
301      *
302      * @return the names of the platform permission groups.
303      */
304     @JvmStatic
getPlatformPermissionGroupsnull305     fun getPlatformPermissionGroups(): List<String> {
306         return PLATFORM_PERMISSION_GROUPS.keys.toList()
307     }
308 
309     /**
310      * Get the names of the runtime platform permissions
311      *
312      * @return the names of the runtime platform permissions.
313      */
314     @JvmStatic
getRuntimePlatformPermissionNamesnull315     fun getRuntimePlatformPermissionNames(): List<String> {
316         return PLATFORM_PERMISSIONS.keys.toList()
317     }
318 
319     /**
320      * Is the permissions a platform runtime permission
321      *
322      * @return the names of the runtime platform permissions.
323      */
324     @JvmStatic
isRuntimePlatformPermissionnull325     fun isRuntimePlatformPermission(permission: String): Boolean {
326         return PLATFORM_PERMISSIONS.containsKey(permission)
327     }
328 
329     /**
330      * Whether the permission group supports one-time
331      *
332      * @param permissionGroup The permission group to check
333      * @return `true` iff the group supports one-time
334      */
335     @JvmStatic
supportsOneTimeGrantnull336     fun supportsOneTimeGrant(permissionGroup: String?): Boolean {
337         return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup)
338     }
339 
340     /** Adds health permissions as platform permissions. */
341     @JvmStatic
addHealthPermissionsToPlatformnull342     fun addHealthPermissionsToPlatform(permissions: Set<String>) {
343         if (permissions.isEmpty()) {
344             Log.w(LOG_TAG, "No health connect permissions found.")
345             return
346         }
347 
348         PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP] = mutableListOf()
349 
350         for (permission in permissions) {
351             PLATFORM_PERMISSIONS[permission] = HEALTH_PERMISSION_GROUP
352             PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP]?.add(permission)
353             HEALTH_PERMISSIONS_SET.add(permission)
354             if (Flags.replaceBodySensorPermissionEnabled()) {
355                 AdminRestrictedPermissionsUtils.addAdminRestrictedPermission(permission)
356             }
357         }
358     }
359 
360     /**
361      * Get the permissions that, if granted, are considered a "partial grant" of the
362      * READ_MEDIA_VISUAL permission group. If the app declares READ_MEDIA_VISUAL_USER_SELECTED, then
363      * both READ_MEDIA_VISUAL_USER_SELECTED and ACCESS_MEDIA_LOCATION are considered a partial
364      * grant. Otherwise, ACCESS_MEDIA_LOCATION is considered a full grant (for compatibility).
365      */
getPartialStorageGrantPermissionsForGroupnull366     fun getPartialStorageGrantPermissionsForGroup(group: LightAppPermGroup): Set<String> {
367         if (!KotlinUtils.isPhotoPickerPromptSupported()) {
368             return emptySet()
369         }
370 
371         val appSupportsPickerPrompt =
372             group.permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit ==
373                 false
374 
375         return if (appSupportsPickerPrompt) {
376             PARTIAL_MEDIA_PERMISSIONS
377         } else {
378             setOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
379         }
380     }
381 
382     /**
383      * Returns the platform permission group for the permission that the provided op backs, if any.
384      */
getPlatformPermissionGroupForOpnull385     fun getPlatformPermissionGroupForOp(opName: String): String? {
386         // The OPSTR_READ_WRITE_HEALTH_DATA is a special case as unlike other ops, it does not
387         // map to a single permission. However it is safe to retrieve a permission group for it,
388         // as all permissions it maps to, map to the same permission group
389         // HEALTH_PERMISSION_GROUP.
390         if (opName == AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA) {
391             return HEALTH_PERMISSION_GROUP
392         }
393 
394         // The following app ops are special cased as they don't back any permissions on their own,
395         // but do indicate usage of certain permissions.
396         if (opName == AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE) {
397             return Manifest.permission_group.MICROPHONE
398         }
399         if (SdkLevel.isAtLeastT() && opName == AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO) {
400             return Manifest.permission_group.MICROPHONE
401         }
402         if (opName == AppOpsManager.OPSTR_PHONE_CALL_CAMERA) {
403             return Manifest.permission_group.CAMERA
404         }
405         if (SdkLevel.isAtLeastV() && opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION) {
406             return Manifest.permission_group.LOCATION
407         }
408 
409         return try {
410             AppOpsManager.opToPermission(opName)?.let { getGroupOfPlatformPermission(it) }
411         } catch (e: IllegalArgumentException) {
412             Log.wtf(LOG_TAG, "No permission group found for $opName")
413             null
414         }
415     }
416 
417     /**
418      * Get the SafetyLabel categories pertaining to a specified permission group.
419      *
420      * @return The categories, or an empty list if the group does not have a supported mapping to
421      *   safety label category
422      */
getDataCategoriesForPermissionGroupnull423     fun getDataCategoriesForPermissionGroup(permissionGroupName: String): List<String> {
424         return if (isSafetyLabelAwarePermissionGroup(permissionGroupName)) {
425             PERMISSION_GROUPS_TO_DATA_CATEGORIES[permissionGroupName] ?: emptyList()
426         } else {
427             emptyList()
428         }
429     }
430 
431     /**
432      * Whether this permission group maps to a SafetyLabel data category.
433      *
434      * @param permissionGroupName the permission group name
435      */
436     @JvmStatic
isSafetyLabelAwarePermissionGroupnull437     fun isSafetyLabelAwarePermissionGroup(permissionGroupName: String): Boolean {
438         if (!KotlinUtils.isPermissionRationaleEnabled()) {
439             return false
440         }
441 
442         return PERMISSION_GROUPS_TO_DATA_CATEGORIES.containsKey(permissionGroupName)
443     }
444 }
445