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