• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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 package com.android.permissioncontroller.permission.domain.usecase.v31
18 
19 import android.Manifest
20 import android.app.Application
21 import android.content.pm.ApplicationInfo
22 import android.content.pm.PackageInfo
23 import android.content.pm.PackageManager
24 import android.os.UserHandle
25 import android.util.Log
26 import com.android.modules.utils.build.SdkLevel
27 import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
28 import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository
29 import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
30 import com.android.permissioncontroller.permission.domain.model.v31.PackagePermissionGroupUsageModel
31 import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel
32 import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModelWrapper
33 import com.android.permissioncontroller.permission.utils.PermissionMapping
34 import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository
35 import com.android.permissioncontroller.role.data.repository.v31.RoleRepository
36 import com.android.permissioncontroller.user.data.repository.v31.UserRepository
37 import kotlinx.coroutines.flow.Flow
38 import kotlinx.coroutines.flow.map
39 
40 /**
41  * This use case read app ops data and transform that data to show the private data access by apps
42  * in privacy dashboard.
43  */
44 class GetPermissionGroupUsageUseCase(
45     private val packageRepository: PackageRepository,
46     private val permissionRepository: PermissionRepository,
47     private val appOpRepository: AppOpRepository,
48     private val roleRepository: RoleRepository,
49     private val userRepository: UserRepository,
50 ) {
51     /**
52      * Returns a flow (i.e. a stream) of permission group usages (i.e. the private data accesses)
53      * for privacy dashboard page.
54      */
55     operator fun invoke(): Flow<PermissionGroupUsageModelWrapper> {
56         return appOpRepository.packageAppOpsUsages.map { packagesOps ->
57             val exemptedPackages = roleRepository.getExemptedPackages()
58             val currentUsers = userRepository.getUserProfilesIncludingCurrentUser()
59 
60             val usages =
61                 packagesOps
62                     .mapToPermissionGroups()
63                     .filter { it.userId in currentUsers }
64                     .filter { it.packageName !in exemptedPackages }
65                     .filterQuietProfilesIfNeeded(currentUsers)
66                     .filterNonRequestedOps()
67                     .buildPermissionGroupUsageModels()
68             PermissionGroupUsageModelWrapper.Success(usages)
69         }
70     }
71 
72     /** filter private space usages if needed. */
73     private suspend fun List<PackagePermissionGroupUsageModel>.filterQuietProfilesIfNeeded(
74         currentUsers: List<Int>
75     ): List<PackagePermissionGroupUsageModel> {
76         if (!SdkLevel.isAtLeastV()) {
77             return this
78         }
79         val usersQuietModeEnabledMap =
80             currentUsers.associateWith { userId -> userRepository.isQuietModeEnabled(userId) }
81         val usersShouldShowInQuietModeMap =
82             currentUsers.associateWith { userId -> userRepository.shouldShowInQuietMode(userId) }
83         return filter {
84             val isQuietModeEnabled = checkNotNull(usersQuietModeEnabledMap[it.userId])
85             val shouldShowInQuietMode = checkNotNull(usersShouldShowInQuietModeMap[it.userId])
86             !isQuietModeEnabled || shouldShowInQuietMode
87         }
88     }
89 
90     private fun List<PackageAppOpUsageModel>.mapToPermissionGroups():
91         List<PackagePermissionGroupUsageModel> {
92         return mapNotNull { packageOps ->
93             val permissionGroupUsages =
94                 packageOps.usages
95                     .mapNotNull {
96                         val permissionGroup =
97                             PermissionMapping.getPlatformPermissionGroupForOp(it.appOpName)
98                         if (permissionGroup != null) {
99                             Pair(permissionGroup, it.lastAccessTimestampMillis)
100                         } else {
101                             Log.w(LOG_TAG, "No permission group found for op: ${it.appOpName}")
102                             null
103                         }
104                     }
105                     .groupBy { it.first } // group by permission group name
106                     .map { it -> // keep permission group and recent usage time
107                         it.key to it.value.map { it.second }.maxOf { it }
108                     }
109                     .toMap()
110 
111             if (permissionGroupUsages.isNotEmpty()) {
112                 PackagePermissionGroupUsageModel(
113                     packageOps.packageName,
114                     permissionGroupUsages,
115                     packageOps.userId
116                 )
117             } else {
118                 null
119             }
120         }
121     }
122 
123     /** Filter Ops where the corresponding permission group is no longer requested by the package */
124     private suspend fun List<PackagePermissionGroupUsageModel>.filterNonRequestedOps():
125         List<PackagePermissionGroupUsageModel> {
126         return mapNotNull { pkgOps ->
127             val userHandle = UserHandle.of(pkgOps.userId)
128             val packageInfo = packageRepository.getPackageInfo(pkgOps.packageName, userHandle)
129             val filteredOps =
130                 pkgOps.usages.filter { permissionGroupUsage ->
131                     packageInfo?.requestedPermissions?.any { permission ->
132                         permissionGroupUsage.key ==
133                             PermissionMapping.getGroupOfPlatformPermission(permission)
134                     } ?: false
135                 }
136             if (filteredOps.isNotEmpty()) {
137                 PackagePermissionGroupUsageModel(pkgOps.packageName, filteredOps, pkgOps.userId)
138             } else {
139                 null
140             }
141         }
142     }
143 
144     private suspend fun List<PackagePermissionGroupUsageModel>.buildPermissionGroupUsageModels():
145         List<PermissionGroupUsageModel> {
146         return flatMap { pkgOps ->
147             pkgOps.usages.map { permGroupLastAccessTimeEntry ->
148                 PermissionGroupUsageModel(
149                     permGroupLastAccessTimeEntry.key,
150                     permGroupLastAccessTimeEntry.value,
151                     isPermissionGroupUserSensitive(
152                         pkgOps.packageName,
153                         permGroupLastAccessTimeEntry.key,
154                         pkgOps.userId
155                     )
156                 )
157             }
158         }
159     }
160 
161     /**
162      * Determines if an app's permission group is user-sensitive. if the permission group is not
163      * user sensitive then its only shown when user choose `Show system` option
164      */
165     private suspend fun isPermissionGroupUserSensitive(
166         packageName: String,
167         permissionGroup: String,
168         userId: Int
169     ): Boolean {
170         if (isTelecomPackage(packageName, permissionGroup)) {
171             return false
172         }
173         val userHandle = UserHandle.of(userId)
174         val packageInfo = packageRepository.getPackageInfo(packageName, userHandle) ?: return false
175         // if not a system app, the permission group must be user sensitive
176         if (packageInfo.applicationFlags and ApplicationInfo.FLAG_SYSTEM == 0) {
177             return true
178         }
179 
180         packageInfo.requestedPermissions.forEachIndexed { index, permissionName ->
181             if (PermissionMapping.getGroupOfPlatformPermission(permissionName) == permissionGroup) {
182                 val permFlags =
183                     permissionRepository.getPermissionFlags(permissionName, packageName, userHandle)
184                 val packageFlags = packageInfo.requestedPermissionsFlags[index]
185                 val isPermissionGranted =
186                     packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 &&
187                         permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0
188                 if (isPermissionUserSensitive(isPermissionGranted, permFlags)) {
189                     return true
190                 }
191             }
192         }
193         return false
194     }
195 
196     private fun isTelecomPackage(packageName: String, permissionGroup: String): Boolean {
197         return packageName == TELECOM_PACKAGE &&
198             (permissionGroup == Manifest.permission_group.CAMERA ||
199                 permissionGroup == Manifest.permission_group.MICROPHONE)
200     }
201 
202     private fun isPermissionUserSensitive(
203         isPermissionGranted: Boolean,
204         permissionFlags: Int
205     ): Boolean {
206         return if (isPermissionGranted) {
207             permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0
208         } else {
209             permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0
210         }
211     }
212 
213     companion object {
214         private val LOG_TAG = GetPermissionGroupUsageUseCase::class.java.simpleName
215         private const val TELECOM_PACKAGE = "com.android.server.telecom"
216 
217         fun create(app: Application): GetPermissionGroupUsageUseCase {
218             val permissionRepository = PermissionRepository.getInstance(app)
219             val userRepository = UserRepository.getInstance(app)
220             val packageRepository = PackageRepository.getInstance(app)
221             val roleRepository = RoleRepository.getInstance(app)
222             val appOpRepository = AppOpRepository.getInstance(app, permissionRepository)
223 
224             return GetPermissionGroupUsageUseCase(
225                 packageRepository,
226                 permissionRepository,
227                 appOpRepository,
228                 roleRepository,
229                 userRepository
230             )
231         }
232     }
233 }
234