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