1 /* <lambda>null2 * Copyright (C) 2019 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.data 18 19 import android.Manifest 20 import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED 21 import android.Manifest.permission_group.STORAGE 22 import android.app.AppOpsManager 23 import android.app.Application 24 import android.content.pm.PackageManager 25 import android.content.pm.PermissionInfo 26 import android.os.Build 27 import android.os.UserHandle 28 import com.android.permissioncontroller.PermissionControllerApplication 29 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo 30 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState 31 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 32 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo 33 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo 34 import com.android.permissioncontroller.permission.model.livedatatypes.PermState 35 import com.android.permissioncontroller.permission.utils.PermissionMapping.isPlatformPermissionGroup 36 import com.android.permissioncontroller.permission.utils.LocationUtils 37 import com.android.permissioncontroller.permission.utils.Utils 38 import kotlinx.coroutines.Job 39 40 /** 41 * A LiveData representing UI properties of an App Permission Group: 42 * <ul> 43 * <li>shouldShow</li> 44 * <li>isSystem</li> 45 * <li>isGranted</li> 46 * </ul> 47 * 48 * @param app The current application 49 * @param packageName The name of the package 50 * @param permGroupName The name of the permission group whose permissions are observed 51 * @param user The user of the package 52 */ 53 class AppPermGroupUiInfoLiveData private constructor( 54 private val app: Application, 55 private val packageName: String, 56 private val permGroupName: String, 57 private val user: UserHandle 58 ) : SmartAsyncMediatorLiveData<AppPermGroupUiInfo>(), LocationUtils.LocationListener { 59 60 private var isSpecialLocation = false 61 private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user] 62 private val permGroupLiveData = PermGroupLiveData[permGroupName] 63 private val permissionStateLiveData = PermStateLiveData[packageName, permGroupName, user] 64 private val isStorage = permGroupName == STORAGE 65 66 init { 67 isSpecialLocation = LocationUtils.isLocationGroupAndProvider(app, 68 permGroupName, packageName) || 69 LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName) 70 71 addSource(packageInfoLiveData) { 72 update() 73 } 74 75 addSource(permGroupLiveData) { 76 update() 77 } 78 79 addSource(permissionStateLiveData) { 80 update() 81 } 82 } 83 84 override suspend fun loadDataAndPostValue(job: Job) { 85 if (job.isCancelled) { 86 return 87 } 88 val packageInfo = packageInfoLiveData.value 89 val permissionGroup = permGroupLiveData.value 90 val permissionState = permissionStateLiveData.value 91 92 if (packageInfo == null || permissionGroup == null || permissionState == null) { 93 if (packageInfoLiveData.isInitialized && permGroupLiveData.isInitialized && 94 permissionStateLiveData.isInitialized) { 95 invalidateSingle(Triple(packageName, permGroupName, user)) 96 postValue(null) 97 } 98 return 99 } 100 101 postValue(getAppPermGroupUiInfo(packageInfo, permissionGroup.groupInfo, 102 permissionGroup.permissionInfos, permissionState)) 103 } 104 105 /** 106 * Determines if the UI should show a given package, if that package is a system app, and 107 * if it has granted permissions in this LiveData's permission group. 108 * 109 * @param packageInfo The PackageInfo of the package we wish to examine 110 * @param groupInfo The groupInfo of the permission group we wish to examine 111 * @param allPermInfos All of the PermissionInfos in the permission group 112 * @param permissionState The flags and grant state for all permissions in the permission 113 * group that this package requests 114 */ 115 private fun getAppPermGroupUiInfo( 116 packageInfo: LightPackageInfo, 117 groupInfo: LightPermGroupInfo, 118 allPermInfos: Map<String, LightPermInfo>, 119 permissionState: Map<String, PermState> 120 ): AppPermGroupUiInfo { 121 /* 122 * Filter out any permission infos in the permission group that this package 123 * does not request. 124 */ 125 val requestedPermissionInfos = 126 allPermInfos.filter { permissionState.containsKey(it.key) }.values 127 128 val shouldShow = packageInfo.enabled && 129 isGrantableAndNotLegacyPlatform(packageInfo, groupInfo, requestedPermissionInfos) && 130 (!isStorage || Utils.shouldShowStorage(packageInfo)) 131 132 val isSystemApp = !isUserSensitive(permissionState) 133 134 val isUserSet = isUserSet(permissionState) 135 136 val permGrantState = 137 getGrantedIncludingBackground(permissionState, allPermInfos, packageInfo) 138 139 return AppPermGroupUiInfo(shouldShow, permGrantState, isSystemApp, isUserSet) 140 } 141 142 /** 143 * Determines if a package permission group is able to be granted, and whether or not it is a 144 * legacy system permission group. 145 * 146 * @param packageInfo The PackageInfo of the package we are examining 147 * @param groupInfo The Permission Group Info of the permission group we are examining 148 * @param permissionInfos The LightPermInfos corresponding to the permissions in the 149 * permission group that this package requests 150 * 151 * @return True if the app permission group is grantable, and is not a legacy system permission, 152 * false otherwise. 153 */ 154 private fun isGrantableAndNotLegacyPlatform( 155 packageInfo: LightPackageInfo, 156 groupInfo: LightPermGroupInfo, 157 permissionInfos: Collection<LightPermInfo> 158 ): Boolean { 159 if (groupInfo.packageName == Utils.OS_PKG && 160 !isPlatformPermissionGroup(groupInfo.name)) { 161 return false 162 } 163 164 var hasInstantPerm = false 165 var hasPreRuntime = false 166 167 for (permissionInfo in permissionInfos) { 168 if (permissionInfo.protectionFlags and 169 PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY == 0) { 170 hasPreRuntime = true 171 } 172 173 if (permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT != 0) { 174 hasInstantPerm = true 175 } 176 } 177 178 val isGrantingAllowed = (!packageInfo.isInstantApp || hasInstantPerm) && 179 (packageInfo.targetSdkVersion >= Build.VERSION_CODES.M || hasPreRuntime) 180 if (!isGrantingAllowed) { 181 return false 182 } 183 184 return true 185 } 186 187 /** 188 * Determines if an app's permission group is user-sensitive. If an app is not user sensitive, 189 * then it is considered a system app, and hidden in the UI by default. 190 * 191 * @param permissionState The permission flags and grant state corresponding to the permissions 192 * in this group requested by a given app 193 * 194 * @return Whether or not this package requests a user sensitive permission in the given 195 * permission group 196 */ 197 private fun isUserSensitive(permissionState: Map<String, PermState>): Boolean { 198 if (!isPlatformPermissionGroup(permGroupName)) { 199 return true 200 } 201 202 for (permissionName in permissionState.keys) { 203 val flags = permissionState[permissionName]?.permFlags ?: return true 204 val granted = permissionState[permissionName]?.granted ?: return true 205 if ((granted && 206 flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0) || 207 (!granted && 208 flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0)) { 209 return true 210 } 211 } 212 return false 213 } 214 215 /** 216 * Determines if the app permission group is user set 217 * 218 * @param permissionState The permission flags and grant state corresponding to the permissions 219 * in this group requested by a given app 220 * 221 * @return Whether or not any of the permissions in this group have been set or fixed by the 222 * user 223 */ 224 private fun isUserSet(permissionState: Map<String, PermState>): Boolean { 225 val flagMask = PackageManager.FLAG_PERMISSION_USER_SET or 226 PackageManager.FLAG_PERMISSION_USER_FIXED 227 return permissionState.any { (it.value.permFlags and flagMask) != 0 } 228 } 229 230 /** 231 * Determines if this app permission group is granted, granted in foreground only, or denied. 232 * It is granted if it either requests no background permissions, and has at least one requested 233 * permission that is granted, or has granted at least one requested background permission. 234 * It is granted in foreground only if it has at least one non-background permission granted, 235 * and has denied all requested background permissions. It is denied if all requested 236 * permissions are denied. 237 * 238 * @param permissionState The permission flags and grant state corresponding to the permissions 239 * in this group requested by a given app 240 * @param allPermInfos All of the permissionInfos in the permission group of this app 241 * permission group 242 * 243 * @return The int code corresponding to the app permission group state, either allowed, allowed 244 * in foreground only, or denied. 245 */ 246 private fun getGrantedIncludingBackground( 247 permissionState: Map<String, PermState>, 248 allPermInfos: Map<String, LightPermInfo>, 249 pkg: LightPackageInfo 250 ): PermGrantState { 251 val specialLocationState = getIsSpecialLocationState() 252 if (isStorage && isFullFilesAccessGranted(pkg)) { 253 return PermGrantState.PERMS_ALLOWED 254 } 255 256 var hasPermWithBackground = false 257 var isUserFixed = false 258 259 for ((permName, permState) in permissionState) { 260 val permInfo = allPermInfos[permName] ?: continue 261 permInfo.backgroundPermission?.let { backgroundPerm -> 262 hasPermWithBackground = true 263 if (permissionState[backgroundPerm]?.granted == true && 264 (permissionState[backgroundPerm]!!.permFlags and 265 PackageManager.FLAG_PERMISSION_ONE_TIME == 0) && 266 specialLocationState != false) { 267 return PermGrantState.PERMS_ALLOWED_ALWAYS 268 } 269 } 270 isUserFixed = isUserFixed || 271 permState.permFlags and PackageManager.FLAG_PERMISSION_USER_FIXED != 0 272 } 273 274 // isOneTime indicates whether all granted permissions in permission states are one-time 275 // permissions 276 val isOneTime = permissionState.any { 277 it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0 } && 278 !permissionState.any { 279 it.value.permFlags and PackageManager.FLAG_PERMISSION_ONE_TIME == 0 && 280 it.value.granted } 281 282 val supportsRuntime = pkg.targetSdkVersion >= Build.VERSION_CODES.M 283 val anyAllowed = specialLocationState ?: permissionState.any { (_, state) -> 284 state.granted || (supportsRuntime && 285 (state.permFlags and PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) 286 } 287 val onlySelectedPhotosGranted = 288 permissionState.containsKey(READ_MEDIA_VISUAL_USER_SELECTED) && 289 permissionState.all { (permName, state) -> 290 (permName == READ_MEDIA_VISUAL_USER_SELECTED && state.granted) || 291 (permName != READ_MEDIA_VISUAL_USER_SELECTED && !state.granted) 292 } 293 if (anyAllowed && (hasPermWithBackground || shouldShowAsForegroundGroup())) { 294 return if (isOneTime) { 295 PermGrantState.PERMS_ASK 296 } else { 297 PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY 298 } 299 } else if (anyAllowed) { 300 return if (isOneTime || onlySelectedPhotosGranted) { 301 PermGrantState.PERMS_ASK 302 } else { 303 PermGrantState.PERMS_ALLOWED 304 } 305 } 306 if (isUserFixed) { 307 return PermGrantState.PERMS_DENIED 308 } 309 if (isOneTime) { 310 return PermGrantState.PERMS_ASK 311 } 312 return PermGrantState.PERMS_DENIED 313 } 314 315 private fun getIsSpecialLocationState(): Boolean? { 316 if (!isSpecialLocation) { 317 return null 318 } 319 320 val userContext = Utils.getUserContext(app, user) 321 if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) { 322 return LocationUtils.isLocationEnabled(userContext) 323 } 324 // The permission of the extra location controller package is determined by the 325 // status of the controller package itself. 326 if (LocationUtils.isLocationGroupAndControllerExtraPackage(userContext, 327 permGroupName, packageName)) { 328 return LocationUtils.isExtraLocationControllerPackageEnabled(userContext) 329 } 330 return null 331 } 332 333 private fun isFullFilesAccessGranted(pkg: LightPackageInfo): Boolean { 334 val packageState = if (!FullStoragePermissionAppsLiveData.isStale) { 335 val fullStoragePackages = FullStoragePermissionAppsLiveData.value ?: return false 336 fullStoragePackages.find { 337 it.packageName == packageName && it.user == user 338 } ?: return false 339 } else { 340 val appOpsManager = Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.uid)) 341 .getSystemService(AppOpsManager::class.java)!! 342 FullStoragePermissionAppsLiveData.getFullStorageStateForPackage( 343 appOpsManager, pkg) ?: return false 344 } 345 return !packageState.isLegacy && packageState.isGranted 346 } 347 348 // TODO moltmann-team: Actually change mic/camera to be a foreground only permission 349 private fun shouldShowAsForegroundGroup(): Boolean { 350 return permGroupName.equals(Manifest.permission_group.CAMERA) || 351 permGroupName.equals(Manifest.permission_group.MICROPHONE) 352 } 353 354 override fun onLocationStateChange(enabled: Boolean) { 355 update() 356 } 357 358 override fun onActive() { 359 super.onActive() 360 if (isSpecialLocation) { 361 LocationUtils.addLocationListener(this) 362 update() 363 } 364 } 365 366 override fun onInactive() { 367 super.onInactive() 368 369 if (isSpecialLocation) { 370 LocationUtils.removeLocationListener(this) 371 } 372 } 373 374 /** 375 * Repository for AppPermGroupUiInfoLiveDatas. 376 * <p> Key value is a triple of string package name, string permission group name, and UserHandle, 377 * value is its corresponding LiveData. 378 */ 379 companion object : DataRepositoryForPackage<Triple<String, String, UserHandle>, 380 AppPermGroupUiInfoLiveData>() { 381 override fun newValue(key: Triple<String, String, UserHandle>): 382 AppPermGroupUiInfoLiveData { 383 return AppPermGroupUiInfoLiveData(PermissionControllerApplication.get(), 384 key.first, key.second, key.third) 385 } 386 } 387 } 388