• 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.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