• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.model.livedatatypes.LightAppPermGroup
49 import com.android.permissioncontroller.permission.utils.KotlinUtils
50 import com.android.permissioncontroller.permission.utils.LocationUtils
51 import com.android.settingslib.RestrictedLockUtils
52 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
53 import kotlin.collections.set
54 
55 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
56 class SafetyCenterQsViewModel(
57     private val app: Application,
58     private val sessionId: Long,
59     private val permGroupUsages: List<PermissionGroupUsage>
60 ) : AndroidViewModel(app) {
61     private val configMicToggleEnabled = app.getString(R.string.mic_toggle_enable_config)
62     private val configCameraToggleEnabled = app.getString(R.string.camera_toggle_enable_config)
63 
64     private val sensorPrivacyManager: SensorPrivacyManager =
65         app.getSystemService(SensorPrivacyManager::class.java)!!
66     private val locationManager: LocationManager =
67         app.getSystemService(LocationManager::class.java)!!
68     private val userManager: UserManager = app.getSystemService(UserManager::class.java)!!
69 
70     val lightAppPermMap = mutableMapOf<LightAppPermissionGroupUsageKey, LightAppPermGroup?>()
71     val revokedUsages = mutableSetOf<PermissionGroupUsage>()
72 
73     val permDataLoadedLiveData =
74         object : SmartUpdateMediatorLiveData<Boolean>() {
75 
76             private val lightAppPermLiveDatas =
77                 mutableMapOf<LightAppPermissionGroupUsageKey, LightAppPermGroupLiveData>()
78 
<lambda>null79             init {
80                 for (permGroupUsage in permGroupUsages) {
81                     val packageName = permGroupUsage.packageName
82                     val permissionGroupName = permGroupUsage.permissionGroupName
83                     val userHandle = UserHandle.getUserHandleForUid(permGroupUsage.uid)
84                     val lightAppPermissionGroupUsageKey =
85                         LightAppPermissionGroupUsageKey(
86                             packageName, permissionGroupName, userHandle)
87                     val appPermGroupLiveData: LightAppPermGroupLiveData =
88                         LightAppPermGroupLiveData[
89                             Triple(packageName, permissionGroupName, userHandle)]
90                     lightAppPermLiveDatas[lightAppPermissionGroupUsageKey] = appPermGroupLiveData
91                     addSource(appPermGroupLiveData) { update() }
92                 }
93             }
94 
onUpdatenull95             override fun onUpdate() {
96                 if (!lightAppPermLiveDatas.all { it.value.isInitialized }) {
97                     return
98                 }
99                 for ((lightAppPermissionGroupUsageKey, lightAppPermLiveData) in
100                     lightAppPermLiveDatas) {
101                     lightAppPermMap[lightAppPermissionGroupUsageKey] = lightAppPermLiveData.value
102                 }
103                 value = true
104             }
105         }
106 
shouldAllowRevokenull107     fun shouldAllowRevoke(usage: PermissionGroupUsage): Boolean {
108         val group =
109             lightAppPermMap[
110                 LightAppPermissionGroupUsageKey(
111                     usage.packageName,
112                     usage.permissionGroupName,
113                     UserHandle.getUserHandleForUid(usage.uid))]
114                 ?: return false
115         return group.supportsRuntimePerms &&
116             !group.hasInstallToRuntimeSplit &&
117             !group.isBackgroundFixed &&
118             !group.isForegroundFixed &&
119             !group.isGrantedByDefault
120     }
121 
revokePermissionnull122     fun revokePermission(usage: PermissionGroupUsage) {
123         val group =
124             lightAppPermMap[
125                 LightAppPermissionGroupUsageKey(
126                     usage.packageName,
127                     usage.permissionGroupName,
128                     UserHandle.getUserHandleForUid(usage.uid))]
129                 ?: return
130 
131         KotlinUtils.revokeForegroundRuntimePermissions(app, group)
132         KotlinUtils.revokeBackgroundRuntimePermissions(app, group)
133 
134         revokedUsages.add(usage)
135     }
136 
toggleSensornull137     fun toggleSensor(groupName: String) {
138         when (groupName) {
139             MICROPHONE -> {
140                 val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.MICROPHONE)
141                 sensorPrivacyManager.setSensorPrivacy(Sensors.MICROPHONE, !blocked)
142                 sensorPrivacyLiveData.update()
143             }
144             CAMERA -> {
145                 val blocked = sensorPrivacyManager.isSensorPrivacyEnabled(Sensors.CAMERA)
146                 sensorPrivacyManager.setSensorPrivacy(Sensors.CAMERA, !blocked)
147                 sensorPrivacyLiveData.update()
148             }
149             LOCATION -> {
150                 val enabled = locationManager.isLocationEnabledForUser(Process.myUserHandle())
151                 locationManager.setLocationEnabledForUser(!enabled, Process.myUserHandle())
152                 sensorPrivacyLiveData.update()
153             }
154         }
155     }
156 
navigateToSecuritySettingsnull157     fun navigateToSecuritySettings(fragment: Fragment) {
158         fragment.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
159     }
160 
161     data class SensorState(val visible: Boolean, val enabled: Boolean, val admin: EnforcedAdmin?)
162 
163     val sensorPrivacyLiveData: SmartUpdateMediatorLiveData<Map<String, SensorState>> =
164         object :
165             SmartUpdateMediatorLiveData<Map<String, SensorState>>(),
166             SensorPrivacyManager.OnSensorPrivacyChangedListener,
167             LocationUtils.LocationListener {
onUpdatenull168             override fun onUpdate() {
169                 val locationEnabled =
170                     locationManager.isLocationEnabledForUser(Process.myUserHandle())
171                 val locationEnforcedAdmin =
172                     getEnforcedAdmin(UserManager.DISALLOW_SHARE_LOCATION)
173                         ?: getEnforcedAdmin(UserManager.DISALLOW_CONFIG_LOCATION)
174                 value =
175                     mapOf(
176                         CAMERA to
177                             getSensorState(
178                                 Sensors.CAMERA,
179                                 UserManager.DISALLOW_CAMERA_TOGGLE,
180                                 configCameraToggleEnabled),
181                         MICROPHONE to
182                             getSensorState(
183                                 Sensors.MICROPHONE,
184                                 UserManager.DISALLOW_MICROPHONE_TOGGLE,
185                                 configMicToggleEnabled),
186                         LOCATION to SensorState(true, locationEnabled, locationEnforcedAdmin))
187             }
188 
189             @Suppress("OVERRIDE_DEPRECATION")
onSensorPrivacyChangednull190             override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) {
191                 update()
192             }
193 
onLocationStateChangenull194             override fun onLocationStateChange(enabled: Boolean) {
195                 update()
196             }
197 
onActivenull198             override fun onActive() {
199                 super.onActive()
200                 sensorPrivacyManager.addSensorPrivacyListener(Sensors.CAMERA, this)
201                 sensorPrivacyManager.addSensorPrivacyListener(Sensors.MICROPHONE, this)
202                 LocationUtils.addLocationListener(this)
203                 update()
204             }
205 
onInactivenull206             override fun onInactive() {
207                 super.onInactive()
208                 sensorPrivacyManager.removeSensorPrivacyListener(Sensors.CAMERA, this)
209                 sensorPrivacyManager.removeSensorPrivacyListener(Sensors.MICROPHONE, this)
210                 LocationUtils.removeLocationListener(this)
211             }
212         }
213 
getSensorStatenull214     private fun getSensorState(
215         sensor: Int,
216         restriction: String,
217         enableConfig: String
218     ): SensorState {
219         val sensorConfigEnabled =
220             DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, enableConfig, true)
221         return SensorState(
222             sensorConfigEnabled && sensorPrivacyManager.supportsSensorToggle(sensor),
223             !sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor),
224             getEnforcedAdmin(restriction))
225     }
226 
getEnforcedAdminnull227     private fun getEnforcedAdmin(restriction: String) =
228         if (userManager
229             .getUserRestrictionSources(restriction, Process.myUserHandle())
230             .isNotEmpty()) {
231             RestrictedLockUtils.getProfileOrDeviceOwner(app, Process.myUserHandle())
232         } else {
233             null
234         }
235 
navigateToManageServicenull236     fun navigateToManageService(fragment: Fragment, navigationIntent: Intent) {
237         fragment.startActivity(navigationIntent)
238     }
239 
navigateToManageAppPermissionsnull240     fun navigateToManageAppPermissions(fragment: Fragment, usage: PermissionGroupUsage) {
241         fragment.startActivity(getDefaultManageAppPermissionsIntent(usage.packageName, usage.uid))
242     }
243 
getStartViewPermissionUsageIntentnull244     fun getStartViewPermissionUsageIntent(context: Context, usage: PermissionGroupUsage): Intent? {
245         var intent: Intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
246         intent.setPackage(usage.packageName)
247         intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, usage.permissionGroupName)
248         intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(usage.attributionTag.toString()))
249         intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
250         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
251         val resolveInfo: ResolveInfo? =
252             context.packageManager.resolveActivity(intent, PackageManager.ResolveInfoFlags.of(0))
253         if (resolveInfo != null &&
254             resolveInfo.activityInfo != null &&
255             resolveInfo.activityInfo.permission ==
256                 android.Manifest.permission.START_VIEW_PERMISSION_USAGE) {
257             intent.component = ComponentName(usage.packageName, resolveInfo.activityInfo.name)
258             return intent
259         }
260         return null
261     }
262 
getDefaultManageAppPermissionsIntentnull263     private fun getDefaultManageAppPermissionsIntent(packageName: String, uid: Int): Intent {
264         val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
265         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
266         intent.putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(uid))
267         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
268         return intent
269     }
270 
navigateToSeeUsagenull271     fun navigateToSeeUsage(fragment: Fragment, permGroupName: String) {
272         val seeUsageIntent = Intent(Intent.ACTION_REVIEW_PERMISSION_HISTORY)
273         seeUsageIntent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName)
274         fragment.startActivity(seeUsageIntent)
275     }
276 
277     data class LightAppPermissionGroupUsageKey(
278         val packageName: String,
279         val permissionGroupName: String,
280         val userHandle: UserHandle
281     )
282 }
283 
284 /**
285  * Factory for a SafetyCenterQsViewModel
286  *
287  * @param app The current application
288  * @param sessionId A session ID used in logs to identify this particular session
289  */
290 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
291 class SafetyCenterQsViewModelFactory(
292     private val app: Application,
293     private val sessionId: Long,
294     private val permGroupUsages: List<PermissionGroupUsage>
295 ) : ViewModelProvider.Factory {
createnull296     override fun <T : ViewModel> create(modelClass: Class<T>): T {
297         @Suppress("UNCHECKED_CAST")
298         return SafetyCenterQsViewModel(app, sessionId, permGroupUsages) as T
299     }
300 }
301