Merge cherrypicks of ['googleplex-android-review.googlesource.com/31576081'] into security-aosp-tm-release.

Change-Id: Id952bc9011ef10a2cb45ad0a0045e8a52898caa8
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
index f3addd1..d45e78f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/AppPermGroupUiInfoLiveData.kt
@@ -17,6 +17,7 @@
 package com.android.permissioncontroller.permission.data
 
 import android.Manifest
+import android.Manifest.permission_group.READ_MEDIA_VISUAL
 import android.Manifest.permission_group.STORAGE
 import android.app.AppOpsManager
 import android.app.Application
@@ -63,9 +64,8 @@
     private val isStorage = permGroupName == STORAGE
 
     init {
-        isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app,
-            permGroupName, packageName) ||
-            LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
+        isSpecialLocation = LightAppPermGroupLiveData
+            .isSpecialLocationGranted(app, packageName, permGroupName, user) != null
 
         addSource(packageInfoLiveData) {
             update()
@@ -246,9 +246,14 @@
         allPermInfos: Map<String, LightPermInfo>,
         pkg: LightPackageInfo
     ): PermGrantState {
-        val specialLocationState = getIsSpecialLocationState()
+        val specialLocationState = LightAppPermGroupLiveData
+            .isSpecialLocationGranted(app, packageName, permGroupName, user)
+        val specialFixedStorage = LightAppPermGroupLiveData
+            .isSpecialFixedStorageGranted(app, packageName, permGroupName, pkg.uid)
         if (isStorage && isFullFilesAccessGranted(pkg)) {
             return PermGrantState.PERMS_ALLOWED
+        } else if (permGroupName == READ_MEDIA_VISUAL && specialFixedStorage) {
+            return PermGrantState.PERMS_ALLOWED
         }
 
         var hasPermWithBackground = false
@@ -304,24 +309,6 @@
         return PermGrantState.PERMS_DENIED
     }
 
-    private fun getIsSpecialLocationState(): Boolean? {
-        if (!isSpecialLocation) {
-            return null
-        }
-
-        val userContext = Utils.getUserContext(app, user)
-        if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
-            return LocationUtils.isLocationEnabled(userContext)
-        }
-        // The permission of the extra location controller package is determined by the
-        // status of the controller package itself.
-        if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext,
-                permGroupName, packageName)) {
-            return LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
-        }
-        return null
-    }
-
     private fun isFullFilesAccessGranted(pkg: LightPackageInfo): Boolean {
         val packageState = if (!FullStoragePermissionAppsLiveData.isStale) {
             val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
index 3621319..3f0ff86 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
@@ -16,7 +16,13 @@
 
 package com.android.permissioncontroller.permission.data
 
+import android.Manifest.permission_group.READ_MEDIA_VISUAL
+import android.Manifest.permission_group.STORAGE
+import android.app.AppOpsManager
+import android.app.AppOpsManager.MODE_ALLOWED
+import android.app.AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES
 import android.app.Application
+import android.app.role.RoleManager
 import android.content.pm.PackageManager
 import android.content.pm.PermissionInfo
 import android.os.Build
@@ -114,22 +120,16 @@
                     foregroundPerms)
         }
 
-        // Determine if this app permission group is a special location package or provider
-        var specialLocationGrant: Boolean? = null
-        val userContext = Utils.getUserContext(app, user)
-        if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
-            specialLocationGrant = LocationUtils.isLocationEnabled(userContext)
-        } else if (LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName,
-                packageName)) {
-            // The permission of the extra location controller package is determined by the status
-            // of the controller package itself.
-            specialLocationGrant = LocationUtils.isExtraLocationControllerPackageEnabled(
-                userContext)
-        }
-
         val hasInstallToRuntimeSplit = hasInstallToRuntimeSplit(packageInfo, permissionMap)
