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