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