1 /* 2 * Copyright (C) 2021 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.systemui.sensorprivacy 18 19 import android.app.Activity 20 import android.app.AlertDialog 21 import android.content.DialogInterface 22 import android.content.DialogInterface.BUTTON_NEGATIVE 23 import android.content.DialogInterface.BUTTON_POSITIVE 24 import android.content.Intent 25 import android.content.Intent.EXTRA_PACKAGE_NAME 26 import android.hardware.SensorPrivacyManager 27 import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS 28 import android.hardware.SensorPrivacyManager.EXTRA_SENSOR 29 import android.hardware.SensorPrivacyManager.Sources.DIALOG 30 import android.os.Bundle 31 import android.os.Handler 32 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION 33 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL 34 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE 35 import com.android.internal.util.FrameworkStatsLog.write 36 import com.android.systemui.dagger.qualifiers.Background 37 import com.android.systemui.statusbar.phone.KeyguardDismissUtil 38 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController 39 import com.android.systemui.statusbar.policy.KeyguardStateController 40 import javax.inject.Inject 41 42 /** 43 * Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is 44 * currently in "sensor privacy mode", aka. muted. 45 * 46 * <p>The dialog is started for the user the app is running for which might be a secondary users. 47 */ 48 class SensorUseStartedActivity @Inject constructor( 49 private val sensorPrivacyController: IndividualSensorPrivacyController, 50 private val keyguardStateController: KeyguardStateController, 51 private val keyguardDismissUtil: KeyguardDismissUtil, 52 @Background private val bgHandler: Handler 53 ) : Activity(), DialogInterface.OnClickListener, DialogInterface.OnDismissListener { 54 55 companion object { 56 private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName 57 58 private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L 59 private const val UNLOCK_DELAY_MILLIS = 200L 60 61 internal const val CAMERA = SensorPrivacyManager.Sensors.CAMERA 62 internal const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE 63 internal const val ALL_SENSORS = Integer.MAX_VALUE 64 } 65 66 private var sensor = -1 67 private lateinit var sensorUsePackageName: String 68 private var unsuppressImmediately = false 69 70 private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback 71 72 private var mDialog: AlertDialog? = null 73 74 override fun onCreate(savedInstanceState: Bundle?) { 75 super.onCreate(savedInstanceState) 76 77 setShowWhenLocked(true) 78 79 setFinishOnTouchOutside(false) 80 81 setResult(RESULT_CANCELED) 82 83 sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return 84 85 if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) { 86 sensor = ALL_SENSORS 87 sensorPrivacyListener = 88 IndividualSensorPrivacyController.Callback { _, _ -> 89 if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) && 90 !sensorPrivacyController.isSensorBlocked(CAMERA)) { 91 finish() 92 } 93 } 94 95 sensorPrivacyController.addCallback(sensorPrivacyListener) 96 if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) && 97 !sensorPrivacyController.isSensorBlocked(CAMERA)) { 98 finish() 99 return 100 } 101 } else { 102 sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also { 103 if (it == -1) { 104 finish() 105 return 106 } 107 } 108 sensorPrivacyListener = 109 IndividualSensorPrivacyController.Callback { whichSensor: Int, 110 isBlocked: Boolean -> 111 if (whichSensor == sensor && !isBlocked) { 112 finish() 113 } 114 } 115 sensorPrivacyController.addCallback(sensorPrivacyListener) 116 117 if (!sensorPrivacyController.isSensorBlocked(sensor)) { 118 finish() 119 return 120 } 121 } 122 123 mDialog = SensorUseDialog(this, sensor, this, this) 124 mDialog!!.show() 125 } 126 127 override fun onStart() { 128 super.onStart() 129 130 setSuppressed(true) 131 unsuppressImmediately = false 132 } 133 134 override fun onClick(dialog: DialogInterface?, which: Int) { 135 when (which) { 136 BUTTON_POSITIVE -> { 137 if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) { 138 keyguardDismissUtil.executeWhenUnlocked({ 139 bgHandler.postDelayed({ 140 disableSensorPrivacy() 141 write(PRIVACY_TOGGLE_DIALOG_INTERACTION, 142 PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE, 143 sensorUsePackageName) 144 }, UNLOCK_DELAY_MILLIS) 145 146 false 147 }, false, true) 148 } else { 149 disableSensorPrivacy() 150 write(PRIVACY_TOGGLE_DIALOG_INTERACTION, 151 PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE, 152 sensorUsePackageName) 153 } 154 } 155 BUTTON_NEGATIVE -> { 156 unsuppressImmediately = false 157 write(PRIVACY_TOGGLE_DIALOG_INTERACTION, 158 PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL, 159 sensorUsePackageName) 160 } 161 } 162 163 finish() 164 } 165 166 override fun onStop() { 167 super.onStop() 168 169 if (unsuppressImmediately) { 170 setSuppressed(false) 171 } else { 172 bgHandler.postDelayed({ 173 setSuppressed(false) 174 }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) 175 } 176 } 177 178 override fun onDestroy() { 179 super.onDestroy() 180 mDialog?.dismiss() 181 sensorPrivacyController.removeCallback(sensorPrivacyListener) 182 } 183 184 override fun onBackPressed() { 185 // do not allow backing out 186 } 187 188 override fun onNewIntent(intent: Intent?) { 189 setIntent(intent) 190 recreate() 191 } 192 193 private fun disableSensorPrivacy() { 194 if (sensor == ALL_SENSORS) { 195 sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false) 196 sensorPrivacyController.setSensorBlocked(DIALOG, CAMERA, false) 197 } else { 198 sensorPrivacyController.setSensorBlocked(DIALOG, sensor, false) 199 } 200 unsuppressImmediately = true 201 setResult(RESULT_OK) 202 } 203 204 private fun setSuppressed(suppressed: Boolean) { 205 if (sensor == ALL_SENSORS) { 206 sensorPrivacyController 207 .suppressSensorPrivacyReminders(MICROPHONE, suppressed) 208 sensorPrivacyController 209 .suppressSensorPrivacyReminders(CAMERA, suppressed) 210 } else { 211 sensorPrivacyController 212 .suppressSensorPrivacyReminders(sensor, suppressed) 213 } 214 } 215 216 override fun onDismiss(dialog: DialogInterface?) { 217 finish() 218 } 219 } 220