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