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