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