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