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 import com.android.modules.utils.build.SdkLevel 27 import com.android.permission.safetylabel.DataCategoryConstants 28 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 29 30 /** 31 * This file contains the canonical mapping of permission to permission group, used in the 32 * Permission settings screens and grant dialog. It also includes methods related to that mapping. 33 */ 34 object PermissionMapping { 35 36 private const val LOG_TAG = "PermissionMapping" 37 38 private val PERMISSION_GROUPS_TO_DATA_CATEGORIES: Map<String, List<String>> = 39 mapOf(Manifest.permission_group.LOCATION to listOf(DataCategoryConstants.CATEGORY_LOCATION)) 40 41 @JvmField 42 val SENSOR_DATA_PERMISSIONS: List<String> = 43 listOf( 44 Manifest.permission_group.LOCATION, 45 Manifest.permission_group.CAMERA, 46 Manifest.permission_group.MICROPHONE 47 ) 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 59 val PARTIAL_MEDIA_PERMISSIONS: MutableSet<String> = mutableSetOf() 60 61 /** Mapping permission -> group for all dangerous platform permissions */ 62 private val PLATFORM_PERMISSIONS: MutableMap<String, String> = mutableMapOf() 63 64 /** Mapping group -> permissions for all dangerous platform permissions */ 65 private val PLATFORM_PERMISSION_GROUPS: MutableMap<String, MutableList<String>> = mutableMapOf() 66 67 /** Set of groups that will be able to receive one-time grant */ 68 private val ONE_TIME_PERMISSION_GROUPS: MutableSet<String> = mutableSetOf() 69 70 private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf() 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 * @return The group the permission belongs to 206 */ 207 @JvmStatic getGroupOfPlatformPermissionnull208 fun getGroupOfPlatformPermission(permission: String): String? { 209 return PLATFORM_PERMISSIONS[permission] 210 } 211 212 /** 213 * Get name of the permission group a permission belongs to. 214 * 215 * @param permission the [info][PermissionInfo] of the permission to resolve 216 * @return The group the permission belongs to 217 */ 218 @JvmStatic getGroupOfPermissionnull219 fun getGroupOfPermission(permission: PermissionInfo): String? { 220 var groupName = getGroupOfPlatformPermission(permission.name) 221 if (groupName == null) { 222 groupName = permission.group 223 } 224 return groupName 225 } 226 227 /** 228 * Get the names for all platform permissions belonging to a group. 229 * 230 * @param group the group 231 * @return The permission names or an empty list if the group does not have platform runtime 232 * permissions 233 */ 234 @JvmStatic getPlatformPermissionNamesOfGroupnull235 fun getPlatformPermissionNamesOfGroup(group: String): List<String> { 236 val permissions = PLATFORM_PERMISSION_GROUPS[group] 237 return permissions ?: emptyList() 238 } 239 240 /** 241 * Get the [infos][PermissionInfo] for all platform permissions belonging to a group. 242 * 243 * @param pm Package manager to use to resolve permission infos 244 * @param group the group 245 * @return The infos for platform permissions belonging to the group or an empty list if the 246 * group does not have platform runtime permissions 247 */ 248 @JvmStatic getPlatformPermissionsOfGroupnull249 fun getPlatformPermissionsOfGroup(pm: PackageManager, group: String): List<PermissionInfo> { 250 val permInfos = mutableListOf<PermissionInfo>() 251 for (permName in PLATFORM_PERMISSION_GROUPS[group] ?: emptyList()) { 252 val permInfo: PermissionInfo = 253 try { 254 pm.getPermissionInfo(permName, 0) 255 } catch (e: PackageManager.NameNotFoundException) { 256 throw IllegalStateException("$permName not defined by platform", e) 257 } 258 permInfos.add(permInfo) 259 } 260 return permInfos 261 } 262 263 @JvmStatic isPlatformPermissionGroupnull264 fun isPlatformPermissionGroup(name: String?): Boolean { 265 return PLATFORM_PERMISSION_GROUPS.containsKey(name) 266 } 267 268 /** 269 * Get the names of the platform permission groups. 270 * 271 * @return the names of the platform permission groups. 272 */ 273 @JvmStatic getPlatformPermissionGroupsnull274 fun getPlatformPermissionGroups(): List<String> { 275 return PLATFORM_PERMISSION_GROUPS.keys.toList() 276 } 277 278 /** 279 * Get the names of the runtime platform permissions 280 * 281 * @return the names of the runtime platform permissions. 282 */ 283 @JvmStatic getRuntimePlatformPermissionNamesnull284 fun getRuntimePlatformPermissionNames(): List<String> { 285 return PLATFORM_PERMISSIONS.keys.toList() 286 } 287 288 /** 289 * Is the permissions a platform runtime permission 290 * 291 * @return the names of the runtime platform permissions. 292 */ 293 @JvmStatic isRuntimePlatformPermissionnull294 fun isRuntimePlatformPermission(permission: String): Boolean { 295 return PLATFORM_PERMISSIONS.containsKey(permission) 296 } 297 298 /** 299 * Whether the permission group supports one-time 300 * 301 * @param permissionGroup The permission group to check 302 * @return `true` iff the group supports one-time 303 */ 304 @JvmStatic supportsOneTimeGrantnull305 fun supportsOneTimeGrant(permissionGroup: String?): Boolean { 306 return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup) 307 } 308 309 /** Adds health permissions as platform permissions. */ 310 @JvmStatic addHealthPermissionsToPlatformnull311 fun addHealthPermissionsToPlatform(permissions: Set<String>) { 312 if (permissions.isEmpty()) { 313 Log.w(LOG_TAG, "No health connect permissions found.") 314 return 315 } 316 317 PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP] = mutableListOf() 318 319 for (permission in permissions) { 320 PLATFORM_PERMISSIONS[permission] = HEALTH_PERMISSION_GROUP 321 PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP]?.add(permission) 322 HEALTH_PERMISSIONS_SET.add(permission) 323 } 324 } 325 326 /** 327 * Get the permissions that, if granted, are considered a "partial grant" of the 328 * READ_MEDIA_VISUAL permission group. If the app declares READ_MEDIA_VISUAL_USER_SELECTED, then 329 * both READ_MEDIA_VISUAL_USER_SELECTED and ACCESS_MEDIA_LOCATION are considered a partial 330 * grant. Otherwise, ACCESS_MEDIA_LOCATION is considered a full grant (for compatibility). 331 */ getPartialStorageGrantPermissionsForGroupnull332 fun getPartialStorageGrantPermissionsForGroup(group: LightAppPermGroup): Set<String> { 333 if (!KotlinUtils.isPhotoPickerPromptSupported()) { 334 return emptySet() 335 } 336 337 val appSupportsPickerPrompt = 338 group.permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == 339 false 340 341 return if (appSupportsPickerPrompt) { 342 PARTIAL_MEDIA_PERMISSIONS 343 } else { 344 setOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) 345 } 346 } 347 348 /** Returns true if the given permission is a health platform permission. */ 349 @JvmStatic isHealthPermissionnull350 fun isHealthPermission(permissionName: String): Boolean { 351 return HEALTH_PERMISSIONS_SET.contains(permissionName) 352 } 353 354 /** 355 * Returns the platform permission group for the permission that the provided op backs, if any. 356 */ getPlatformPermissionGroupForOpnull357 fun getPlatformPermissionGroupForOp(opName: String): String? { 358 // The OPSTR_READ_WRITE_HEALTH_DATA is a special case as unlike other ops, it does not 359 // map to a single permission. However it is safe to retrieve a permission group for it, 360 // as all permissions it maps to, map to the same permission group 361 // HEALTH_PERMISSION_GROUP. 362 if (opName == AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA) { 363 return HEALTH_PERMISSION_GROUP 364 } 365 366 // The following app ops are special cased as they don't back any permissions on their own, 367 // but do indicate usage of certain permissions. 368 if (opName == AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE) { 369 return Manifest.permission_group.MICROPHONE 370 } 371 if (SdkLevel.isAtLeastT() && opName == AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO) { 372 return Manifest.permission_group.MICROPHONE 373 } 374 if (opName == AppOpsManager.OPSTR_PHONE_CALL_CAMERA) { 375 return Manifest.permission_group.CAMERA 376 } 377 378 return try { 379 AppOpsManager.opToPermission(opName)?.let { getGroupOfPlatformPermission(it) } 380 } catch (e: IllegalArgumentException) { 381 Log.wtf(LOG_TAG, "No permission group found for $opName") 382 null 383 } 384 } 385 386 /** 387 * Get the SafetyLabel categories pertaining to a specified permission group. 388 * 389 * @return The categories, or an empty list if the group does not have a supported mapping to 390 * safety label category 391 */ getDataCategoriesForPermissionGroupnull392 fun getDataCategoriesForPermissionGroup(permissionGroupName: String): List<String> { 393 return if (isSafetyLabelAwarePermissionGroup(permissionGroupName)) { 394 PERMISSION_GROUPS_TO_DATA_CATEGORIES[permissionGroupName] ?: emptyList() 395 } else { 396 emptyList() 397 } 398 } 399 400 /** 401 * Whether this permission group maps to a SafetyLabel data category. 402 * 403 * @param permissionGroupName the permission group name 404 */ 405 @JvmStatic isSafetyLabelAwarePermissionGroupnull406 fun isSafetyLabelAwarePermissionGroup(permissionGroupName: String): Boolean { 407 if (!KotlinUtils.isPermissionRationaleEnabled()) { 408 return false 409 } 410 411 return PERMISSION_GROUPS_TO_DATA_CATEGORIES.containsKey(permissionGroupName) 412 } 413 } 414