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