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.utils.PermissionMapping 32 import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData 33 import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData 34 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 35 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData 36 import com.android.permissioncontroller.permission.data.UserPackageInfosLiveData 37 import com.android.permissioncontroller.permission.data.get 38 import com.android.permissioncontroller.permission.data.getUnusedPackages 39 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo 40 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState 41 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 42 import com.android.permissioncontroller.permission.utils.Utils 43 import kotlinx.coroutines.Dispatchers.IO 44 import kotlinx.coroutines.Dispatchers.Main 45 import kotlinx.coroutines.GlobalScope 46 import kotlinx.coroutines.async 47 import kotlinx.coroutines.launch 48 import kotlinx.coroutines.withTimeout 49 import java.util.function.IntConsumer 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 61 * be set to "started" correctly. If the liveData was inactive, create a no op observer, which 62 * will survive until the service goes inactive. Will remove the provided observer after one 63 * update (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 76 if (service.lifecycle.currentState != Lifecycle.State.STARTED) { 77 service.setLifecycleToStarted() 78 } 79 80 if (!liveData.hasActiveObservers()) { 81 observedLiveDatas.add(liveData) 82 liveData.observe(service, Observer { }) 83 } 84 85 if (forceUpdate && liveData is SmartUpdateMediatorLiveData<T>) { 86 liveData.update() 87 } 88 89 var updated = false 90 val observer = object : Observer<T> { 91 override fun onChanged(data: T) { 92 if (updated) { 93 return 94 } 95 if ((liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) || 96 liveData !is SmartUpdateMediatorLiveData<T>) { 97 onChangedFun(data) 98 liveData.removeObserver(this) 99 updated = true 100 } 101 } 102 } 103 104 liveData.observe(service, observer) 105 } 106 } 107 108 /** 109 * Stop observing all currently observed liveDatas 110 */ 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, 137 packageInfos) 138 } 139 } 140 141 /** 142 * Called upon receiving a list of packages which we want to filter by a list of permissions 143 * and flags. Observes the AppPermGroupUiInfoLiveData for every app, and, upon receiving a 144 * non-stale update, adds it to the count if it matches the permission list and flags. Will 145 * only use the first non-stale update, so if an app is updated after this update, but before 146 * execution is complete, the changes will not be reflected until the method is called again. 147 * 148 * @param permissionNames The list of permission names whose apps we want to count 149 * @param flags Flags specifying if we want to count system apps, and count only granted apps 150 * @param callback The callback our result will be returned to 151 * @param packageInfos The list of LightPackageInfos we want to filter and count 152 */ 153 private fun onPackagesLoadedForCountPermissionApps( 154 permissionNames: List<String>, 155 flags: Int, 156 callback: IntConsumer, 157 packageInfos: List<LightPackageInfo>? 158 ) { 159 if (packageInfos == null) { 160 callback.accept(0) 161 return 162 } 163 164 val countSystem = flags and COUNT_WHEN_SYSTEM != 0 165 val countOnlyGranted = flags and COUNT_ONLY_WHEN_GRANTED != 0 166 167 // Store the group of all installed, runtime permissions in permissionNames 168 val permToGroup = mutableMapOf<String, String?>() 169 for (permName in permissionNames) { 170 val permInfo = 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(AppPermGroupUiInfoLiveData[packageName, 188 permToGroup[permName]!!, Process.myUserHandle()]) 189 } 190 } 191 if (packageUiLiveDatas.isNotEmpty()) { 192 uiLiveDatasPerPackage.add(packageUiLiveDatas) 193 numLiveDatas += packageUiLiveDatas.size 194 } 195 } 196 197 if (numLiveDatas == 0) { 198 callback.accept(0) 199 } 200 201 var packagesWithPermission = 0 202 var numPermAppsChecked = 0 203 204 for (packageUiInfoLiveDatas in uiLiveDatasPerPackage) { 205 var packageAdded = false 206 // We don't need to check for new packages in between the updates of the ui info live 207 // datas, because this method is used primarily for UI, and there is inherent delay 208 // when calling this method, due to binder calls, so some staleness is acceptable 209 for (packageUiInfoLiveData in packageUiInfoLiveDatas) { 210 observeAndCheckForLifecycleState(packageUiInfoLiveData) { uiInfo -> 211 numPermAppsChecked++ 212 213 if (uiInfo != null && uiInfo.shouldShow && (!uiInfo.isSystem || countSystem)) { 214 val granted = uiInfo.permGrantState != PermGrantState.PERMS_DENIED && 215 uiInfo.permGrantState != PermGrantState.PERMS_ASK 216 if (granted || !countOnlyGranted && !packageAdded) { 217 // The permission might not be granted, but some permissions of the 218 // group are granted. In this case the permission is granted silently 219 // when the app asks for it. 220 // Hence this is as-good-as-granted and we count it. 221 packageAdded = true 222 packagesWithPermission++ 223 } 224 } 225 226 if (numPermAppsChecked == numLiveDatas) { 227 callback.accept(packagesWithPermission) 228 } 229 } 230 } 231 } 232 } 233 234 /** 235 * Gets a list of the runtime permission groups which a package requests, and the UI information 236 * about those groups. Will only use the first non-stale data for each group, so if an app is 237 * updated after this update, but before execution is complete, the changes will not be 238 * reflected until the method is called again. 239 * 240 * @param packageName The package whose permission information we want 241 * @param callback The callback which will accept the list of <group name, group UI info> pairs 242 */ 243 fun onGetAppPermissions( 244 packageName: String, 245 callback: Consumer<List<Pair<String, AppPermGroupUiInfo>>> 246 ) { 247 val packageGroupsLiveData = PackagePermissionsLiveData[packageName, 248 Process.myUserHandle()] 249 observeAndCheckForLifecycleState(packageGroupsLiveData) { groups -> 250 val groupNames = groups?.keys?.toMutableList() ?: mutableListOf() 251 groupNames.remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS) 252 val uiInfos = mutableListOf<Pair<String, AppPermGroupUiInfo>>() 253 if (groupNames.isEmpty()) { 254 callback.accept(uiInfos) 255 } 256 var numLiveDatasUpdated = 0 257 258 for (groupName in groupNames) { 259 // We don't need to check for new packages in between the updates of the ui info 260 // live datas, because this method is used primarily for UI, and there is inherent 261 // delay when calling this method, due to binder calls, so some staleness is 262 // acceptable 263 val uiInfoLiveData = AppPermGroupUiInfoLiveData[packageName, groupName, 264 Process.myUserHandle()] 265 observeAndCheckForLifecycleState(uiInfoLiveData, forceUpdate = true) { uiInfo -> 266 numLiveDatasUpdated++ 267 268 uiInfo?.let { 269 if (uiInfo.shouldShow) { 270 uiInfos.add(groupName to uiInfo) 271 } 272 } 273 274 if (numLiveDatasUpdated == groupNames.size) { 275 callback.accept(uiInfos) 276 } 277 } 278 } 279 } 280 } 281 282 /** 283 * Counts the number of unused, hibernating apps. This data is gathered from a series of 284 * LiveData objects. 285 * 286 * @param callback The callback our result will be returned to 287 */ 288 fun onCountUnusedApps( 289 callback: IntConsumer 290 ) { 291 val unusedAppsCount = Transformations.map(getUnusedPackages()) { 292 it?.size ?: 0 293 } 294 observeAndCheckForLifecycleState(unusedAppsCount) { count -> callback.accept(count ?: 0) } 295 } 296 297 /** 298 * Gets whether the package is eligible for hibernation. The logic is the same logic used by 299 * the app hibernation job when determining which apps to hibernate. 300 * 301 * @param packageName The package to check eligibility for 302 * @param callback The callback the result will be returned to 303 */ 304 fun onGetHibernationEligibility( 305 packageName: String, 306 callback: IntConsumer 307 ) { 308 val user = Process.myUserHandle() 309 val hibernationSettingLiveData = HibernationSettingStateLiveData[packageName, user] 310 observeAndCheckForLifecycleState(hibernationSettingLiveData) { hibernationSettingState -> 311 callback.accept( 312 hibernationSettingState?.hibernationEligibility ?: HIBERNATION_ELIGIBILITY_UNKNOWN) 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() 327 .addAllLogs(dumpedLogs.await()) 328 .build() 329 } 330 } 331 } 332