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