summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNate Myren <ntmyren@google.com>2023-03-07 15:12:47 -0800
committerNate Myren <ntmyren@google.com>2023-03-10 17:05:25 +0000
commit0ed5ef371b9dbe39f32a8a32af42f557b3958609 (patch)
tree1d6ea6389c951e0887d913cdd32e46ff22b7b2cb
parente2bad242d21b76689c2bc5ffc27892ee3c1c8820 (diff)
downloadPermission-0ed5ef371b9dbe39f32a8a32af42f557b3958609.tar.gz
Treat AML as a "partial grant" for apps that support photo picker
When an app supports the photo picker permission prompt (by targeting T+ and explicitly requesting READ_MEDIA_VISUAL_USER_SELECTED), then treat the ACCESS_MEDIA_LOCATION permission as a "partial grant", given when clicking "select photos" or "allow all", and not silently expanding if READ_MEDIA_IMAGES/VIDEO are requested later Bug: 268075614 Test: atest PhotoPickerPermissionTest Change-Id: I5ba2dadeee3e6e4548c9eab2f859a0d6bd046a9d
-rw-r--r--PermissionController/res/values/strings.xml4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt44
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt26
4 files changed, 81 insertions, 25 deletions
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index 7c01ea800..13423312d 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -74,10 +74,10 @@
<string name="grant_dialog_button_always_allow_all">Always allow all</string>
<!-- Title for the dialog button to allow access to select photos to be shared. [CHAR LIMIT=60] -->
- <string name="grant_dialog_button_allow_selected_photos">Select photos</string>
+ <string name="grant_dialog_button_allow_selected_photos">Select photos and videos</string>
<!-- Title for the dialog button to allow access to select more photos to be shared. [CHAR LIMIT=60] -->
- <string name="grant_dialog_button_allow_more_selected_photos">Select more photos and videos</string>
+ <string name="grant_dialog_button_allow_more_selected_photos">Select more</string>
<!-- Title for the dialog button to not allow access to select more photos to be shared. [CHAR LIMIT=60] -->
<string name="grant_dialog_button_dont_allow_more_selected_photos">Don\u2019t select more</string>
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
index f63539e27..2fd6a0cac 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
@@ -20,7 +20,6 @@ package com.android.permissioncontroller.permission.ui.model
import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
-import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
import android.Manifest.permission_group.READ_MEDIA_VISUAL
import android.annotation.SuppressLint
import android.app.Activity
@@ -83,6 +82,7 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissio
import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled
import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup
import com.android.permissioncontroller.permission.utils.PermissionRationales
import com.android.permissioncontroller.permission.utils.SafetyNetLogger
import com.android.permissioncontroller.permission.utils.Utils
@@ -347,10 +347,8 @@ class AppPermissionViewModel(
selectState.isShown = true
deniedState.isChecked = !group.isGranted
- val onlyUserSelectedGranted = group.isGranted && group.permissions.values.all {
- it.name == READ_MEDIA_VISUAL_USER_SELECTED || !it.isGrantedIncludingAppOp }
- selectState.isChecked = onlyUserSelectedGranted
- allowedState.isChecked = group.isGranted && !onlyUserSelectedGranted
+ selectState.isChecked = isPartialStorageGrant(group)
+ allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group)
} else {
// Allow / Deny case
allowedState.isShown = true
@@ -653,12 +651,12 @@ class AppPermissionViewModel(
}
if (changeRequest == ChangeRequest.PHOTOS_SELECTED) {
- val nonSelectedPerms = group.permissions.keys.filter {
- it != READ_MEDIA_VISUAL_USER_SELECTED }
+ val partialGrantPerms = getPartialStorageGrantPermissionsForGroup(group)
+ val nonSelectedPerms = group.permissions.keys.filter { it !in partialGrantPerms }
var newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group,
filterPermissions = nonSelectedPerms)
newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
- filterPermissions = listOf(READ_MEDIA_VISUAL_USER_SELECTED))
+ filterPermissions = partialGrantPerms.toList())
logPermissionChanges(group, newGroup, buttonClicked)
return
}
@@ -1105,6 +1103,24 @@ class AppPermissionViewModel(
"$uid packageName=$packageName" +
"permGroupName=$permGroupName")
}
+
+ /**
+ * A partial storage grant happens when:
+ * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or
+ * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or
+ * ACCESS_MEDIA_LOCATION granted
+ */
+ private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean {
+ if (!isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) {
+ return false
+ }
+
+ val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
+
+ return group.isGranted && group.permissions.values.all {
+ it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp)
+ }
+ }
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index 86ce85039..b311afc00 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -128,6 +128,7 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissio
import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeBackgroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.revokeForegroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.utils.PermissionMapping.getPartialStorageGrantPermissionsForGroup
import com.android.permissioncontroller.permission.utils.PermissionRationales
import com.android.permissioncontroller.permission.utils.SafetyNetLogger
import com.android.permissioncontroller.permission.utils.Utils
@@ -381,7 +382,7 @@ class GrantPermissionsViewModel(
groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) {
requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true))
continue
- } else if (isVisualUserSelectedOnlyGranted(groupState.group)) {
+ } else if (isPartialStorageGrant(groupState.group)) {
// More photos dialog
message = RequestMessage.MORE_PHOTOS_MESSAGE
buttonVisibilities[ALLOW_BUTTON] = false
@@ -763,9 +764,10 @@ class GrantPermissionsViewModel(
// is still grantable.
return true
}
- } else if (perm == READ_MEDIA_VISUAL_USER_SELECTED &&
+ } else if (perm in getPartialStorageGrantPermissionsForGroup(group) &&
lightPermission.isGrantedIncludingAppOp) {
- // If USER_SELECTED is granted as fixed, we should immediately show the photo picker
+ // If a partial storage permission is granted as fixed, we should immediately show
+ // the photo picker
return true
}
reportRequestResult(perm,
@@ -795,11 +797,14 @@ class GrantPermissionsViewModel(
group.foreground.isUserSet) {
return STATE_SKIPPED
} else if (perm == READ_MEDIA_VISUAL_USER_SELECTED) {
+ val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
val otherRequestedPerms = unfilteredAffectedPermissions.filter { otherPerm ->
- otherPerm in group.permissions && otherPerm != READ_MEDIA_VISUAL_USER_SELECTED
+ otherPerm in group.permissions && otherPerm !in partialPerms
}
if (otherRequestedPerms.isEmpty()) {
- // If the user only requested USER_SELECTED, skip the request
+ // If the app requested USER_SELECTED while not supporting the photo picker, or if
+ // the app explicitly requested only USER_SELECTED and/or ACCESS_MEDIA_LOCATION,
+ // then skip the request
return STATE_SKIPPED
}
}
@@ -861,18 +866,27 @@ class GrantPermissionsViewModel(
}
// If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted,
// do not grant.
- if (isVisualUserSelectedOnlyGranted(group) ||
- HEALTH_PERMISSION_GROUP == group.permGroupName) {
+ if (isPartialStorageGrant(group) || HEALTH_PERMISSION_GROUP == group.permGroupName) {
return false
}
return true
}
- private fun isVisualUserSelectedOnlyGranted(group: LightAppPermGroup): Boolean {
- return KotlinUtils.isPhotoPickerPromptEnabled() &&
- group.permGroupName == READ_MEDIA_VISUAL && group.permissions.values.all {
- (it.name == READ_MEDIA_VISUAL_USER_SELECTED) || !it.isGrantedIncludingAppOp } &&
- group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isGrantedIncludingAppOp == true
+ /**
+ * A partial storage grant happens when:
+ * An app which doesn't support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED granted, or
+ * An app which does support the photo picker has READ_MEDIA_VISUAL_USER_SELECTED and/or
+ * ACCESS_MEDIA_LOCATION granted
+ */
+ private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean {
+ if (!KotlinUtils.isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) {
+ return false
+ }
+
+ val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
+ return group.isGranted && group.permissions.values.all {
+ it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp)
+ }
}
private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
@@ -1060,11 +1074,11 @@ class GrantPermissionsViewModel(
appPermGroup.setSelfRevoked()
appPermGroup.persistChanges(false, null, nonSelectedPerms.toSet())
} else {
- val nonSelectedPerms = groupState.affectedPermissions
- .filter { it != READ_MEDIA_VISUAL_USER_SELECTED }
+ val partialPerms = getPartialStorageGrantPermissionsForGroup(groupState.group)
+ val nonSelectedPerms = groupState.affectedPermissions.filter { it !in partialPerms }
val setUserFixed = userSelectedPerm.isUserFixed || userSelectedPerm.isUserSet
grantForegroundRuntimePermissions(app, groupState.group,
- listOf(READ_MEDIA_VISUAL_USER_SELECTED), userFixed = setUserFixed)
+ partialPerms.toList(), userFixed = setUserFixed)
revokeForegroundRuntimePermissions(app, groupState.group,
userFixed = setUserFixed, oneTime = false, filterPermissions = nonSelectedPerms)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
index c0b174c28..69b9af7e7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
@@ -23,6 +23,7 @@ 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
@@ -48,6 +49,8 @@ object PermissionMapping {
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()
@@ -59,6 +62,7 @@ object PermissionMapping {
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] =
@@ -188,6 +192,11 @@ object PermissionMapping {
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)
+ }
}
/**
@@ -321,6 +330,23 @@ object PermissionMapping {
}
/**
+ * 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