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.app.Application 20 import android.content.pm.PackageManager 21 import android.content.pm.PermissionInfo 22 import android.os.Build 23 import android.os.UserHandle 24 import android.permission.PermissionManager 25 import android.util.Log 26 import com.android.permissioncontroller.PermissionControllerApplication 27 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 28 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 29 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission 30 import com.android.permissioncontroller.permission.utils.KotlinUtils 31 import com.android.permissioncontroller.permission.utils.LocationUtils 32 import com.android.permissioncontroller.permission.utils.Utils 33 import com.android.permissioncontroller.permission.utils.Utils.OS_PKG 34 35 /** 36 * A LiveData which represents the permissions for one package and permission group. 37 * 38 * @param app The current application 39 * @param packageName The name of the package 40 * @param permGroupName The name of the permission group 41 * @param user The user of the package 42 */ 43 class LightAppPermGroupLiveData 44 private constructor( 45 private val app: Application, 46 private val packageName: String, 47 private val permGroupName: String, 48 private val user: UserHandle, 49 private val deviceId: Int 50 ) : SmartUpdateMediatorLiveData<LightAppPermGroup?>(), LocationUtils.LocationListener { 51 52 private val LOG_TAG = this::class.java.simpleName 53 54 private var isSpecialLocation = false 55 private val permStateLiveData = PermStateLiveData[packageName, permGroupName, user, deviceId] 56 private val permGroupLiveData = PermGroupLiveData[permGroupName] 57 private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user, deviceId] 58 private val fgPermNamesLiveData = ForegroundPermNamesLiveData 59 60 init { 61 isSpecialLocation = 62 LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) || 63 LocationUtils.isLocationGroupAndControllerExtraPackage( 64 app, 65 permGroupName, 66 packageName 67 ) 68 69 addSource(fgPermNamesLiveData) { update() } 70 71 val key = KotlinUtils.Quadruple(packageName, permGroupName, user, deviceId) 72 73 addSource(permStateLiveData) { permStates -> 74 if (permStates == null && permStateLiveData.isInitialized) { 75 invalidateSingle(key) 76 value = null 77 } else { 78 update() 79 } 80 } 81 82 addSource(permGroupLiveData) { permGroup -> 83 if (permGroup == null && permGroupLiveData.isInitialized) { 84 invalidateSingle(key) 85 value = null 86 } else { 87 update() 88 } 89 } 90 91 addSource(packageInfoLiveData) { packageInfo -> 92 if (packageInfo == null && packageInfoLiveData.isInitialized) { 93 invalidateSingle(key) 94 value = null 95 } else { 96 update() 97 } 98 } 99 } 100 101 override fun onUpdate() { 102 val permStates = permStateLiveData.value ?: return 103 val permGroup = permGroupLiveData.value ?: return 104 val packageInfo = packageInfoLiveData.value ?: return 105 val allForegroundPerms = fgPermNamesLiveData.value ?: return 106 107 // Do not allow toggling pre-M custom perm groups 108 if ( 109 packageInfo.targetSdkVersion < Build.VERSION_CODES.M && 110 permGroup.groupInfo.packageName != OS_PKG 111 ) { 112 value = LightAppPermGroup(packageInfo, permGroup.groupInfo, emptyMap()) 113 return 114 } 115 116 val permissionMap = mutableMapOf<String, LightPermission>() 117 for ((permName, permState) in permStates) { 118 val permInfo = permGroup.permissionInfos[permName] ?: continue 119 val foregroundPerms = allForegroundPerms[permName] 120 permissionMap[permName] = 121 LightPermission(packageInfo, permInfo, permState, foregroundPerms) 122 } 123 124 // Determine if this app permission group is a special location package or provider 125 var specialLocationGrant: Boolean? = null 126 val userContext = Utils.getUserContext(app, user) 127 if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) { 128 specialLocationGrant = LocationUtils.isLocationEnabled(userContext) 129 } else if ( 130 LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName) 131 ) { 132 // The permission of the extra location controller package is determined by the status 133 // of the controller package itself. 134 specialLocationGrant = 135 LocationUtils.isExtraLocationControllerPackageEnabled(userContext) 136 } 137 138 val hasInstallToRuntimeSplit = hasInstallToRuntimeSplit(packageInfo, permissionMap) 139 value = 140 LightAppPermGroup( 141 packageInfo, 142 permGroup.groupInfo, 143 permissionMap, 144 hasInstallToRuntimeSplit, 145 specialLocationGrant 146 ) 147 } 148 149 /** 150 * Check if permission group contains a runtime permission that split from an installed 151 * permission and the split happened in an Android version higher than app's targetSdk. 152 * 153 * @return `true` if there is such permission, `false` otherwise 154 */ 155 private fun hasInstallToRuntimeSplit( 156 packageInfo: LightPackageInfo, 157 permissionMap: Map<String, LightPermission> 158 ): Boolean { 159 val permissionManager = app.getSystemService(PermissionManager::class.java) ?: return false 160 161 for (spi in permissionManager.splitPermissions) { 162 val splitPerm = spi.splitPermission 163 164 val pi = 165 try { 166 app.packageManager.getPermissionInfo(splitPerm, 0) 167 } catch (e: PackageManager.NameNotFoundException) { 168 Log.w(LOG_TAG, "No such permission: $splitPerm", e) 169 continue 170 } 171 172 // Skip if split permission is not "install" permission. 173 if (pi.protection != PermissionInfo.PROTECTION_NORMAL) { 174 continue 175 } 176 177 val newPerms = spi.newPermissions 178 for (permName in newPerms) { 179 val newPerm = permissionMap[permName]?.permInfo ?: continue 180 181 // Skip if new permission is not "runtime" permission. 182 if (newPerm.protection != PermissionInfo.PROTECTION_DANGEROUS) { 183 continue 184 } 185 186 if (packageInfo.targetSdkVersion < spi.targetSdk) { 187 return true 188 } 189 } 190 } 191 return false 192 } 193 194 override fun onLocationStateChange(enabled: Boolean) { 195 update() 196 } 197 198 override fun onActive() { 199 super.onActive() 200 201 if (isSpecialLocation) { 202 LocationUtils.addLocationListener(this) 203 update() 204 } 205 } 206 207 override fun onInactive() { 208 if (isSpecialLocation) { 209 LocationUtils.removeLocationListener(this) 210 } 211 212 super.onInactive() 213 } 214 215 /** 216 * Repository for AppPermGroupLiveDatas. 217 * 218 * <p> Key value is a triple of string package name, string permission group name, and 219 * UserHandle, value is its corresponding LiveData. 220 */ 221 companion object : 222 DataRepositoryForDevice< 223 KotlinUtils.Quadruple<String, String, UserHandle, Int>, LightAppPermGroupLiveData 224 >() { 225 override fun newValue( 226 key: KotlinUtils.Quadruple<String, String, UserHandle, Int>, 227 deviceId: Int 228 ): LightAppPermGroupLiveData { 229 return LightAppPermGroupLiveData( 230 PermissionControllerApplication.get(), 231 key.first, 232 key.second, 233 key.third, 234 deviceId 235 ) 236 } 237 } 238 } 239