diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index d30848f2d1d3d..85e7d77d93cd6 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -73,6 +73,13 @@ public final class SensorPrivacyManager {
public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
+ ".extra.sensor";
+ /**
+ * An extra indicating if all sensors are affected
+ * @hide
+ */
+ public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName()
+ + ".extra.all_sensors";
+
/**
* Individual sensors not listed in {@link Sensors}
* @hide
diff --git a/core/res/res/drawable/ic_camera_allowed.xml b/core/res/res/drawable/ic_camera_allowed.xml
new file mode 100644
index 0000000000000..990bc44d3ec68
--- /dev/null
+++ b/core/res/res/drawable/ic_camera_allowed.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/core/res/res/drawable/ic_camera_blocked.xml b/core/res/res/drawable/ic_camera_blocked.xml
index 0161bcbd19370..775974e4808a9 100644
--- a/core/res/res/drawable/ic_camera_blocked.xml
+++ b/core/res/res/drawable/ic_camera_blocked.xml
@@ -1,29 +1,25 @@
+
+ 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.
+-->
-
-
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+
diff --git a/core/res/res/drawable/ic_mic_allowed.xml b/core/res/res/drawable/ic_mic_allowed.xml
new file mode 100644
index 0000000000000..9dbae91cdc26b
--- /dev/null
+++ b/core/res/res/drawable/ic_mic_allowed.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/core/res/res/drawable/ic_mic_blocked.xml b/core/res/res/drawable/ic_mic_blocked.xml
index 0ce7a581a7e5d..21fc7aa8b0259 100644
--- a/core/res/res/drawable/ic_mic_blocked.xml
+++ b/core/res/res/drawable/ic_mic_blocked.xml
@@ -1,29 +1,25 @@
+
+ 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.
+-->
-
-
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 90e9c098bf292..c1ef2f42ab230 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5941,12 +5941,14 @@ ul.
Dismiss
-
- To continue, <b>%s</b> needs access to your device microphone.
+
+ Unblock device microphone
+
+ Unblock device camera
- To continue, <b>%s</b> needs access to your device’s camera.
+ For <b>%s</b> and all apps and services
- Turn on
+ Unblock
Sensor Privacy
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dbfb0307e96e4..8c8d99ec239ec 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4202,8 +4202,11 @@
-
-
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/sensor_use_started_title.xml b/packages/SystemUI/res/layout/sensor_use_started_title.xml
new file mode 100644
index 0000000000000..3e61262926db5
--- /dev/null
+++ b/packages/SystemUI/res/layout/sensor_use_started_title.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4a29578cb766f..269fdc53853bc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -832,9 +832,13 @@
Location Off
- Block Camera
+ Camera access
- Mute Microphone
+ Mic access
+
+ Available
+
+ Blocked
Media device
@@ -981,10 +985,19 @@
Stop
+
+ Unblock device microphone?
+
+ Unblock device camera?
+
+ Unblock device camera and microphone?
+
- To continue, <b>%s</b> needs access to your device microphone.
+ This unblocks access for all apps and services allowed to use your microphone.
- To continue, <b>%s</b> needs access to your device’s camera.
+ This unblocks access for all apps and services allowed to use your camera.
+
+ This unblocks access for all apps and services allowed to use your camera or microphone.
Device
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 242c6afebc3e7..2dbf30fdd2899 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -24,6 +24,7 @@ import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.screenshot.LongScreenshotActivity;
+import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity;
@@ -113,4 +114,10 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(LaunchConversationActivity.class)
public abstract Activity bindLaunchConversationActivity(LaunchConversationActivity activity);
+
+ /** Inject into SensorUseStartedActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(SensorUseStartedActivity.class)
+ public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 5b986b6fece9a..e05558598e00b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -70,8 +70,12 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
}
@Override
- public @DrawableRes int getIconRes() {
- return com.android.internal.R.drawable.ic_camera_blocked;
+ public @DrawableRes int getIconRes(boolean isBlocked) {
+ if (isBlocked) {
+ return com.android.internal.R.drawable.ic_camera_blocked;
+ } else {
+ return com.android.internal.R.drawable.ic_camera_allowed;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 42bd77b354bb7..9c01bb9cc1fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -71,8 +71,12 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
}
@Override
- public @DrawableRes int getIconRes() {
- return com.android.internal.R.drawable.ic_mic_blocked;
+ public @DrawableRes int getIconRes(boolean isBlocked) {
+ if (isBlocked) {
+ return com.android.internal.R.drawable.ic_mic_blocked;
+ } else {
+ return com.android.internal.R.drawable.ic_mic_allowed;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index a492330c1796f..5d2d6f8845a48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -26,10 +26,12 @@ import android.widget.Switch;
import androidx.annotation.DrawableRes;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -55,7 +57,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImplThe dialog is started for the user the app is running for which might be a secondary users.
*/
-class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListener {
+class SensorUseStartedActivity @Inject constructor(
+ private val sensorPrivacyController: IndividualSensorPrivacyController,
+ private val keyguardManager: KeyguardManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+) : AlertActivity(), DialogInterface.OnClickListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
+
+ private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
+ private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
+ private const val ALL_SENSORS = Integer.MAX_VALUE
}
private var sensor = -1
private lateinit var sensorUsePackageName: String
private var unsuppressImmediately = false
- private lateinit var sensorPrivacyManager: SensorPrivacyManager
- private lateinit var appOpsManager: AppOpsManager
- private lateinit var keyguardManager: KeyguardManager
+ private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -64,35 +76,81 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
setFinishOnTouchOutside(false)
setResult(RESULT_CANCELED)
- sensorPrivacyManager = getSystemService(SensorPrivacyManager::class.java)!!
- appOpsManager = getSystemService(AppOpsManager::class.java)!!
- keyguardManager = getSystemService(KeyguardManager::class.java)!!
sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
- sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
- if (it == -1) {
+
+ if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) {
+ sensor = ALL_SENSORS
+ sensorPrivacyListener =
+ IndividualSensorPrivacyController.Callback { _, _ ->
+ if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
+ !sensorPrivacyController.isSensorBlocked(CAMERA)) {
+ dismiss()
+ }
+ }
+
+ 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) {
+ dismiss()
+ }
+ }
+ sensorPrivacyController.addCallback(sensorPrivacyListener)
+
+ if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
return
}
}
- sensorPrivacyManager.addSensorPrivacyListener(sensor) { isBlocked ->
- if (!isBlocked) {
- dismiss()
- }
- }
- if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) {
- finish()
- return
- }
-
mAlertParams.apply {
try {
+ mCustomTitleView = mInflater.inflate(R.layout.sensor_use_started_title, null)
+ mCustomTitleView.findViewById(R.id.sensor_use_started_title_message)!!
+ .setText(when (sensor) {
+ MICROPHONE ->
+ R.string.sensor_privacy_start_use_mic_dialog_title
+ CAMERA ->
+ R.string.sensor_privacy_start_use_camera_dialog_title
+ ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_title
+ else -> Resources.ID_NULL
+ })
+
+ mCustomTitleView.findViewById(R.id.sensor_use_microphone_icon)!!
+ .visibility = if (sensor == MICROPHONE || sensor == ALL_SENSORS) {
+ VISIBLE
+ } else {
+ GONE
+ }
+ mCustomTitleView.findViewById(R.id.sensor_use_camera_icon)!!
+ .visibility = if (sensor == CAMERA || sensor == ALL_SENSORS) {
+ VISIBLE
+ } else {
+ GONE
+ }
+
mMessage = Html.fromHtml(getString(when (sensor) {
MICROPHONE ->
R.string.sensor_privacy_start_use_mic_dialog_content
CAMERA ->
R.string.sensor_privacy_start_use_camera_dialog_content
+ ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_content
else -> Resources.ID_NULL
}, packageManager.getApplicationInfo(sensorUsePackageName, 0)
.loadLabel(packageManager)), 0)
@@ -101,13 +159,6 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
return
}
- mIconId = when (sensor) {
- MICROPHONE ->
- com.android.internal.R.drawable.perm_group_microphone
- CAMERA -> com.android.internal.R.drawable.perm_group_camera
- else -> Resources.ID_NULL
- }
-
mPositiveButtonText = getString(
com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button)
mNegativeButtonText = getString(android.R.string.cancel)
@@ -121,14 +172,14 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
override fun onStart() {
super.onStart()
- sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true)
+ sensorPrivacyController.suppressSensorPrivacyReminders(sensorUsePackageName, true)
unsuppressImmediately = false
}
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
- if (keyguardManager.isDeviceLocked) {
+ if (keyguardUpdateMonitor.getUserHasTrust(userId)) {
keyguardManager
.requestDismissKeyguard(this, object : KeyguardDismissCallback() {
override fun onDismissError() {
@@ -152,25 +203,30 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
}
override fun onStop() {
- super.onDestroy()
+ super.onStop()
if (unsuppressImmediately) {
- sensorPrivacyManager
+ sensorPrivacyController
.suppressSensorPrivacyReminders(sensorUsePackageName, false)
} else {
Handler(mainLooper).postDelayed({
- sensorPrivacyManager
+ sensorPrivacyController
.suppressSensorPrivacyReminders(sensorUsePackageName, false)
}, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
}
}
+ override fun onDestroy() {
+ super.onDestroy()
+ sensorPrivacyController.removeCallback(sensorPrivacyListener)
+ }
+
override fun onBackPressed() {
// do not allow backing out
}
private fun disableSensorPrivacy() {
- sensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, false)
+ sensorPrivacyController.setSensorBlocked(sensor, false)
unsuppressImmediately = true
setResult(RESULT_OK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index 7f935d28285f3..4e6db40d78edb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -26,6 +26,8 @@ public interface IndividualSensorPrivacyController extends
void setSensorBlocked(@Sensor int sensor, boolean blocked);
+ void suppressSensorPrivacyReminders(String packageName, boolean suppress);
+
interface Callback {
void onSensorBlockedChanged(@Sensor int sensor, boolean blocked);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 295df05797eab..beb4b44323a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -61,6 +61,11 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked);
}
+ @Override
+ public void suppressSensorPrivacyReminders(String packageName, boolean suppress) {
+ mSensorPrivacyManager.suppressSensorPrivacyReminders(packageName, suppress);
+ }
+
@Override
public void addCallback(@NonNull Callback listener) {
mCallbacks.add(listener);
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 18d47c6fb7b3d..df6ab5da70d49 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -63,6 +63,7 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyServiceDumpProto;
import android.service.SensorPrivacyUserProto;
@@ -368,10 +369,10 @@ public final class SensorPrivacyService extends SystemService {
if (sensor == MICROPHONE) {
iconRes = R.drawable.ic_mic_blocked;
- messageRes = R.string.sensor_privacy_start_use_mic_notification_content;
+ messageRes = R.string.sensor_privacy_start_use_mic_notification_content_title;
} else {
iconRes = R.drawable.ic_camera_blocked;
- messageRes = R.string.sensor_privacy_start_use_camera_notification_content;
+ messageRes = R.string.sensor_privacy_start_use_camera_notification_content_title;
}
NotificationManager notificationManager =
@@ -390,10 +391,11 @@ public final class SensorPrivacyService extends SystemService {
Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
notificationManager.notify(sensor,
new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
- .setContentTitle(Html.fromHtml(getUiContext().getString(messageRes,
+ .setContentTitle(getUiContext().getString(messageRes))
+ .setContentText(Html.fromHtml(getUiContext().getString(
+ R.string.sensor_privacy_start_use_notification_content_text,
packageLabel),0))
.setSmallIcon(icon)
- .setLargeIcon(icon)
.addAction(new Notification.Action.Builder(icon,
getUiContext().getString(
R.string.sensor_privacy_start_use_dialog_turn_on_button),
@@ -405,6 +407,10 @@ public final class SensorPrivacyService extends SystemService {
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT))
.build())
+ .setContentIntent(PendingIntent.getActivity(mContext, sensor,
+ new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+ PendingIntent.FLAG_IMMUTABLE
+ | PendingIntent.FLAG_UPDATE_CURRENT))
.build());
}