• 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.data
18 
19 import android.app.Application
20 import android.content.Context
21 import android.content.pm.ApplicationInfo
22 import android.content.pm.PackageManager
23 import android.os.Process
24 import android.os.Process.INVALID_UID
25 import android.os.UserHandle
26 
27 import com.android.permissioncontroller.PermissionControllerApplication
28 import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState
29 import com.android.permissioncontroller.permission.utils.KotlinUtils
30 import com.android.permissioncontroller.permission.utils.Utils
31 import kotlinx.coroutines.Job
32 import java.lang.IllegalArgumentException
33 
34 /**
35  * Live data of the user sensitivity of either one uid, or all uids that belong to a user.
36  * Maps <uid, user sensitive state>
37  *
38  * @param app The current application
39  * @param uid The uid whose user sensitivity we would like to observer, or INVALID_UID if we want
40  * all uids for a user
41  * @param user The user for whom we want the uid/s
42  */
43 class UserSensitivityLiveData private constructor(
44     private val app: Application,
45     private val uid: Int,
46     private val user: UserHandle
47 ) : SmartAsyncMediatorLiveData<Map<Int, UidSensitivityState>?>() {
48 
49     private val context: Context
50     private val packageLiveDatas = mutableMapOf<String, LightPackageInfoLiveData>()
51     private val userPackageInfosLiveData = UserPackageInfosLiveData[user]
52     private val getAllUids = uid == INVALID_UID
53 
54     init {
55         try {
56             context = Utils.getUserContext(app, user)
57         } catch (cannotHappen: PackageManager.NameNotFoundException) {
58             throw IllegalStateException(cannotHappen)
59         }
60 
61         if (getAllUids) {
62             addSource(userPackageInfosLiveData) {
63                 update()
64             }
65             addSource(LauncherPackagesLiveData) {
66                 update()
67             }
68         } else {
69             update()
70         }
71     }
72 
73     override suspend fun loadDataAndPostValue(job: Job) {
74         val pm = context.packageManager
75         if (!getAllUids) {
76             val uidHasPackages = getAndObservePackageLiveDatas()
77 
78             if (!uidHasPackages || packageLiveDatas.all {
79                     it.value.isInitialized &&
80                         it.value.value == null
81                 }) {
82                 packageLiveDatas.clear()
83                 invalidateSingle(uid to user)
84                 postValue(null)
85                 return
86             } else if (!packageLiveDatas.all { it.value.isInitialized }) {
87                 return
88             }
89         }
90         val pkgs = if (getAllUids) {
91             userPackageInfosLiveData.value ?: return
92         } else {
93             packageLiveDatas.mapNotNull { it.value.value }
94         }
95         if (job.isCancelled) {
96             return
97         }
98 
99         // map of <uid, userSensitiveState>
100         val sensitiveStatePerUid = mutableMapOf<Int, UidSensitivityState>()
101 
102         // TODO ntmyren: Figure out how to get custom runtime permissions in a less costly manner
103         val runtimePerms = Utils.getRuntimePlatformPermissionNames()
104 
105         for (pkg in pkgs) {
106             // sensitivityState for one uid
107             val userSensitiveState = sensitiveStatePerUid.getOrPut(pkg.uid) {
108                 UidSensitivityState(mutableSetOf(), mutableMapOf())
109             }
110             userSensitiveState.packages.add(pkg)
111 
112             val pkgHasLauncherIcon = if (getAllUids) {
113                 // The launcher packages set will only be null when it is uninitialized.
114                 LauncherPackagesLiveData.value?.contains(pkg.packageName) ?: return
115             } else {
116                 KotlinUtils.packageHasLaunchIntent(context, pkg.packageName)
117             }
118             val pkgIsSystemApp = pkg.appFlags and ApplicationInfo.FLAG_SYSTEM != 0
119             // Iterate through all runtime perms, setting their keys
120             for (perm in pkg.requestedPermissions.intersect(runtimePerms)) {
121                 /*
122                  * Permissions are considered user sensitive for a package, when
123                  * - the package has a launcher icon, or
124                  * - the permission is not pre-granted, or
125                  * - the package is not a system app (i.e. not preinstalled)
126                  */
127                 var flags = if (pkgIsSystemApp && !pkgHasLauncherIcon) {
128                     val permGrantedByDefault = pm.getPermissionFlags(perm, pkg.packageName,
129                         user) and PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0
130 
131                     if (permGrantedByDefault) {
132                         0
133                     } else {
134                         PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
135                     }
136                 } else {
137                     Utils.FLAGS_ALWAYS_USER_SENSITIVE
138                 }
139 
140                 /*
141                  * If two packages share a UID there can be two cases:
142                  * - for well known UIDs: if the permission for any package is non-user sensitive,
143                  *                        it is non-sensitive. I.e. prefer to hide
144                  * - for non system UIDs: if the permission for any package is user sensitive, it is
145                  *                        user sensitive. I.e. prefer to show
146                  */
147                 val previousFlags = userSensitiveState.permStates[perm]
148                 if (previousFlags != null) {
149                     flags = if (pkg.uid < Process.FIRST_APPLICATION_UID) {
150                         flags and previousFlags
151                     } else {
152                         flags or previousFlags
153                     }
154                 }
155 
156                 userSensitiveState.permStates[perm] = flags
157             }
158 
159             if (job.isCancelled) {
160                 return
161             }
162         }
163         postValue(sensitiveStatePerUid)
164     }
165 
166     private fun getAndObservePackageLiveDatas(): Boolean {
167         val packageNames = app.packageManager.getPackagesForUid(uid)?.toList() ?: emptyList()
168         val getLiveData = { packageName: String -> LightPackageInfoLiveData[packageName, user] }
169         setSourcesToDifference(packageNames, packageLiveDatas, getLiveData)
170         return packageNames.isNotEmpty()
171     }
172 
173     /**
174      * Repository for a UserSensitivityLiveData
175      * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle,
176      * value is its corresponding LiveData.
177      */
178     companion object : DataRepository<Pair<Int, UserHandle>, UserSensitivityLiveData>() {
179         override fun newValue(key: Pair<Int, UserHandle>): UserSensitivityLiveData {
180             return UserSensitivityLiveData(PermissionControllerApplication.get(), key.first,
181                 key.second)
182         }
183 
184         /**
185          * Gets a liveData for a uid, automatically generating the UserHandle from the uid. Will
186          * throw an exception if the uid is INVALID_UID.
187          *
188          * @param uid The uid for which we want the liveData
189          *
190          * @return The liveData associated with the given UID
191          */
192         operator fun get(uid: Int): UserSensitivityLiveData {
193             if (uid == INVALID_UID) {
194                 throw IllegalArgumentException("Cannot get single uid livedata without a valid uid")
195             }
196             return get(uid, UserHandle.getUserHandleForUid(uid))
197         }
198 
199         /**
200          * Gets a liveData for a user, which will track all uids under
201          *
202          * @param user The user for whom we want the liveData
203          *
204          * @return The liveData associated with that user, for all uids
205          */
206         operator fun get(user: UserHandle): UserSensitivityLiveData {
207             return get(INVALID_UID, user)
208         }
209     }
210 }
211