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