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