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.service 18 19 import android.content.pm.PackageManager 20 import android.os.Process 21 import android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED 22 import android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM 23 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_UNKNOWN 24 import androidx.core.util.Consumer 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.LiveData 27 import androidx.lifecycle.Observer 28 import androidx.lifecycle.map 29 import com.android.permissioncontroller.DumpableLog 30 import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto 31 import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData 32 import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData 33 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 34 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData 35 import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData 36 import com.android.permissioncontroller.permission.data.get 37 import com.android.permissioncontroller.permission.data.getUnusedPackages 38 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo 39 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState 40 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 41 import com.android.permissioncontroller.permission.utils.PermissionMapping 42 import com.android.permissioncontroller.permission.utils.Utils 43 import java.util.function.IntConsumer 44 import kotlinx.coroutines.Dispatchers.IO 45 import kotlinx.coroutines.Dispatchers.Main 46 import kotlinx.coroutines.GlobalScope 47 import kotlinx.coroutines.async 48 import kotlinx.coroutines.launch 49 import kotlinx.coroutines.withTimeout 50 51 /** 52 * A model for the PermissionControllerServiceImpl. Handles the data gathering for some methods of 53 * ServiceImpl, and supports retrieving data from LiveDatas. 54 */ 55 class PermissionControllerServiceModel(private val service: PermissionControllerServiceImpl) { 56 57 private val observedLiveDatas = mutableListOf<LiveData<*>>() 58 59 /** 60 * *Must* be used instead of LiveData.observe, in order to allow the lifecycle state to be set 61 * to "started" correctly. If the liveData was inactive, create a no op observer, which will 62 * survive until the service goes inactive. Will remove the provided observer after one update 63 * (one non-stale update, in the case of a SmartUpdateMediatorLiveData). 64 * 65 * @param liveData The livedata we wish to observe 66 * @param onChangedFun The function we wish to be called upon livedata updates 67 * @param <T> The type of the livedata and observer 68 */ 69 fun <T> observeAndCheckForLifecycleState( 70 liveData: LiveData<T>, 71 forceUpdate: Boolean = false, 72 onChangedFun: (t: T?) -> Unit 73 ) { 74 GlobalScope.launch(Main.immediate) { 75 if (service.lifecycle.currentState != Lifecycle.State.STARTED) { 76 service.setLifecycleToStarted() 77 } 78 79 if (!liveData.hasActiveObservers()) { 80 observedLiveDatas.add(liveData) 81 liveData.observe(service, Observer {}) 82 } 83 84 if (forceUpdate && liveData is SmartUpdateMediatorLiveData<T>) { 85 liveData.update() 86 } 87 88 var updated = false 89 val observer = 90 object : Observer<T> { 91 override fun onChanged(value: T) { 92 if (updated) { 93 return 94 } 95 if ( 96 (liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) || 97 liveData !is SmartUpdateMediatorLiveData<T> 98 ) { 99 onChangedFun(value) 100 liveData.removeObserver(this) 101 updated = true 102 } 103 } 104 } 105 106 liveData.observe(service, observer) 107 } 108 } 109 110 /** Stop observing all currently observed liveDatas */ 111 fun removeObservers() { 112 GlobalScope.launch(Main.immediate) { 113 for (liveData in observedLiveDatas) { 114 liveData.removeObservers(service) 115 } 116 117 observedLiveDatas.clear() 118 } 119 } 120 121 /** 122 * Counts the number of apps that have at least one of a provided list of permissions, subject 123 * to the options specified in flags. This data is gathered from a series of LiveData objects. 124 * 125 * @param permissionNames The list of permission names whose apps we want to count 126 * @param flags Flags specifying if we want to count system apps, and count only granted apps 127 * @param callback The callback our result will be returned to 128 */ 129 fun onCountPermissionAppsLiveData( 130 permissionNames: List<String>, 131 flags: Int, 132 callback: IntConsumer 133 ) { 134 val packageInfosLiveData = UserPackageInfosLiveData[Process.myUserHandle()] 135 observeAndCheckForLifecycleState(packageInfosLiveData) { packageInfos -> 136 onPackagesLoadedForCountPermissionApps(permissionNames, flags, callback, packageInfos) 137 } 138 } 139 140 /** 141 * Called upon receiving a list of packages which we want to filter by a list of permissions and 142 * flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a non-stale 143 * update, adds it to the count if it matches the permission list and flags. Will only use the 144 * first non-stale update, so if an app is updated after this update, but before execution is 145 * complete, the changes will not be reflected until the method is called again. 146 * 147 * @param permissionNames The list of permission names whose apps we want to count 148 * @param flags Flags specifying if we want to count system apps, and count only granted apps 149 * @param callback The callback our result will be returned to 150 * @param packageInfos The list of LightPackageInfos we want to filter and count 151 */ 152 private fun onPackagesLoadedForCountPermissionApps( 153 permissionNames: List<String>, 154 flags: Int, 155 callback: IntConsumer, 156 packageInfos: List<LightPackageInfo>? 157 ) { 158 if (packageInfos == null) { 159 callback.accept(0) 160 return 161 } 162 163 val countSystem = flags and COUNT_WHEN_SYSTEM != 0 164 val countOnlyGranted = flags and COUNT_ONLY_WHEN_GRANTED != 0 165 166 // Store the group of all installed, runtime permissions in permissionNames 167 val permToGroup = mutableMapOf<String, String?>() 168 for (permName in permissionNames) { 169 val permInfo = 170 try { 171 service.packageManager.getPermissionInfo(permName, 0) 172 } catch (e: PackageManager.NameNotFoundException) { 173 continue 174 } 175 176 if (Utils.isPermissionDangerousInstalledNotRemoved(permInfo)) { 177 permToGroup[permName] = PermissionMapping.getGroupOfPermission(permInfo) 178 } 179 } 180 181 val uiLiveDatasPerPackage = mutableListOf<MutableSet<AppPermGroupUiInfoLiveData>>() 182 var numLiveDatas = 0 183 for ((packageName, _, requestedPermissions) in packageInfos) { 184 val packageUiLiveDatas = mutableSetOf<AppPermGroupUiInfoLiveData>() 185 for (permName in permToGroup.keys) { 186 if (requestedPermissions.contains(permName)) { 187 packageUiLiveDatas.add( 188 AppPermGroupUiInfoLiveData[ 189 packageName, permToGroup[permName]!!, Process.myUserHandle()] 190 ) 191 } 192 } 193 if (packageUiLiveDatas.isNotEmpty()) { 194 uiLiveDatasPerPackage.add(packageUiLiveDatas) 195 numLiveDatas += packageUiLiveDatas.size 196 } 197 } 198 199 if (numLiveDatas == 0) { 200 callback.accept(0) 201 } 202 203 var packagesWithPermission = 0 204 var numPermAppsChecked = 0 205 206 for (packageUiInfoLiveDatas in uiLiveDatasPerPackage) { 207 var packageAdded = false 208 // We don't need to check for new packages in between the updates of the ui info live 209 // datas, because this method is used primarily for UI, and there is inherent delay 210 // when calling this method, due to binder calls, so some staleness is acceptable 211 for (packageUiInfoLiveData in packageUiInfoLiveDatas) { 212 observeAndCheckForLifecycleState(packageUiInfoLiveData) { uiInfo -> 213 numPermAppsChecked++ 214 215 if (uiInfo != null && uiInfo.shouldShow && (!uiInfo.isSystem || countSystem)) { 216 val granted = 217 uiInfo.permGrantState != PermGrantState.PERMS_DENIED && 218 uiInfo.permGrantState != PermGrantState.PERMS_ASK 219 if (granted || !countOnlyGranted && !packageAdded) { 220 // The permission might not be granted, but some permissions of the 221 // group are granted. In this case the permission is granted silently 222 // when the app asks for it. 223 // Hence this is as-good-as-granted and we count it. 224 packageAdded = true 225 packagesWithPermission++ 226 } 227 } 228 229 if (numPermAppsChecked == numLiveDatas) { 230 callback.accept(packagesWithPermission) 231 } 232 } 233 } 234 } 235 } 236 237 /** 238 * Gets a list of the runtime permission groups which a package requests, and the UI information 239 * about those groups. Will only use the first non-stale data for each group, so if an app is 240 * updated after this update, but before execution is complete, the changes will not be 241 * reflected until the method is called again. 242 * 243 * @param packageName The package whose permission information we want 244 * @param callback The callback which will accept the list of <group name, group UI info> pairs 245 */ 246 fun onGetAppPermissions( 247 packageName: String, 248 callback: Consumer<List<Pair<String, AppPermGroupUiInfo>>> 249 ) { 250 val packageGroupsLiveData = PackagePermissionsLiveData[packageName, Process.myUserHandle()] 251 observeAndCheckForLifecycleState(packageGroupsLiveData) { groups -> 252 val groupNames = groups?.keys?.toMutableList() ?: mutableListOf() 253 groupNames.remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) 254 val uiInfos = mutableListOf<Pair<String, AppPermGroupUiInfo>>() 255 if (groupNames.isEmpty()) { 256 callback.accept(uiInfos) 257 } 258 var numLiveDatasUpdated = 0 259 260 for (groupName in groupNames) { 261 // We don't need to check for new packages in between the updates of the ui info 262 // live datas, because this method is used primarily for UI, and there is inherent 263 // delay when calling this method, due to binder calls, so some staleness is 264 // acceptable 265 val uiInfoLiveData = 266 AppPermGroupUiInfoLiveData[packageName, groupName, Process.myUserHandle()] 267 observeAndCheckForLifecycleState(uiInfoLiveData, forceUpdate = true) { uiInfo -> 268 numLiveDatasUpdated++ 269 270 uiInfo?.let { 271 if (uiInfo.shouldShow) { 272 uiInfos.add(groupName to uiInfo) 273 } 274 } 275 276 if (numLiveDatasUpdated == groupNames.size) { 277 callback.accept(uiInfos) 278 } 279 } 280 } 281 } 282 } 283 284 /** 285 * Counts the number of unused, hibernating apps. This data is gathered from a series of 286 * LiveData objects. 287 * 288 * @param callback The callback our result will be returned to 289 */ 290 fun onCountUnusedApps(callback: IntConsumer) { 291 GlobalScope.launch(Main.immediate) { 292 val unusedAppsCount = getUnusedPackages().map { it?.size ?: 0 } 293 observeAndCheckForLifecycleState(unusedAppsCount) { count -> 294 callback.accept(count ?: 0) 295 } 296 } 297 } 298 299 /** 300 * Gets whether the package is eligible for hibernation. The logic is the same logic used by the 301 * app hibernation job when determining which apps to hibernate. 302 * 303 * @param packageName The package to check eligibility for 304 * @param callback The callback the result will be returned to 305 */ 306 fun onGetHibernationEligibility(packageName: String, callback: IntConsumer) { 307 val user = Process.myUserHandle() 308 val hibernationSettingLiveData = HibernationSettingStateLiveData[packageName, user] 309 observeAndCheckForLifecycleState(hibernationSettingLiveData) { hibernationSettingState -> 310 callback.accept( 311 hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN 312 ) 313 } 314 } 315 316 /** 317 * Dump state of the permission controller service 318 * 319 * @return the dump state as a proto 320 */ 321 suspend fun onDump(): PermissionControllerDumpProto { 322 // Timeout is less than the timeout used by dumping (10 s) 323 return withTimeout(9000) { 324 val dumpedLogs = GlobalScope.async(IO) { DumpableLog.get() } 325 326 PermissionControllerDumpProto.newBuilder().addAllLogs(dumpedLogs.await()).build() 327 } 328 } 329 } 330