1 /* <lambda>null2 * Copyright (C) 2020 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 17 @file:JvmName("AutoRevokePermissions") 18 19 package com.android.permissioncontroller.permission.service 20 21 import android.Manifest 22 import android.content.Context 23 import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED 24 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET 25 import android.os.UserHandle 26 import android.os.UserManager 27 import android.permission.PermissionManager 28 import androidx.annotation.MainThread 29 import com.android.permissioncontroller.Constants.INVALID_SESSION_ID 30 import com.android.permissioncontroller.DumpableLog 31 import com.android.permissioncontroller.PermissionControllerStatsLog 32 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED 33 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED 34 import com.android.permissioncontroller.hibernation.getUnusedThresholdMs 35 import com.android.permissioncontroller.permission.utils.PermissionMapping 36 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData 37 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 38 import com.android.permissioncontroller.permission.data.get 39 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 40 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 41 import com.android.permissioncontroller.permission.utils.KotlinUtils 42 import com.android.permissioncontroller.permission.utils.application 43 import com.android.permissioncontroller.permission.utils.forEachInParallel 44 import com.android.permissioncontroller.permission.utils.updatePermissionFlags 45 import kotlinx.coroutines.Dispatchers.Main 46 import java.util.concurrent.atomic.AtomicBoolean 47 48 private const val LOG_TAG = "AutoRevokePermissions" 49 const val DEBUG_AUTO_REVOKE = true 50 51 private val EXEMPT_PERMISSIONS = listOf( 52 Manifest.permission.ACTIVITY_RECOGNITION, 53 Manifest.permission.POST_NOTIFICATIONS) 54 55 private val SERVER_LOG_ID = 56 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED 57 58 /** 59 * Revoke granted app permissions for apps that should be auto-revoked 60 * 61 * @return list of packages that successfully had their permissions revoked 62 */ 63 @MainThread 64 suspend fun revokeAppPermissions( 65 apps: Map<UserHandle, List<LightPackageInfo>>, 66 context: Context, 67 sessionId: Long = INVALID_SESSION_ID 68 ): Set<Pair<String, UserHandle>> { 69 val revokedApps = mutableSetOf<Pair<String, UserHandle>>() 70 val userManager = context.getSystemService(UserManager::class.java) 71 72 val permissionManager = context.getSystemService(PermissionManager::class.java)!! 73 val splitPermissionIndex = SplitPermissionIndex(permissionManager.splitPermissions) 74 75 for ((user, userApps) in apps) { 76 if (userManager == null || !userManager.isUserUnlocked(user)) { 77 DumpableLog.w(LOG_TAG, "Skipping $user - locked direct boot state") 78 continue 79 } 80 81 val pkgPermChanges = PermissionChangeStorageImpl.getInstance().loadEvents() 82 .associateBy { it.packageName } 83 // For each autorevoke-eligible app... 84 userApps.forEachInParallel(Main) forEachInParallelOuter@ { pkg: LightPackageInfo -> 85 if (pkg.grantedPermissions.isEmpty()) { 86 return@forEachInParallelOuter 87 } 88 val packageName = pkg.packageName 89 val pkgPermChange = pkgPermChanges[packageName] 90 val now = System.currentTimeMillis() 91 if (pkgPermChange != null && now - pkgPermChange.eventTime < getUnusedThresholdMs()) { 92 if (DEBUG_AUTO_REVOKE) { 93 DumpableLog.i(LOG_TAG, "Not revoking because permissions were changed " + 94 "recently for package $packageName") 95 } 96 return@forEachInParallelOuter 97 } 98 val targetSdk = pkg.targetSdkVersion 99 val pkgPermGroups: Map<String, List<String>> = 100 PackagePermissionsLiveData[packageName, user] 101 .getInitializedValue() ?: return@forEachInParallelOuter 102 103 // Determine which permGroups are revocable 104 val revocableGroups = mutableSetOf<String>() 105 for (groupName in pkgPermGroups.keys) { 106 if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) { 107 continue 108 } 109 if (groupName !in PermissionMapping.getPlatformPermissionGroups()) { 110 continue 111 } 112 val group: LightAppPermGroup = 113 LightAppPermGroupLiveData[packageName, groupName, user] 114 .getInitializedValue() ?: continue 115 val fixed = group.isBackgroundFixed || group.isForegroundFixed 116 val granted = group.permissions.any { (_, perm) -> 117 perm.isGrantedIncludingAppOp && perm.name !in EXEMPT_PERMISSIONS 118 } 119 if (!fixed && granted && 120 !group.isGrantedByDefault && 121 !group.isGrantedByRole && 122 !group.isRevokeWhenRequested && 123 group.isUserSensitive) { 124 revocableGroups.add(groupName) 125 } 126 } 127 128 // Mark any groups that split from an install-time permission as unrevocable 129 for (fromPerm in 130 pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) { 131 for (toGroup in 132 splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) { 133 revocableGroups.remove(toGroup) 134 } 135 } 136 137 // For each unrevocable group, mark all groups that it splits from and to as unrevocable 138 for (groupName in pkgPermGroups.keys) { 139 if (!revocableGroups.contains(groupName)) { 140 for (fromGroup in 141 splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) { 142 revocableGroups.remove(fromGroup) 143 } 144 for (toGroup in 145 splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) { 146 revocableGroups.remove(toGroup) 147 } 148 } 149 } 150 151 // For each revocable group, revoke all of its permissions 152 val anyPermsRevoked = AtomicBoolean(false) 153 pkgPermGroups.entries 154 .filter { revocableGroups.contains(it.key) } 155 .forEachInParallel(Main) forEachInParallelInner@ { (groupName, _) -> 156 val group: LightAppPermGroup = 157 LightAppPermGroupLiveData[packageName, groupName, user] 158 .getInitializedValue()!! 159 160 val revocablePermissions = group.permissions.keys.toList() 161 162 if (revocablePermissions.isEmpty()) { 163 return@forEachInParallelInner 164 } 165 166 if (DEBUG_AUTO_REVOKE) { 167 DumpableLog.i(LOG_TAG, 168 "revokeUnused $packageName - $revocablePermissions") 169 } 170 171 val uid = group.packageInfo.uid 172 for (permName in revocablePermissions) { 173 PermissionControllerStatsLog.write( 174 PERMISSION_GRANT_REQUEST_RESULT_REPORTED, 175 sessionId, uid, packageName, permName, false, SERVER_LOG_ID, 176 /* permission_rationale_shown = */ false) 177 } 178 179 if (DEBUG_AUTO_REVOKE) { 180 DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions") 181 DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}") 182 } 183 anyPermsRevoked.compareAndSet(false, true) 184 185 val bgRevokedState = KotlinUtils.revokeBackgroundRuntimePermissions( 186 context.application, group, 187 userFixed = false, oneTime = false, 188 filterPermissions = revocablePermissions) 189 if (DEBUG_AUTO_REVOKE) { 190 DumpableLog.i(LOG_TAG, 191 "Bg state post revocation: ${bgRevokedState.allPermissions}") 192 } 193 val fgRevokedState = KotlinUtils.revokeForegroundRuntimePermissions( 194 context.application, group, 195 userFixed = false, oneTime = false, 196 filterPermissions = revocablePermissions) 197 if (DEBUG_AUTO_REVOKE) { 198 DumpableLog.i(LOG_TAG, 199 "Fg state post revocation: ${fgRevokedState.allPermissions}") 200 } 201 202 for (permission in revocablePermissions) { 203 context.packageManager.updatePermissionFlags( 204 permission, packageName, user, 205 FLAG_PERMISSION_AUTO_REVOKED to true, 206 FLAG_PERMISSION_USER_SET to false) 207 } 208 } 209 210 if (anyPermsRevoked.get()) { 211 synchronized(revokedApps) { 212 revokedApps.add(packageName to user) 213 } 214 } 215 } 216 217 if (DEBUG_AUTO_REVOKE) { 218 synchronized(revokedApps) { 219 DumpableLog.i(LOG_TAG, 220 "Done auto-revoke for user ${user.identifier} - revoked $revokedApps") 221 } 222 } 223 } 224 return revokedApps 225 } 226