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.ecm.EnhancedConfirmationStatsLogUtils 35 import com.android.permissioncontroller.hibernation.getUnusedThresholdMs 36 import com.android.permissioncontroller.permission.data.AutoRevokedPackagesLiveData 37 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData 38 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 39 import com.android.permissioncontroller.permission.data.get 40 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 41 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 42 import com.android.permissioncontroller.permission.utils.KotlinUtils 43 import com.android.permissioncontroller.permission.utils.PermissionMapping 44 import com.android.permissioncontroller.permission.utils.application 45 import com.android.permissioncontroller.permission.utils.forEachInParallel 46 import com.android.permissioncontroller.permission.utils.updatePermissionFlags 47 import java.util.concurrent.atomic.AtomicBoolean 48 import kotlinx.coroutines.Dispatchers.Main 49 50 private const val LOG_TAG = "AutoRevokePermissions" 51 const val DEBUG_AUTO_REVOKE = true 52 53 val AUTO_REVOKE_EXEMPT_PERMISSIONS = 54 listOf(Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.POST_NOTIFICATIONS) 55 56 private val SERVER_LOG_ID = 57 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED 58 59 /** 60 * Revoke granted app permissions for apps that should be auto-revoked 61 * 62 * @return list of packages that successfully had their permissions revoked 63 */ 64 @MainThread 65 suspend fun revokeAppPermissions( 66 apps: Map<UserHandle, List<LightPackageInfo>>, 67 context: Context, 68 sessionId: Long = INVALID_SESSION_ID 69 ): Set<Pair<String, UserHandle>> { 70 val revokedApps = mutableSetOf<Pair<String, UserHandle>>() 71 val userManager = context.getSystemService(UserManager::class.java) 72 73 val permissionManager = context.getSystemService(PermissionManager::class.java)!! 74 val splitPermissions = SplitPermissionIndex(permissionManager.splitPermissions) 75 76 for ((user, userApps) in apps) { 77 if (userManager == null || !userManager.isUserUnlocked(user)) { 78 DumpableLog.w(LOG_TAG, "Skipping $user - locked direct boot state") 79 continue 80 } 81 82 val pkgPermChanges = 83 PermissionChangeStorageImpl.getInstance().loadEvents().associateBy { it.packageName } 84 // For each autorevoke-eligible app... 85 userApps.forEachInParallel(Main) forEachInParallelOuter@{ pkg: LightPackageInfo -> 86 if (pkg.grantedPermissions.isEmpty()) { 87 if (DEBUG_AUTO_REVOKE) { 88 DumpableLog.i(LOG_TAG, "${pkg.packageName}: no granted permissions") 89 } 90 return@forEachInParallelOuter 91 } 92 val packageName = pkg.packageName 93 val pkgPermChange = pkgPermChanges[packageName] 94 val now = System.currentTimeMillis() 95 if (pkgPermChange != null && now - pkgPermChange.eventTime < getUnusedThresholdMs()) { 96 if (DEBUG_AUTO_REVOKE) { 97 DumpableLog.i( 98 LOG_TAG, 99 "Not revoking because permissions were changed " + 100 "recently for package $packageName" 101 ) 102 } 103 return@forEachInParallelOuter 104 } 105 val appTargetSdk = pkg.targetSdkVersion 106 val pkgPermGroups: Map<String, List<String>>? = 107 PackagePermissionsLiveData[packageName, user].getInitializedValue() 108 109 if (pkgPermGroups.isNullOrEmpty()) { 110 if (DEBUG_AUTO_REVOKE) { 111 DumpableLog.i(LOG_TAG, "$packageName: no permission groups found.") 112 } 113 return@forEachInParallelOuter 114 } 115 116 if (DEBUG_AUTO_REVOKE) { 117 DumpableLog.i(LOG_TAG, "$packageName: perm groups: ${pkgPermGroups.keys}.") 118 } 119 120 // Determine which permGroups are revocable 121 val revocableGroups = mutableSetOf<String>() 122 for (groupName in pkgPermGroups.keys) { 123 if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) { 124 continue 125 } 126 if (groupName !in PermissionMapping.getPlatformPermissionGroups()) { 127 continue 128 } 129 val group: LightAppPermGroup = 130 LightAppPermGroupLiveData[packageName, groupName, user].getInitializedValue() 131 ?: continue 132 val fixed = group.isBackgroundFixed || group.isForegroundFixed 133 val granted = 134 group.permissions.any { (_, perm) -> 135 perm.isGranted && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS 136 } 137 if ( 138 !fixed && 139 granted && 140 !group.isGrantedByDefault && 141 !group.isGrantedByRole && 142 !group.isRevokeWhenRequested && 143 group.isUserSensitive 144 ) { 145 revocableGroups.add(groupName) 146 } 147 } 148 149 if (DEBUG_AUTO_REVOKE) { 150 DumpableLog.i(LOG_TAG, "$packageName: initial revocable groups: $revocableGroups") 151 } 152 153 // Mark any groups that split from an install-time permission as unrevocable 154 val requestedInstallPermissions = 155 pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList() 156 for (permissionName in requestedInstallPermissions) { 157 val permissionGroups = 158 splitPermissions.getPermissionGroupsFromSplitPermission( 159 permissionName, 160 appTargetSdk 161 ) 162 for (permissionGroup in permissionGroups) { 163 revocableGroups.remove(permissionGroup) 164 } 165 } 166 167 // For each unrevocable group, mark all groups that it splits from and to as unrevocable 168 for (groupName in pkgPermGroups.keys) { 169 if (!revocableGroups.contains(groupName)) { 170 val sourcePermissionGroups = 171 splitPermissions.getSplitPermissionGroups(groupName, appTargetSdk) 172 for (sourcePermissionGroup in sourcePermissionGroups) { 173 revocableGroups.remove(sourcePermissionGroup) 174 } 175 val newPermissionGroups = 176 splitPermissions.getPermissionGroupsFromSplitPermissionGroup( 177 groupName, 178 appTargetSdk 179 ) 180 for (permissionGroup in newPermissionGroups) { 181 revocableGroups.remove(permissionGroup) 182 } 183 } 184 } 185 186 if (DEBUG_AUTO_REVOKE) { 187 DumpableLog.i(LOG_TAG, "$packageName: final revocable groups: $revocableGroups") 188 } 189 // For each revocable group, revoke all of its permissions 190 val anyPermsRevoked = AtomicBoolean(false) 191 pkgPermGroups.entries 192 .filter { revocableGroups.contains(it.key) } 193 .forEachInParallel(Main) forEachInParallelInner@{ (groupName, _) -> 194 val group: LightAppPermGroup = 195 LightAppPermGroupLiveData[packageName, groupName, user] 196 .getInitializedValue()!! 197 198 val revocablePermissions = group.permissions.keys.toList() 199 200 if (revocablePermissions.isEmpty()) { 201 if (DEBUG_AUTO_REVOKE) { 202 DumpableLog.i(LOG_TAG, "$packageName: revocable permissions empty") 203 } 204 return@forEachInParallelInner 205 } 206 207 if (DEBUG_AUTO_REVOKE) { 208 DumpableLog.i(LOG_TAG, "revokeUnused $packageName - $revocablePermissions") 209 } 210 211 val uid = group.packageInfo.uid 212 for (permName in revocablePermissions) { 213 val isPackageRestrictedByEnhancedConfirmation = 214 EnhancedConfirmationStatsLogUtils.isPackageEcmRestricted( 215 context, 216 packageName, 217 uid 218 ) 219 PermissionControllerStatsLog.write( 220 PERMISSION_GRANT_REQUEST_RESULT_REPORTED, 221 sessionId, 222 uid, 223 packageName, 224 permName, 225 false, 226 SERVER_LOG_ID, 227 /* permission_rationale_shown = */ false, 228 isPackageRestrictedByEnhancedConfirmation 229 ) 230 } 231 232 if (DEBUG_AUTO_REVOKE) { 233 DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions") 234 DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}") 235 } 236 anyPermsRevoked.compareAndSet(false, true) 237 238 val bgRevokedState = 239 KotlinUtils.revokeBackgroundRuntimePermissions( 240 context.application, 241 group, 242 userFixed = false, 243 oneTime = false, 244 filterPermissions = revocablePermissions 245 ) 246 if (DEBUG_AUTO_REVOKE) { 247 DumpableLog.i( 248 LOG_TAG, 249 "Bg state post revocation: ${bgRevokedState.allPermissions}" 250 ) 251 } 252 val fgRevokedState = 253 KotlinUtils.revokeForegroundRuntimePermissions( 254 context.application, 255 group, 256 userFixed = false, 257 oneTime = false, 258 filterPermissions = revocablePermissions 259 ) 260 if (DEBUG_AUTO_REVOKE) { 261 DumpableLog.i( 262 LOG_TAG, 263 "Fg state post revocation: ${fgRevokedState.allPermissions}" 264 ) 265 } 266 267 for (permission in revocablePermissions) { 268 context.packageManager.updatePermissionFlags( 269 permission, 270 packageName, 271 user, 272 FLAG_PERMISSION_AUTO_REVOKED to true, 273 FLAG_PERMISSION_USER_SET to false 274 ) 275 } 276 } 277 278 if (anyPermsRevoked.get()) { 279 synchronized(revokedApps) { revokedApps.add(packageName to user) } 280 } 281 } 282 283 if (DEBUG_AUTO_REVOKE) { 284 synchronized(revokedApps) { 285 DumpableLog.i( 286 LOG_TAG, 287 "Done auto-revoke for user ${user.identifier} - revoked $revokedApps" 288 ) 289 } 290 } 291 } 292 if (revokedApps.isNotEmpty()) { 293 AutoRevokedPackagesLiveData.update() 294 } 295 return revokedApps 296 } 297