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