blob: dae375ad7cc7ee2674f8bdb6b455e2c2e0ddf0e7 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.sensorprivacy
import android.app.Activity
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.DialogInterface.BUTTON_NEGATIVE
import android.content.DialogInterface.BUTTON_POSITIVE
import android.content.Intent
import android.content.Intent.EXTRA_PACKAGE_NAME
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS
import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
import android.hardware.SensorPrivacyManager.Sources.DIALOG
import android.os.Bundle
import android.os.Handler
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
import com.android.internal.util.FrameworkStatsLog.write
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
/**
* Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
* currently in "sensor privacy mode", aka. muted.
*
* <p>The dialog is started for the user the app is running for which might be a secondary users.
*/
class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@Background private val bgHandler: Handler
) : Activity(), DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
private const val UNLOCK_DELAY_MILLIS = 200L
internal const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
internal const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
internal const val ALL_SENSORS = Integer.MAX_VALUE
}
private var sensor = -1
private lateinit var sensorUsePackageName: String
private var unsuppressImmediately = false
private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
private var mDialog: AlertDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setShowWhenLocked(true)
setFinishOnTouchOutside(false)
setResult(RESULT_CANCELED)
sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) {
sensor = ALL_SENSORS
sensorPrivacyListener =
IndividualSensorPrivacyController.Callback { _, _ ->
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
finish()
}
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
finish()
return
}
} else {
sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
if (it == -1) {
finish()
return
}
}
sensorPrivacyListener =
IndividualSensorPrivacyController.Callback { whichSensor: Int,
isBlocked: Boolean ->
if (whichSensor == sensor && !isBlocked) {
finish()
}
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
return
}
}
mDialog = SensorUseDialog(this, sensor, this, this)
mDialog!!.show()
}
override fun onStart() {
super.onStart()
setSuppressed(true)
unsuppressImmediately = false
}
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
keyguardDismissUtil.executeWhenUnlocked({
bgHandler.postDelayed({
disableSensorPrivacy()
write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
sensorUsePackageName)
}, UNLOCK_DELAY_MILLIS)
false
}, false, true)
} else {
disableSensorPrivacy()
write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
sensorUsePackageName)
}
}
BUTTON_NEGATIVE -> {
unsuppressImmediately = false
write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL,
sensorUsePackageName)
}
}
finish()
}
override fun onStop() {
super.onStop()
if (unsuppressImmediately) {
setSuppressed(false)
} else {
bgHandler.postDelayed({
setSuppressed(false)
}, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
}
}
override fun onDestroy() {
super.onDestroy()
mDialog?.dismiss()
sensorPrivacyController.removeCallback(sensorPrivacyListener)
}
override fun onBackPressed() {
// do not allow backing out
}
override fun onNewIntent(intent: Intent?) {
setIntent(intent)
recreate()
}
private fun disableSensorPrivacy() {
if (sensor == ALL_SENSORS) {
sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false)
sensorPrivacyController.setSensorBlocked(DIALOG, CAMERA, false)
} else {
sensorPrivacyController.setSensorBlocked(DIALOG, sensor, false)
}
unsuppressImmediately = true
setResult(RESULT_OK)
}
private fun setSuppressed(suppressed: Boolean) {
if (sensor == ALL_SENSORS) {
sensorPrivacyController
.suppressSensorPrivacyReminders(MICROPHONE, suppressed)
sensorPrivacyController
.suppressSensorPrivacyReminders(CAMERA, suppressed)
} else {
sensorPrivacyController
.suppressSensorPrivacyReminders(sensor, suppressed)
}
}
override fun onDismiss(dialog: DialogInterface?) {
finish()
}
}