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