• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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