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