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