-        value = LightAppPermGroup(packageInfo, permGroup.groupInfo, permissionMap,
-            hasInstallToRuntimeSplit, specialLocationGrant)
+        value =
+            LightAppPermGroup(
+                packageInfo,
+                permGroup.groupInfo,
+                permissionMap,
+                hasInstallToRuntimeSplit,
+                isSpecialLocationGranted(app, packageName, permGroupName, user),
+                isSpecialFixedStorageGranted(app, packageName, permGroupName, packageInfo.uid)
+            )
     }
 
     /**
@@ -209,5 +209,56 @@
             return LightAppPermGroupLiveData(PermissionControllerApplication.get(),
                 key.first, key.second, key.third)
         }
+
+        private const val SYSTEM_GALLERY_ROLE_NAME = "android.app.role.SYSTEM_GALLERY"
+
+        // Returns true if this app is the location provider or location extra package, and location
+        // access is granted, false if it is the provider/extra, and location is not granted, and
+        // null if it is not a special package
+        fun isSpecialLocationGranted(
+            app: Application,
+            packageName: String,
+            permGroupName: String,
+            user: UserHandle
+        ): Boolean? {
+            val userContext = Utils.getUserContext(app, user)
+            return if (
+                LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)
+            ) {
+                LocationUtils.isLocationEnabled(userContext)
+            } else if (
+                LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
+            ) {
+                // The permission of the extra location controller package is determined by the status
+                // of the controller package itself.
+                LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
+            } else {
+                null
+            }
+        }
+
+        // Gallery role is static, so we only need to get the set gallery app once
+        private val systemGalleryApps: List<String> by lazy {
+            val roleManager = PermissionControllerApplication.get()
+                .getSystemService(RoleManager::class.java) ?: return@lazy emptyList()
+            roleManager.getRoleHolders(SYSTEM_GALLERY_ROLE_NAME)
+        }
+
+        fun isSpecialFixedStorageGranted(
+            app: Application,
+            packageName: String,
+            permGroupName: String,
+            uid: Int
+        ): Boolean {
+            if (permGroupName != READ_MEDIA_VISUAL && permGroupName != STORAGE) {
+                return false
+            }
+            if (packageName !in systemGalleryApps) {
+                return false
+            }
+            // This is the storage group, and the gallery app. Check the write media app op
+            val appOps = app.getSystemService(AppOpsManager::class.java)
+            return appOps.unsafeCheckOpNoThrow(OPSTR_WRITE_MEDIA_IMAGES, uid, packageName) == MODE_ALLOWED
+        }
     }
 }
\ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
index 2e7d3b4..515825b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
@@ -33,18 +33,26 @@
  * @param hasInstallToRuntimeSplit If this group contains a permission that was previously an
  * install permission, but is currently a runtime permission
  * @param specialLocationGrant If this package is the location provider, or the extra location
- * package, then the grant state of the group is not determined by the grant state of individual
- * permissions, but by other system properties
+ *   package, then the grant state of the group is not determined by the grant state of individual
+ *   permissions, but by other system properties
+ * @param specialFixedStorageGrant If this package holds the SYSTEM_GALLERY role, and has the
+ *   WRITE_MEDIA_IMAGES app op granted, then we should show the grant state of the storage
+ *   permissions as system fixed and granted.
+ *
  */
 data class LightAppPermGroup(
     val packageInfo: LightPackageInfo,
     val permGroupInfo: LightPermGroupInfo,
     val allPermissions: Map<String, LightPermission>,
     val hasInstallToRuntimeSplit: Boolean,
-    val specialLocationGrant: Boolean?
+    val specialLocationGrant: Boolean?,
+    val specialFixedStorageGrant: Boolean,
 ) {
-    constructor(pI: LightPackageInfo, pGI: LightPermGroupInfo, perms: Map<String, LightPermission>):
-        this(pI, pGI, perms, false, null)
+    constructor(
+        pI: LightPackageInfo,
+        pGI: LightPermGroupInfo,
+        perms: Map<String, LightPermission>
+    ) : this(pI, pGI, perms, false, null, false)
 
     /**
      * All unrestricted permissions. Usually restricted permissions are ignored
@@ -82,11 +90,23 @@
 
     val isPlatformPermissionGroup = permGroupInfo.packageName == Utils.OS_PKG
 
-    val foreground = AppPermSubGroup(permissions.filter { it.key in foregroundPermNames },
-        packageInfo, isPlatformPermissionGroup, specialLocationGrant)
+    val foreground =
+        AppPermSubGroup(
+            permissions.filter { it.key in foregroundPermNames },
+            packageInfo,
+            isPlatformPermissionGroup,
+            specialLocationGrant,
+            specialFixedStorageGrant
+        )
 
-    val background = AppPermSubGroup(permissions.filter { it.key in backgroundPermNames },
-        packageInfo, isPlatformPermissionGroup, specialLocationGrant)
+    val background =
+        AppPermSubGroup(
+            permissions.filter { it.key in backgroundPermNames },
+            packageInfo,
+            isPlatformPermissionGroup,
+            specialLocationGrant,
+            specialFixedStorageGrant
+        )
 
     /**
      * Whether or not this App Permission Group has a permission which has a background mode
@@ -181,17 +201,19 @@
      * in the full group
      * @param isPlatformPermissionGroup Whether this is a platform permission group
      * @param specialLocationGrant Whether this is a special location package
+     * @param specialFixedStorageGrant Whether this is a special storage grant
      */
     data class AppPermSubGroup internal constructor(
         private val permissions: Map<String, LightPermission>,
         private val packageInfo: LightPackageInfo,
         private val isPlatformPermissionGroup: Boolean,
-        private val specialLocationGrant: Boolean?
+        private val specialLocationGrant: Boolean?,
+        private val specialFixedStorageGrant: Boolean
     ) {
         /** Whether any of this App Permission SubGroup's permissions are granted */
         val isGranted =
             specialLocationGrant
-                ?: permissions.any {
+                ?: specialFixedStorageGrant || permissions.any {
                     val mayGrantByPlatformOrSystem =
                         !isPlatformPermissionGroup || it.value.isPlatformOrSystem
                     it.value.isGranted && mayGrantByPlatformOrSystem
@@ -231,10 +253,8 @@
          */
         val isPolicyFixed = permissions.any { it.value.isPolicyFixed }
 
-        /**
-         * Whether any of this App Permission Subgroup's permissions are fixed by the system
-         */
-        val isSystemFixed = permissions.any { it.value.isSystemFixed }
+        /** Whether any of this App Permission Subgroup's permissions are fixed by the system */
+        val isSystemFixed = permissions.any { it.value.isSystemFixed } || specialFixedStorageGrant
 
         /**
          * Whether any of this App Permission Subgroup's permissions are fixed by the user
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
index 12d7d45..efcd059 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -393,9 +393,15 @@
                         perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
                         perm.foregroundPerms)
 
-                bgAppsWithExemption[pkgName] = LightAppPermGroup(bgApp.packageInfo,
-                        bgApp.permGroupInfo, allPermissionsWithxemption,
-                        bgApp.hasInstallToRuntimeSplit, bgApp.specialLocationGrant)
+                bgAppsWithExemption[pkgName] =
+                    LightAppPermGroup(
+                        bgApp.packageInfo,
+                        bgApp.permGroupInfo,
+                        allPermissionsWithxemption,
+                        bgApp.hasInstallToRuntimeSplit,
+                        bgApp.specialLocationGrant,
+                        bgApp.specialFixedStorageGrant,
+                    )
             }
 
             exemptions.addAll(bgLocExemptions)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
index e7f4874..c1f9e7a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -472,8 +472,14 @@
             newPerms[permName] = LightPermission(group.packageInfo, perm.permInfo,
                 perm.isGranted, perm.flags or flagsToSet, perm.foregroundPerms)
         }
-        return LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
-            group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+        return LightAppPermGroup(
+            group.packageInfo,
+            group.permGroupInfo,
+            newPerms,
+            group.hasInstallToRuntimeSplit,
+            group.specialLocationGrant,
+            group.specialFixedStorageGrant,
+        )
     }
 
     /**
@@ -557,8 +563,15 @@
             (app.getSystemService(ActivityManager::class.java) as ActivityManager).killUid(
                 group.packageInfo.uid, KILL_REASON_APP_OP_CHANGE)
         }
-        val newGroup = LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
-            group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+        val newGroup =
+            LightAppPermGroup(
+                group.packageInfo,
+                group.permGroupInfo,
+                newPerms,
+                group.hasInstallToRuntimeSplit,
+                group.specialLocationGrant,
+                group.specialFixedStorageGrant,
+            )
         // If any permission in the group is one time granted, start one time permission session.
         if (newGroup.permissions.any { it.value.isOneTime && it.value.isGranted }) {
             if (SdkLevel.isAtLeastT()) {
@@ -756,8 +769,15 @@
                 group.packageInfo.uid, KILL_REASON_APP_OP_CHANGE)
         }
 
-        val newGroup = LightAppPermGroup(group.packageInfo, group.permGroupInfo, newPerms,
-            group.hasInstallToRuntimeSplit, group.specialLocationGrant)
+        val newGroup =
+            LightAppPermGroup(
+                group.packageInfo,
+                group.permGroupInfo,
+                newPerms,
+                group.hasInstallToRuntimeSplit,
+                group.specialLocationGrant,
+                group.specialFixedStorageGrant,
+            )
 
         if (wasOneTime && !anyPermsOfPackageOneTimeGranted(app, newGroup.packageInfo, newGroup)) {
             app.getSystemService(PermissionManager::class.java)!!.stopOneTimePermissionSession(
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
index 0d0edc8..836ff30 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/utils/GrantRevokeTests.kt
@@ -200,7 +200,7 @@
         perms: Map<String, LightPermission> = emptyMap()
     ): LightAppPermGroup {
         val pGi = LightPermGroupInfo(PERM_GROUP_NAME, TEST_PACKAGE_NAME, 0, 0, 0, false)
-        return LightAppPermGroup(pkgInfo, pGi, perms, false, false)
+        return LightAppPermGroup(pkgInfo, pGi, perms, false, false, false)
     }
 
     /**