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