• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.content.DialogInterface
20 import android.content.Intent
21 import android.content.Intent.EXTRA_PACKAGE_NAME
22 import android.content.pm.PackageManager
23 import android.content.res.Resources
24 import android.hardware.SensorPrivacyManager
25 import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS
26 import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
27 import android.hardware.SensorPrivacyManager.Sources.DIALOG
28 import android.os.Bundle
29 import android.os.Handler
30 import android.text.Html
31 import android.view.View.GONE
32 import android.view.View.VISIBLE
33 import android.widget.ImageView
34 import com.android.internal.app.AlertActivity
35 import com.android.internal.widget.DialogTitle
36 import com.android.systemui.R
37 import com.android.systemui.dagger.qualifiers.Background
38 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
39 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
40 import com.android.systemui.statusbar.policy.KeyguardStateController
41 import javax.inject.Inject
42 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
43 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
44 import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
45 import com.android.internal.util.FrameworkStatsLog.write
46 
47 /**
48  * Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
49  * currently in "sensor privacy mode", aka. muted.
50  *
51  * <p>The dialog is started for the user the app is running for which might be a secondary users.
52  */
53 class SensorUseStartedActivity @Inject constructor(
54     private val sensorPrivacyController: IndividualSensorPrivacyController,
55     private val keyguardStateController: KeyguardStateController,
56     private val keyguardDismissUtil: KeyguardDismissUtil,
57     @Background private val bgHandler: Handler
58 ) : AlertActivity(), DialogInterface.OnClickListener {
59 
60     companion object {
61         private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
62 
63         private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
64         private const val UNLOCK_DELAY_MILLIS = 200L
65 
66         private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
67         private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
68         private const val ALL_SENSORS = Integer.MAX_VALUE
69     }
70 
71     private var sensor = -1
72     private lateinit var sensorUsePackageName: String
73     private var unsuppressImmediately = false
74 
75     private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
76 
77     override fun onCreate(savedInstanceState: Bundle?) {
78         super.onCreate(savedInstanceState)
79 
80         setShowWhenLocked(true)
81 
82         setFinishOnTouchOutside(false)
83 
84         setResult(RESULT_CANCELED)
85 
86         sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
87 
88         if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) {
89             sensor = ALL_SENSORS
90             sensorPrivacyListener =
91                     IndividualSensorPrivacyController.Callback { _, _ ->
92                         if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
93                                 !sensorPrivacyController.isSensorBlocked(CAMERA)) {
94                             dismiss()
95                         }
96                     }
97 
98             sensorPrivacyController.addCallback(sensorPrivacyListener)
99             if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
100                     !sensorPrivacyController.isSensorBlocked(CAMERA)) {
101                 finish()
102                 return
103             }
104         } else {
105             sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
106                 if (it == -1) {
107                     finish()
108                     return
109                 }
110             }
111             sensorPrivacyListener =
112                     IndividualSensorPrivacyController.Callback {
113                         whichSensor: Int, isBlocked: Boolean ->
114                         if (whichSensor == sensor && !isBlocked) {
115                             dismiss()
116                         }
117                     }
118             sensorPrivacyController.addCallback(sensorPrivacyListener)
119 
120             sensorPrivacyController.addCallback { _, isBlocked ->
121                 if (!isBlocked) {
122                     dismiss()
123                 }
124             }
125         }
126 
127         mAlertParams.apply {
128             try {
129                 mCustomTitleView = mInflater.inflate(R.layout.sensor_use_started_title, null)
130                 mCustomTitleView.findViewById<DialogTitle>(R.id.sensor_use_started_title_message)!!
131                         .setText(when (sensor) {
132                             MICROPHONE ->
133                                 R.string.sensor_privacy_start_use_mic_dialog_title
134                             CAMERA ->
135                                 R.string.sensor_privacy_start_use_camera_dialog_title
136                             ALL_SENSORS ->
137                                 R.string.sensor_privacy_start_use_mic_camera_dialog_title
138                             else -> Resources.ID_NULL
139                         })
140 
141                 mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_microphone_icon)!!
142                         .visibility = if (sensor == MICROPHONE || sensor == ALL_SENSORS) {
143                     VISIBLE
144                 } else {
145                     GONE
146                 }
147                 mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_camera_icon)!!
148                         .visibility = if (sensor == CAMERA || sensor == ALL_SENSORS) {
149                     VISIBLE
150                 } else {
151                     GONE
152                 }
153 
154                 mMessage = Html.fromHtml(getString(when (sensor) {
155                     MICROPHONE ->
156                         R.string.sensor_privacy_start_use_mic_dialog_content
157                     CAMERA ->
158                         R.string.sensor_privacy_start_use_camera_dialog_content
159                     ALL_SENSORS ->
160                         R.string.sensor_privacy_start_use_mic_camera_dialog_content
161                     else -> Resources.ID_NULL
162                 }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
163                         .loadLabel(packageManager)), 0)
164             } catch (e: PackageManager.NameNotFoundException) {
165                 finish()
166                 return
167             }
168 
169             mPositiveButtonText = getString(
170                     com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button)
171             mNegativeButtonText = getString(android.R.string.cancel)
172             mPositiveButtonListener = this@SensorUseStartedActivity
173             mNegativeButtonListener = this@SensorUseStartedActivity
174         }
175 
176         setupAlert()
177     }
178 
179     override fun onStart() {
180         super.onStart()
181 
182         setSuppressed(true)
183         unsuppressImmediately = false
184     }
185 
186     override fun onClick(dialog: DialogInterface?, which: Int) {
187         when (which) {
188             BUTTON_POSITIVE -> {
189                 if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
190                     keyguardDismissUtil.executeWhenUnlocked({
191                         bgHandler.postDelayed({
192                             disableSensorPrivacy()
193                             write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
194                                     PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
195                                     sensorUsePackageName)
196                         }, UNLOCK_DELAY_MILLIS)
197 
198                         false
199                     }, false, true)
200                 } else {
201                     disableSensorPrivacy()
202                     write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
203                             PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
204                             sensorUsePackageName)
205                 }
206             }
207             BUTTON_NEGATIVE -> {
208                 unsuppressImmediately = false
209                 write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
210                         PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL,
211                         sensorUsePackageName)
212             }
213         }
214 
215         dismiss()
216     }
217 
218     override fun onStop() {
219         super.onStop()
220 
221         if (unsuppressImmediately) {
222             setSuppressed(false)
223         } else {
224             bgHandler.postDelayed({
225                 setSuppressed(false)
226             }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
227         }
228     }
229 
230     override fun onDestroy() {
231         super.onDestroy()
232         sensorPrivacyController.removeCallback(sensorPrivacyListener)
233     }
234 
235     override fun onBackPressed() {
236         // do not allow backing out
237     }
238 
239     override fun onNewIntent(intent: Intent?) {
240         setIntent(intent)
241         recreate()
242     }
243 
244     private fun disableSensorPrivacy() {
245         if (sensor == ALL_SENSORS) {
246             sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false)
247             sensorPrivacyController.setSensorBlocked(DIALOG, CAMERA, false)
248         } else {
249             sensorPrivacyController.setSensorBlocked(DIALOG, sensor, false)
250         }
251         unsuppressImmediately = true
252         setResult(RESULT_OK)
253     }
254 
255     private fun setSuppressed(suppressed: Boolean) {
256         if (sensor == ALL_SENSORS) {
257             sensorPrivacyController
258                     .suppressSensorPrivacyReminders(MICROPHONE, suppressed)
259             sensorPrivacyController
260                     .suppressSensorPrivacyReminders(CAMERA, suppressed)
261         } else {
262             sensorPrivacyController
263                     .suppressSensorPrivacyReminders(sensor, suppressed)
264         }
265     }
266 }