| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| @file:Suppress("DEPRECATION") |
| |
| package com.android.permissioncontroller.permission.utils |
| |
| import android.Manifest |
| import android.content.pm.PackageManager |
| import android.content.pm.PermissionInfo |
| import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP |
| import android.util.Log |
| import com.android.modules.utils.build.SdkLevel |
| import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup |
| |
| /** |
| * This file contains the canonical mapping of permission to permission group, used in the |
| * Permission settings screens and grant dialog. It also includes methods related to that mapping. |
| */ |
| object PermissionMapping { |
| |
| private val LOG_TAG = "PermissionMapping" |
| |
| @JvmField |
| val SENSOR_DATA_PERMISSIONS: List<String> = |
| listOf( |
| Manifest.permission_group.LOCATION, |
| Manifest.permission_group.CAMERA, |
| Manifest.permission_group.MICROPHONE) |
| |
| @JvmField |
| val STORAGE_SUPERGROUP_PERMISSIONS: List<String> = |
| if (!SdkLevel.isAtLeastT()) listOf() |
| else |
| listOf( |
| Manifest.permission_group.STORAGE, |
| Manifest.permission_group.READ_MEDIA_AURAL, |
| Manifest.permission_group.READ_MEDIA_VISUAL) |
| |
| val PARTIAL_MEDIA_PERMISSIONS: MutableSet<String> = mutableSetOf() |
| |
| /** Mapping permission -> group for all dangerous platform permissions */ |
| private val PLATFORM_PERMISSIONS: MutableMap<String, String> = mutableMapOf() |
| |
| /** Mapping group -> permissions for all dangerous platform permissions */ |
| private val PLATFORM_PERMISSION_GROUPS: MutableMap<String, MutableList<String>> = mutableMapOf() |
| |
| /** Set of groups that will be able to receive one-time grant */ |
| private val ONE_TIME_PERMISSION_GROUPS: MutableSet<String> = mutableSetOf() |
| |
| private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf() |
| |
| |
| init { |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS |
| PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] = |
| Manifest.permission_group.CONTACTS |
| PLATFORM_PERMISSIONS[Manifest.permission.GET_ACCOUNTS] = Manifest.permission_group.CONTACTS |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_CALENDAR] = Manifest.permission_group.CALENDAR |
| PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALENDAR] = |
| Manifest.permission_group.CALENDAR |
| |
| // Any updates to the permissions for the SMS permission group must also be made in |
| // Permissions {@link com.android.role.controller.model.Permissions} in the role |
| // library |
| PLATFORM_PERMISSIONS[Manifest.permission.SEND_SMS] = Manifest.permission_group.SMS |
| PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_SMS] = Manifest.permission_group.SMS |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_SMS] = Manifest.permission_group.SMS |
| PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_MMS] = Manifest.permission_group.SMS |
| PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_WAP_PUSH] = Manifest.permission_group.SMS |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_CELL_BROADCASTS] = |
| Manifest.permission_group.SMS |
| |
| // If permissions are added to the Storage group, they must be added to the |
| // STORAGE_PERMISSIONS list in PermissionManagerService in frameworks/base |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_EXTERNAL_STORAGE] = |
| Manifest.permission_group.STORAGE |
| PLATFORM_PERMISSIONS[Manifest.permission.WRITE_EXTERNAL_STORAGE] = |
| Manifest.permission_group.STORAGE |
| if (!SdkLevel.isAtLeastT()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_MEDIA_LOCATION] = |
| Manifest.permission_group.STORAGE |
| } |
| |
| if (SdkLevel.isAtLeastT()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_AUDIO] = |
| Manifest.permission_group.READ_MEDIA_AURAL |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_IMAGES] = |
| Manifest.permission_group.READ_MEDIA_VISUAL |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VIDEO] = |
| Manifest.permission_group.READ_MEDIA_VISUAL |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_MEDIA_LOCATION] = |
| Manifest.permission_group.READ_MEDIA_VISUAL |
| } |
| |
| if (SdkLevel.isAtLeastU()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED] = |
| Manifest.permission_group.READ_MEDIA_VISUAL |
| } |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_FINE_LOCATION] = |
| Manifest.permission_group.LOCATION |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_COARSE_LOCATION] = |
| Manifest.permission_group.LOCATION |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_BACKGROUND_LOCATION] = |
| Manifest.permission_group.LOCATION |
| |
| if (SdkLevel.isAtLeastS()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_ADVERTISE] = |
| Manifest.permission_group.NEARBY_DEVICES |
| PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_CONNECT] = |
| Manifest.permission_group.NEARBY_DEVICES |
| PLATFORM_PERMISSIONS[Manifest.permission.BLUETOOTH_SCAN] = |
| Manifest.permission_group.NEARBY_DEVICES |
| PLATFORM_PERMISSIONS[Manifest.permission.UWB_RANGING] = |
| Manifest.permission_group.NEARBY_DEVICES |
| } |
| if (SdkLevel.isAtLeastT()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.NEARBY_WIFI_DEVICES] = |
| Manifest.permission_group.NEARBY_DEVICES |
| } |
| |
| // Any updates to the permissions for the CALL_LOG permission group must also be made in |
| // Permissions {@link com.android.role.controller.model.Permissions} in the role |
| // library |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_CALL_LOG] = Manifest.permission_group.CALL_LOG |
| PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALL_LOG] = |
| Manifest.permission_group.CALL_LOG |
| PLATFORM_PERMISSIONS[Manifest.permission.PROCESS_OUTGOING_CALLS] = |
| Manifest.permission_group.CALL_LOG |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_PHONE_STATE] = Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.READ_PHONE_NUMBERS] = |
| Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.CALL_PHONE] = Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.ADD_VOICEMAIL] = Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.USE_SIP] = Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.ANSWER_PHONE_CALLS] = |
| Manifest.permission_group.PHONE |
| PLATFORM_PERMISSIONS[Manifest.permission.ACCEPT_HANDOVER] = Manifest.permission_group.PHONE |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.RECORD_AUDIO] = |
| Manifest.permission_group.MICROPHONE |
| if (SdkLevel.isAtLeastS()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.RECORD_BACKGROUND_AUDIO] = |
| Manifest.permission_group.MICROPHONE |
| } |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.ACTIVITY_RECOGNITION] = |
| Manifest.permission_group.ACTIVITY_RECOGNITION |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.CAMERA] = Manifest.permission_group.CAMERA |
| if (SdkLevel.isAtLeastS()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.BACKGROUND_CAMERA] = |
| Manifest.permission_group.CAMERA |
| } |
| |
| PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS] = Manifest.permission_group.SENSORS |
| |
| if (SdkLevel.isAtLeastT()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.POST_NOTIFICATIONS] = |
| Manifest.permission_group.NOTIFICATIONS |
| PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS_BACKGROUND] = |
| Manifest.permission_group.SENSORS |
| } |
| |
| if (SdkLevel.isAtLeastU()) { |
| PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE] = |
| Manifest.permission_group.SENSORS |
| PLATFORM_PERMISSIONS[Manifest |
| .permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND] = |
| Manifest.permission_group.SENSORS |
| } |
| |
| for ((permission, permissionGroup) in PLATFORM_PERMISSIONS) { |
| PLATFORM_PERMISSION_GROUPS.getOrPut(permissionGroup) { mutableListOf() }.add(permission) |
| } |
| |
| ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.LOCATION) |
| ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.CAMERA) |
| ONE_TIME_PERMISSION_GROUPS.add(Manifest.permission_group.MICROPHONE) |
| |
| if (SdkLevel.isAtLeastU()) { |
| PARTIAL_MEDIA_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) |
| PARTIAL_MEDIA_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION) |
| } |
| } |
| |
| /** |
| * Get permission group a platform permission belongs to, or null if the permission is not a |
| * platform permission. |
| * |
| * @param permission the permission to resolve |
| * |
| * @return The group the permission belongs to |
| */ |
| @JvmStatic |
| fun getGroupOfPlatformPermission(permission: String): String? { |
| return PLATFORM_PERMISSIONS[permission] |
| } |
| |
| /** |
| * Get name of the permission group a permission belongs to. |
| * |
| * @param permission the [info][PermissionInfo] of the permission to resolve |
| * |
| * @return The group the permission belongs to |
| */ |
| @JvmStatic |
| fun getGroupOfPermission(permission: PermissionInfo): String? { |
| var groupName = getGroupOfPlatformPermission(permission.name) |
| if (groupName == null) { |
| groupName = permission.group |
| } |
| return groupName |
| } |
| |
| /** |
| * Get the names for all platform permissions belonging to a group. |
| * |
| * @param group the group |
| * |
| * @return The permission names or an empty list if the group does not have platform runtime |
| * permissions |
| */ |
| @JvmStatic |
| fun getPlatformPermissionNamesOfGroup(group: String): List<String> { |
| val permissions = PLATFORM_PERMISSION_GROUPS[group] |
| return permissions ?: emptyList() |
| } |
| |
| /** |
| * Get the [infos][PermissionInfo] for all platform permissions belonging to a group. |
| * |
| * @param pm Package manager to use to resolve permission infos |
| * @param group the group |
| * |
| * @return The infos for platform permissions belonging to the group or an empty list if the |
| * group does not have platform runtime permissions |
| */ |
| @JvmStatic |
| fun getPlatformPermissionsOfGroup(pm: PackageManager, group: String): List<PermissionInfo> { |
| val permInfos = mutableListOf<PermissionInfo>() |
| for (permName in PLATFORM_PERMISSION_GROUPS[group] ?: emptyList()) { |
| val permInfo: PermissionInfo = try { |
| pm.getPermissionInfo(permName, 0) |
| } catch (e: PackageManager.NameNotFoundException) { |
| throw IllegalStateException("$permName not defined by platform", e) |
| } |
| permInfos.add(permInfo) |
| } |
| return permInfos |
| } |
| |
| @JvmStatic |
| fun isPlatformPermissionGroup(name: String?): Boolean { |
| return PLATFORM_PERMISSION_GROUPS.containsKey(name) |
| } |
| |
| /** |
| * Get the names of the platform permission groups. |
| * |
| * @return the names of the platform permission groups. |
| */ |
| @JvmStatic |
| fun getPlatformPermissionGroups(): List<String> { |
| return PLATFORM_PERMISSION_GROUPS.keys.toList() |
| } |
| |
| /** |
| * Get the names of the runtime platform permissions |
| * |
| * @return the names of the runtime platform permissions. |
| */ |
| @JvmStatic |
| fun getRuntimePlatformPermissionNames(): List<String> { |
| return PLATFORM_PERMISSIONS.keys.toList() |
| } |
| |
| /** |
| * Is the permissions a platform runtime permission |
| * |
| * @return the names of the runtime platform permissions. |
| */ |
| @JvmStatic |
| fun isRuntimePlatformPermission(permission: String): Boolean { |
| return PLATFORM_PERMISSIONS.containsKey(permission) |
| } |
| |
| /** |
| * Whether the permission group supports one-time |
| * @param permissionGroup The permission group to check |
| * @return `true` iff the group supports one-time |
| */ |
| @JvmStatic |
| fun supportsOneTimeGrant(permissionGroup: String?): Boolean { |
| return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup) |
| } |
| |
| /** |
| * Adds health permissions as platform permissions. |
| */ |
| @JvmStatic |
| fun addHealthPermissionsToPlatform(permissions: Set<String>) { |
| if (permissions.isEmpty()) { |
| Log.w(LOG_TAG, "No health connect permissions found.") |
| return |
| } |
| |
| PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP] = mutableListOf() |
| |
| for (permission in permissions) { |
| PLATFORM_PERMISSIONS[permission] = HEALTH_PERMISSION_GROUP |
| PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP]?.add(permission) |
| HEALTH_PERMISSIONS_SET.add(permission) |
| } |
| } |
| |
| /** |
| * Get the permissions that, if granted, are considered a "partial grant" of the |
| * READ_MEDIA_VISUAL permission group. If the app declares READ_MEDIA_VISUAL_USER_SELECTED, then |
| * both READ_MEDIA_VISUAL_USER_SELECTED and ACCESS_MEDIA_LOCATION are considered a partial |
| * grant. Otherwise, ACCESS_MEDIA_LOCATION is considered a full grant (for compatibility). |
| */ |
| fun getPartialStorageGrantPermissionsForGroup(group: LightAppPermGroup): Set<String> { |
| val appSupportsPickerPrompt = group |
| .permissions[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == false |
| |
| return if (appSupportsPickerPrompt) { |
| PARTIAL_MEDIA_PERMISSIONS |
| } else { |
| setOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) |
| } |
| } |
| |
| /** |
| * Returns true if the given permission is a health platform permission. |
| */ |
| @JvmStatic |
| fun isHealthPermission(permissionName: String): Boolean { |
| return HEALTH_PERMISSIONS_SET.contains(permissionName) |
| } |
| } |