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