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.content.Context 22 import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED 23 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET 24 import android.os.UserHandle 25 import android.os.UserManager 26 import androidx.annotation.MainThread 27 import com.android.permissioncontroller.Constants.INVALID_SESSION_ID 28 import com.android.permissioncontroller.DumpableLog 29 import com.android.permissioncontroller.PermissionControllerStatsLog 30 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED 31 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED 32 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData 33 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 34 import com.android.permissioncontroller.permission.data.get 35 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 36 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 37 import com.android.permissioncontroller.permission.utils.KotlinUtils 38 import com.android.permissioncontroller.permission.utils.Utils 39 import com.android.permissioncontroller.permission.utils.application 40 import com.android.permissioncontroller.permission.utils.forEachInParallel 41 import com.android.permissioncontroller.permission.utils.updatePermissionFlags 42 import kotlinx.coroutines.Dispatchers.Main 43 import java.util.concurrent.atomic.AtomicBoolean 44 45 private const val LOG_TAG = "AutoRevokePermissions" 46 const val DEBUG_AUTO_REVOKE = true 47 48 private val EXEMPT_PERMISSIONS = listOf( 49 android.Manifest.permission.ACTIVITY_RECOGNITION) 50 51 private val SERVER_LOG_ID = 52 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_UNUSED_APP_PERMISSION_REVOKED 53 54 /** 55 * Revoke granted app permissions for apps that should be auto-revoked 56 * 57 * @return list of packages that successfully had their permissions revoked 58 */ 59 @MainThread 60 suspend fun revokeAppPermissions( 61 apps: Map<UserHandle, List<LightPackageInfo>>, 62 context: Context, 63 sessionId: Long = INVALID_SESSION_ID 64 ): Set<Pair<String, UserHandle>> { 65 val revokedApps = mutableSetOf<Pair<String, UserHandle>>() 66 val userManager = context.getSystemService(UserManager::class.java) 67 68 for ((user, userApps) in apps) { 69 if (userManager == null || !userManager.isUserUnlocked(user)) { 70 DumpableLog.w(LOG_TAG, "Skipping $user - locked direct boot state") 71 continue 72 } 73 userApps.forEachInParallel(Main) { pkg: LightPackageInfo -> 74 if (pkg.grantedPermissions.isEmpty()) { 75 return@forEachInParallel 76 } 77 val packageName = pkg.packageName 78 val anyPermsRevoked = AtomicBoolean(false) 79 val pkgPermGroups: Map<String, List<String>>? = 80 PackagePermissionsLiveData[packageName, user] 81 .getInitializedValue() 82 83 pkgPermGroups?.entries?.forEachInParallel(Main) { (groupName, _) -> 84 if (groupName == PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS || 85 groupName !in Utils.getPlatformPermissionGroups()) { 86 return@forEachInParallel 87 } 88 89 val group: LightAppPermGroup = 90 LightAppPermGroupLiveData[packageName, groupName, user] 91 .getInitializedValue() 92 ?: return@forEachInParallel 93 94 val fixed = group.isBackgroundFixed || group.isForegroundFixed 95 val granted = group.permissions.any { (_, perm) -> 96 perm.isGrantedIncludingAppOp && perm.name !in EXEMPT_PERMISSIONS 97 } 98 if (!fixed && 99 granted && 100 !group.isGrantedByDefault && 101 !group.isGrantedByRole && 102 !group.isRevokeWhenRequested && 103 group.isUserSensitive) { 104 105 val revocablePermissions = group.permissions.keys.toList() 106 107 if (revocablePermissions.isEmpty()) { 108 return@forEachInParallel 109 } 110 111 if (DEBUG_AUTO_REVOKE) { 112 DumpableLog.i(LOG_TAG, 113 "revokeUnused $packageName - $revocablePermissions") 114 } 115 116 val uid = group.packageInfo.uid 117 for (permName in revocablePermissions) { 118 PermissionControllerStatsLog.write( 119 PERMISSION_GRANT_REQUEST_RESULT_REPORTED, 120 sessionId, uid, packageName, permName, false, SERVER_LOG_ID) 121 } 122 123 if (DEBUG_AUTO_REVOKE) { 124 DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions") 125 DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}") 126 } 127 anyPermsRevoked.compareAndSet(false, true) 128 129 val bgRevokedState = KotlinUtils.revokeBackgroundRuntimePermissions( 130 context.application, group, 131 userFixed = false, oneTime = false, 132 filterPermissions = revocablePermissions) 133 if (DEBUG_AUTO_REVOKE) { 134 DumpableLog.i(LOG_TAG, 135 "Bg state post revocation: ${bgRevokedState.allPermissions}") 136 } 137 val fgRevokedState = KotlinUtils.revokeForegroundRuntimePermissions( 138 context.application, group, 139 userFixed = false, oneTime = false, 140 filterPermissions = revocablePermissions) 141 if (DEBUG_AUTO_REVOKE) { 142 DumpableLog.i(LOG_TAG, 143 "Fg state post revocation: ${fgRevokedState.allPermissions}") 144 } 145 146 for (permission in revocablePermissions) { 147 context.packageManager.updatePermissionFlags( 148 permission, packageName, user, 149 FLAG_PERMISSION_AUTO_REVOKED to true, 150 FLAG_PERMISSION_USER_SET to false) 151 } 152 } 153 } 154 155 if (anyPermsRevoked.get()) { 156 synchronized(revokedApps) { 157 revokedApps.add(packageName to user) 158 } 159 } 160 } 161 if (DEBUG_AUTO_REVOKE) { 162 synchronized(revokedApps) { 163 DumpableLog.i(LOG_TAG, 164 "Done auto-revoke for user ${user.identifier} - revoked $revokedApps") 165 } 166 } 167 } 168 return revokedApps 169 } 170