1 /*
2  * Copyright (C) 2022 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 @file:Suppress("DEPRECATION")
17 
18 package com.android.permissioncontroller.safetycenter.ui.model
19 
20 import android.Manifest.permission_group.CAMERA
21 import android.Manifest.permission_group.LOCATION
22 import android.Manifest.permission_group.MICROPHONE
23 import android.app.Application
24 import android.content.ComponentName
25 import android.content.Context
26 import android.content.Intent
27 import android.content.pm.PackageManager
28 import android.content.pm.ResolveInfo
29 import android.hardware.SensorPrivacyManager
30 import android.hardware.SensorPrivacyManager.Sensors
31 import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE
32 import android.location.LocationManager
33 import android.os.Build
34 import android.os.Process
35 import android.os.UserHandle
36 import android.os.UserManager
37 import android.permission.PermissionGroupUsage
38 import android.provider.DeviceConfig
39 import android.provider.Settings
40 import androidx.annotation.RequiresApi
41 import androidx.fragment.app.Fragment
42 import androidx.lifecycle.AndroidViewModel
43 import androidx.lifecycle.ViewModel
44 import androidx.lifecycle.ViewModelProvider
45 import com.android.permissioncontroller.R
46 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
47 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
48 import com.android.permissioncontroller.permission.data.get
49 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
50 import com.android.permissioncontroller.permission.utils.KotlinUtils
51 import com.android.permissioncontroller.permission.utils.LocationUtils
52 import com.android.settingslib.RestrictedLockUtils
53 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
54 import kotlin.collections.set
55 
56 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
57 class SafetyCenterQsViewModel(
58     private val app: Application,
59     private val sessionId: Long,
60     private val permGroupUsages: List<PermissionGroupUsage>
61 ) : AndroidViewModel(app) {
62     private val configMicToggleEnabled = app.getString(R.string.mic_toggle_enable_config)
63     private val configCameraToggleEnabled = app.getString(R.string.camera_toggle_enable_config)
64 
65     private val sensorPrivacyManager: SensorPrivacyManager =
66         app.getSystemService(SensorPrivacyManager::class.java)!!
67     private val locationManager: LocationManager =
68         app.getSystemService(LocationManager::class.java)!!
69     private val userManager: UserManager = app.getSystemService(UserManager::class.java)!!
70 
71     val lightAppPermMap = mutableMapOf<LightAppPermissionGroupUsageKey, LightAppPermGroup?>()
72     val revokedUsages = mutableSetOf<PermissionGroupUsage>()
73 
74     val permDataLoadedLiveData =
75         object : SmartUpdateMediatorLiveData<Boolean>() {
76 
77             private val lightAppPermLiveDatas =
78                 mutableMapOf<LightAppPermissionGroupUsageKey, LightAppPermGroupLiveData>()
79 
<lambda>null80             init {
81                 for (permGroupUsage in permGroupUsages) {
82                     val packageName = permGroupUsage.packageName
83                     val permissionGroupName = permGroupUsage.permissionGroupName
84                     val userHandle = UserHandle.getUserHandleForUid(permGroupUsage.uid)
85                     val lightAppPermissionGroupUsageKey =
86                         LightAppPermissionGroupUsageKey(
87                             packageName,
88                             permissionGroupName,
89                             userHandle
90                         )
91                     val appPermGroupLiveData: LightAppPermGroupLiveData =
92                         LightAppPermGroupLiveData[
93                             Triple(packageName, permissionGroupName, userHandle)]
94                     lightAppPermLiveDatas[lightAppPermissionGroupUsageKey] = appPermGroupLiveData
95                     addSource(appPermGroupLiveData) { update() }
96                 }
97             }
98 
onUpdatenull99             override fun onUpdate() {
100                 if (!lightAppPermLiveDatas.all { it.value.isInitialized }) {
101                     return
102                 }
103                 for ((lightAppPermissionGroupUsageKey, lightAppPermLiveData) in
104                     lightAppPermLiveDatas) {
105                     lightAppPermMap[lightAppPermissionGroupUsageKey] = lightAppPermLiveData.value
106                 }
107                 value = true
108             }
109         }
110 
shouldAllowRevokenull111     fun shouldAllowRevoke(usage: PermissionGroupUsage): Boolean {
112         val group =
113             lightAppPermMap[
114                 LightAppPermissionGroupUsageKey(
115                     usage.packageName,
116                     usage.permissionGroupName,
117                     UserHandle.getUserHandleForUid(usage.uid)
118                 )]
119                 ?: return false
120         return group.supportsRuntimePerms &&
121             !group.hasInstallToRuntimeSplit &&
122             !group.isBackgroundFixed &&
123             !group.isForegroundFixed &&
124             !group.isGrantedByDefault
125     }
126 
revokePermissionnull127     fun revokePermission(usage: PermissionGroupUsage) {
128         val group =
129             lightAppPermMap[
130                 LightAppPermissionGroupUsageKey(
131                     usage.packageName,
132                     usage.permissionGroupName,
133                     UserHandle.getUserHandleForUid(usage.uid)
134                 )]
135                 ?: return
136 
137         KotlinUtils.revokeForegroundRuntimePermissions(app, group)
138         KotlinUtils.revokeBackgroundRuntimePermissions(app, group)
139 
140         revokedUsages.add(usage)
141     }
142 
toggleSensornull143     fun toggleSensor(groupName: String) {
144         when (groupName) {
145             MICROPHONE -> {
146                 val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.MICROPHONE)
147                 sensorPrivacyManager.setSensorPrivacy(Sensors.MICROPHONE, !blocked)
148                 sensorPrivacyLiveData.update()
149             }
150             CAMERA -> {
151                 val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.CAMERA)
152                 sensorPrivacyManager.setSensorPrivacy(Sensors.CAMERA, !blocked)
153                 sensorPrivacyLiveData.update()
154             }
155             LOCATION -> {
156                 val enabled = locationManager.isLocationEnabledForUser(Process.myUserHandle())
157                 locationManager.setLocationEnabledForUser(!enabled, Process.myUserHandle())
158                 sensorPrivacyLiveData.update()
159             }
160         }
161     }
162 
navigateToSecuritySettingsnull163     fun navigateToSecuritySettings(fragment: Fragment) {
164         fragment.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
165     }
166 
167     data class SensorState(val visible: Boolean, val enabled: Boolean, val admin: EnforcedAdmin?)
168 
169     val sensorPrivacyLiveData: SmartUpdateMediatorLiveData<Map<String, SensorState>> =
170         object :
171             SmartUpdateMediatorLiveData<Map<String, SensorState>>(),
172             SensorPrivacyManager.OnSensorPrivacyChangedListener,
173             LocationUtils.LocationListener {
onUpdatenull174             override fun onUpdate() {
175                 val locationEnabled =
176                     locationManager.isLocationEnabledForUser(Process.myUserHandle())
177                 val locationEnforcedAdmin =
178                     getEnforcedAdmin(UserManager.DISALLOW_SHARE_LOCATION)
179                         ?: getEnforcedAdmin(UserManager.DISALLOW_CONFIG_LOCATION)
180                 value =
181                     mapOf(
182                         CAMERA to
183                             getSensorState(
184                                 Sensors.CAMERA,
185                                 UserManager.DISALLOW_CAMERA_TOGGLE,
186                                 configCameraToggleEnabled
187                             ),
188                         MICROPHONE to
189                             getSensorState(
190                                 Sensors.MICROPHONE,
191                                 UserManager.DISALLOW_MICROPHONE_TOGGLE,
192                                 configMicToggleEnabled
193                             ),
194                         LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin)
195                     )
196             }
197 
198             @Suppress("OVERRIDE_DEPRECATION")
onSensorPrivacyChangednull199             override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) {
200                 update()
201             }
202 
onLocationStateChangenull203             override fun onLocationStateChange(enabled: Boolean) {
204                 update()
205             }
206 
onActivenull207             override fun onActive() {
208                 super.onActive()
209                 sensorPrivacyManager.addSensorPrivacyListener(Sensors.CAMERA, this)
210                 sensorPrivacyManager.addSensorPrivacyListener(Sensors.MICROPHONE, this)
211                 LocationUtils.addLocationListener(this)
212                 update()
213             }
214 
onInactivenull215             override fun onInactive() {
216                 super.onInactive()
217                 sensorPrivacyManager.removeSensorPrivacyListener(Sensors.CAMERA, this)
218                 sensorPrivacyManager.removeSensorPrivacyListener(Sensors.MICROPHONE, this)
219                 LocationUtils.removeLocationListener(this)
220             }
221         }
222 
getSensorStatenull223     private fun getSensorState(
224         sensor: Int,
225         restriction: String,
226         enableConfig: String
227     ): SensorState {
228         val sensorConfigEnabled =
229             DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, enableConfig, true)
230         return SensorState(
231             sensorConfigEnabled && sensorPrivacyManager.supportsSensorToggle(sensor),
232             !sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor),
233             getEnforcedAdmin(restriction)
234         )
235     }
236 
getEnforcedAdminnull237     private fun getEnforcedAdmin(restriction: String) =
238         if (
239             userManager.getUserRestrictionSources(restriction, Process.myUserHandle()).isNotEmpty()
240         ) {
241             RestrictedLockUtils.getProfileOrDeviceOwner(app, Process.myUserHandle())
242         } else {
243             null
244         }
245 
navigateToManageServicenull246     fun navigateToManageService(fragment: Fragment, navigationIntent: Intent) {
247         fragment.startActivity(navigationIntent)
248     }
249 
navigateToManageAppPermissionsnull250     fun navigateToManageAppPermissions(fragment: Fragment, usage: PermissionGroupUsage) {
251         fragment.startActivity(getDefaultManageAppPermissionsIntent(usage.packageName, usage.uid))
252     }
253 
getStartViewPermissionUsageIntentnull254     fun getStartViewPermissionUsageIntent(context: Context, usage: PermissionGroupUsage): Intent? {
255         if (
256             !context
257                 .getSystemService(LocationManager::class.java)!!
258                 .isProviderPackage(usage.packageName)
259         ) {
260             // We should only limit this intent to location provider
261             return null
262         }
263 
264         var intent: Intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
265         intent.setPackage(usage.packageName)
266         intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, usage.permissionGroupName)
267         intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(usage.attributionTag.toString()))
268         intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
269         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
270         val resolveInfo: ResolveInfo? =
271             context.packageManager.resolveActivity(intent, PackageManager.ResolveInfoFlags.of(0))
272         if (
273             resolveInfo != null &&
274                 resolveInfo.activityInfo != null &&
275                 resolveInfo.activityInfo.permission ==
276                     android.Manifest.permission.START_VIEW_PERMISSION_USAGE
277         ) {
278             intent.component = ComponentName(usage.packageName, resolveInfo.activityInfo.name)
279             return intent
280         }
281         return null
282     }
283 
getDefaultManageAppPermissionsIntentnull284     private fun getDefaultManageAppPermissionsIntent(packageName: String, uid: Int): Intent {
285         val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
286         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
287         intent.putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(uid))
288         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
289         return intent
290     }
291 
navigateToSeeUsagenull292     fun navigateToSeeUsage(fragment: Fragment, permGroupName: String) {
293         val seeUsageIntent = Intent(Intent.ACTION_REVIEW_PERMISSION_HISTORY)
294         seeUsageIntent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName)
295         fragment.startActivity(seeUsageIntent)
296     }
297 
298     data class LightAppPermissionGroupUsageKey(
299         val packageName: String,
300         val permissionGroupName: String,
301         val userHandle: UserHandle
302     )
303 }
304 
305 /**
306  * Factory for a SafetyCenterQsViewModel
307  *
308  * @param app The current application
309  * @param sessionId A session ID used in logs to identify this particular session
310  */
311 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
312 class SafetyCenterQsViewModelFactory(
313     private val app: Application,
314     private val sessionId: Long,
315     private val permGroupUsages: List<PermissionGroupUsage>
316 ) : ViewModelProvider.Factory {
createnull317     override fun <T : ViewModel> create(modelClass: Class<T>): T {
318         @Suppress("UNCHECKED_CAST")
319         return SafetyCenterQsViewModel(app, sessionId, permGroupUsages) as T
320     }
321 }
322