• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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