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 import com.android.permissioncontroller.PermissionControllerApplication
27 import com.android.permissioncontroller.permission.model.livedatatypes.UidSensitivityState
28 import com.android.permissioncontroller.permission.utils.KotlinUtils
29 import com.android.permissioncontroller.permission.utils.PermissionMapping
30 import com.android.permissioncontroller.permission.utils.Utils
31 import java.lang.IllegalArgumentException
32 import kotlinx.coroutines.Job
33 
34 /**
35  * Live data of the user sensitivity of either one uid, or all uids that belong to a user. Maps
36  * <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
44 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) { update() }
64             addSource(LauncherPackagesLiveData) { update() }
65         } else {
66             update()
67         }
68     }
69 
70     override suspend fun loadDataAndPostValue(job: Job) {
71         val pm = context.packageManager
72         if (!getAllUids) {
73             val uidHasPackages = getAndObservePackageLiveDatas()
74 
75             if (
76                 !uidHasPackages ||
77                     packageLiveDatas.all { it.value.isInitialized && it.value.value == null }
78             ) {
79                 packageLiveDatas.clear()
80                 invalidateSingle(uid to user)
81                 postValue(null)
82                 return
83             } else if (!packageLiveDatas.all { it.value.isInitialized }) {
84                 return
85             }
86         }
87         val pkgs =
88             if (getAllUids) {
89                 userPackageInfosLiveData.value ?: return
90             } else {
91                 packageLiveDatas.mapNotNull { it.value.value }
92             }
93         if (job.isCancelled) {
94             return
95         }
96 
97         // map of <uid, userSensitiveState>
98         val sensitiveStatePerUid = mutableMapOf<Int, UidSensitivityState>()
99 
100         // TODO ntmyren: Figure out how to get custom runtime permissions in a less costly manner
101         val runtimePerms = PermissionMapping.getRuntimePlatformPermissionNames()
102 
103         for (pkg in pkgs) {
104             // sensitivityState for one uid
105             val userSensitiveState =
106                 sensitiveStatePerUid.getOrPut(pkg.uid) {
107                     UidSensitivityState(mutableSetOf(), mutableMapOf())
108                 }
109             userSensitiveState.packages.add(pkg)
110 
111             val pkgHasLauncherIcon =
112                 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 =
128                     if (pkgIsSystemApp && !pkgHasLauncherIcon) {
129                         val permGrantedByDefault =
130                             pm.getPermissionFlags(perm, pkg.packageName, user) and
131                                 PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT != 0
132 
133                         if (permGrantedByDefault) {
134                             0
135                         } else {
136                             PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
137                         }
138                     } else {
139                         Utils.FLAGS_ALWAYS_USER_SENSITIVE
140                     }
141 
142                 /*
143                  * If two packages share a UID there can be two cases:
144                  * - for well known UIDs: if the permission for any package is non-user sensitive,
145                  *                        it is non-sensitive. I.e. prefer to hide
146                  * - for non system UIDs: if the permission for any package is user sensitive, it is
147                  *                        user sensitive. I.e. prefer to show
148                  */
149                 val previousFlags = userSensitiveState.permStates[perm]
150                 if (previousFlags != null) {
151                     flags =
152                         if (pkg.uid < Process.FIRST_APPLICATION_UID) {
153                             flags and previousFlags
154                         } else {
155                             flags or previousFlags
156                         }
157                 }
158 
159                 userSensitiveState.permStates[perm] = flags
160             }
161 
162             if (job.isCancelled) {
163                 return
164             }
165         }
166         postValue(sensitiveStatePerUid)
167     }
168 
169     private fun getAndObservePackageLiveDatas(): Boolean {
170         synchronized(this) {
171             val packageNames = app.packageManager.getPackagesForUid(uid)?.toList() ?: emptyList()
172             val getLiveData = { packageName: String -> LightPackageInfoLiveData[packageName, user] }
173             setSourcesToDifference(packageNames, packageLiveDatas, getLiveData)
174             return packageNames.isNotEmpty()
175         }
176     }
177 
178     /**
179      * Repository for a UserSensitivityLiveData
180      *
181      * <p> Key value is a pair of int uid (INVALID_UID for all uids), and UserHandle, value is its
182      * corresponding LiveData.
183      */
184     companion object : DataRepository<Pair<Int, UserHandle>, UserSensitivityLiveData>() {
185         override fun newValue(key: Pair<Int, UserHandle>): UserSensitivityLiveData {
186             return UserSensitivityLiveData(
187                 PermissionControllerApplication.get(),
188                 key.first,
189                 key.second
190             )
191         }
192 
193         /**
194          * Gets a liveData for a uid, automatically generating the UserHandle from the uid. Will
195          * throw an exception if the uid is INVALID_UID.
196          *
197          * @param uid The uid for which we want the liveData
198          * @return The liveData associated with the given UID
199          */
200         operator fun get(uid: Int): UserSensitivityLiveData {
201             if (uid == INVALID_UID) {
202                 throw IllegalArgumentException("Cannot get single uid livedata without a valid uid")
203             }
204             return get(uid, UserHandle.getUserHandleForUid(uid))
205         }
206 
207         /**
208          * Gets a liveData for a user, which will track all uids under
209          *
210          * @param user The user for whom we want the liveData
211          * @return The liveData associated with that user, for all uids
212          */
213         operator fun get(user: UserHandle): UserSensitivityLiveData {
214             return get(INVALID_UID, user)
215         }
216     }
217 }
218