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.pm.PackageManager
21 import android.content.pm.PermissionInfo
22 import android.os.Build
23 import android.os.UserHandle
24 import android.permission.PermissionManager
25 import android.util.Log
26 import com.android.permissioncontroller.PermissionControllerApplication
27 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
28 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
29 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
30 import com.android.permissioncontroller.permission.utils.KotlinUtils
31 import com.android.permissioncontroller.permission.utils.LocationUtils
32 import com.android.permissioncontroller.permission.utils.Utils
33 import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
34 
35 /**
36  * A LiveData which represents the permissions for one package and permission group.
37  *
38  * @param app The current application
39  * @param packageName The name of the package
40  * @param permGroupName The name of the permission group
41  * @param user The user of the package
42  */
43 class LightAppPermGroupLiveData
44 private constructor(
45     private val app: Application,
46     private val packageName: String,
47     private val permGroupName: String,
48     private val user: UserHandle,
49     private val deviceId: Int
50 ) : SmartUpdateMediatorLiveData<LightAppPermGroup?>(), LocationUtils.LocationListener {
51 
52     private val LOG_TAG = this::class.java.simpleName
53 
54     private var isSpecialLocation = false
55     private val permStateLiveData = PermStateLiveData[packageName, permGroupName, user, deviceId]
56     private val permGroupLiveData = PermGroupLiveData[permGroupName]
57     private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user, deviceId]
58     private val fgPermNamesLiveData = ForegroundPermNamesLiveData
59 
60     init {
61         isSpecialLocation =
62             LocationUtils.isLocationGroupAndProvider(app, permGroupName, packageName) ||
63                 LocationUtils.isLocationGroupAndControllerExtraPackage(
64                     app,
65                     permGroupName,
66                     packageName
67                 )
68 
69         addSource(fgPermNamesLiveData) { update() }
70 
71         val key = KotlinUtils.Quadruple(packageName, permGroupName, user, deviceId)
72 
73         addSource(permStateLiveData) { permStates ->
74             if (permStates == null && permStateLiveData.isInitialized) {
75                 invalidateSingle(key)
76                 value = null
77             } else {
78                 update()
79             }
80         }
81 
82         addSource(permGroupLiveData) { permGroup ->
83             if (permGroup == null && permGroupLiveData.isInitialized) {
84                 invalidateSingle(key)
85                 value = null
86             } else {
87                 update()
88             }
89         }
90 
91         addSource(packageInfoLiveData) { packageInfo ->
92             if (packageInfo == null && packageInfoLiveData.isInitialized) {
93                 invalidateSingle(key)
94                 value = null
95             } else {
96                 update()
97             }
98         }
99     }
100 
101     override fun onUpdate() {
102         val permStates = permStateLiveData.value ?: return
103         val permGroup = permGroupLiveData.value ?: return
104         val packageInfo = packageInfoLiveData.value ?: return
105         val allForegroundPerms = fgPermNamesLiveData.value ?: return
106 
107         // Do not allow toggling pre-M custom perm groups
108         if (
109             packageInfo.targetSdkVersion < Build.VERSION_CODES.M &&
110                 permGroup.groupInfo.packageName != OS_PKG
111         ) {
112             value = LightAppPermGroup(packageInfo, permGroup.groupInfo, emptyMap())
113             return
114         }
115 
116         val permissionMap = mutableMapOf<String, LightPermission>()
117         for ((permName, permState) in permStates) {
118             val permInfo = permGroup.permissionInfos[permName] ?: continue
119             val foregroundPerms = allForegroundPerms[permName]
120             permissionMap[permName] =
121                 LightPermission(packageInfo, permInfo, permState, foregroundPerms)
122         }
123 
124         // Determine if this app permission group is a special location package or provider
125         var specialLocationGrant: Boolean? = null
126         val userContext = Utils.getUserContext(app, user)
127         if (LocationUtils.isLocationGroupAndProvider(userContext, permGroupName, packageName)) {
128             specialLocationGrant = LocationUtils.isLocationEnabled(userContext)
129         } else if (
130             LocationUtils.isLocationGroupAndControllerExtraPackage(app, permGroupName, packageName)
131         ) {
132             // The permission of the extra location controller package is determined by the status
133             // of the controller package itself.
134             specialLocationGrant =
135                 LocationUtils.isExtraLocationControllerPackageEnabled(userContext)
136         }
137 
138         val hasInstallToRuntimeSplit = hasInstallToRuntimeSplit(packageInfo, permissionMap)
139         value =
140             LightAppPermGroup(
141                 packageInfo,
142                 permGroup.groupInfo,
143                 permissionMap,
144                 hasInstallToRuntimeSplit,
145                 specialLocationGrant
146             )
147     }
148 
149     /**
150      * Check if permission group contains a runtime permission that split from an installed
151      * permission and the split happened in an Android version higher than app's targetSdk.
152      *
153      * @return `true` if there is such permission, `false` otherwise
154      */
155     private fun hasInstallToRuntimeSplit(
156         packageInfo: LightPackageInfo,
157         permissionMap: Map<String, LightPermission>
158     ): Boolean {
159         val permissionManager = app.getSystemService(PermissionManager::class.java) ?: return false
160 
161         for (spi in permissionManager.splitPermissions) {
162             val splitPerm = spi.splitPermission
163 
164             val pi =
165                 try {
166                     app.packageManager.getPermissionInfo(splitPerm, 0)
167                 } catch (e: PackageManager.NameNotFoundException) {
168                     Log.w(LOG_TAG, "No such permission: $splitPerm", e)
169                     continue
170                 }
171 
172             // Skip if split permission is not "install" permission.
173             if (pi.protection != PermissionInfo.PROTECTION_NORMAL) {
174                 continue
175             }
176 
177             val newPerms = spi.newPermissions
178             for (permName in newPerms) {
179                 val newPerm = permissionMap[permName]?.permInfo ?: continue
180 
181                 // Skip if new permission is not "runtime" permission.
182                 if (newPerm.protection != PermissionInfo.PROTECTION_DANGEROUS) {
183                     continue
184                 }
185 
186                 if (packageInfo.targetSdkVersion < spi.targetSdk) {
187                     return true
188                 }
189             }
190         }
191         return false
192     }
193 
194     override fun onLocationStateChange(enabled: Boolean) {
195         update()
196     }
197 
198     override fun onActive() {
199         super.onActive()
200 
201         if (isSpecialLocation) {
202             LocationUtils.addLocationListener(this)
203             update()
204         }
205     }
206 
207     override fun onInactive() {
208         if (isSpecialLocation) {
209             LocationUtils.removeLocationListener(this)
210         }
211 
212         super.onInactive()
213     }
214 
215     /**
216      * Repository for AppPermGroupLiveDatas.
217      *
218      * <p> Key value is a triple of string package name, string permission group name, and
219      * UserHandle, value is its corresponding LiveData.
220      */
221     companion object :
222         DataRepositoryForDevice<
223             KotlinUtils.Quadruple<String, String, UserHandle, Int>, LightAppPermGroupLiveData
224         >() {
225         override fun newValue(
226             key: KotlinUtils.Quadruple<String, String, UserHandle, Int>,
227             deviceId: Int
228         ): LightAppPermGroupLiveData {
229             return LightAppPermGroupLiveData(
230                 PermissionControllerApplication.get(),
231                 key.first,
232                 key.second,
233                 key.third,
234                 deviceId
235             )
236         }
237     }
238 }
239