Expose SensorPrivacy as SystemApi

Rename the apis to be more consistent.
Also expose to test api.
Have the aosp mark that it supports the toggle features by default.

Test: atest CtsSensorPrivacyTestCases
Bug: 162549680

Change-Id: Ib38c16adfc5cec931347e1a1891618983d78248b
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d79c11d..fc8c0fe 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -176,6 +176,7 @@
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
     field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
+    field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -2545,10 +2546,12 @@
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+    field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
     field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
     field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
     field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
+    field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -2903,6 +2906,22 @@
     method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
   }
 
+  public final class SensorPrivacyManager {
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+  }
+
+  public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+    method public void onSensorPrivacyChanged(boolean);
+  }
+
+  public static class SensorPrivacyManager.Sensors {
+    field public static final int CAMERA = 2; // 0x2
+    field public static final int MICROPHONE = 1; // 0x1
+  }
+
 }
 
 package android.hardware.biometrics {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d7b43c0..ad60aca 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -696,8 +696,10 @@
     method public void holdLock(android.os.IBinder, int);
     method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+    field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
     field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
+    field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
     field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -858,11 +860,21 @@
 package android.hardware {
 
   public final class SensorPrivacyManager {
-    method public boolean isIndividualSensorPrivacyEnabled(int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean);
-    field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2
-    field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean);
+  }
+
+  public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+    method public void onSensorPrivacyChanged(boolean);
+  }
+
+  public static class SensorPrivacyManager.Sensors {
+    field public static final int CAMERA = 2; // 0x2
+    field public static final int MICROPHONE = 1; // 0x1
   }
 
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f9122b1..668a7cf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3623,11 +3623,26 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
-     * camera. When sensory privacy for the camera is enabled no camera data is send to clients,
+     * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to
+     * clients, e.g. all audio data is silent.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
+     * camera. When sensory privacy for the camera is enabled no camera data is sent to clients,
      * e.g. the view finder in a camera app would appear blank.
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
 
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index f4f9e17..e03c1f4 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -33,6 +34,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * This class provides access to the sensor privacy services; sensor privacy allows the
@@ -42,9 +44,21 @@
  *
  * @hide
  */
+@SystemApi
 @TestApi
 @SystemService(Context.SENSOR_PRIVACY_SERVICE)
 public final class SensorPrivacyManager {
+
+    /**
+     * @hide
+     */
+    public static final boolean USE_MICROPHONE_TOGGLE = true;
+
+    /**
+     * @hide
+     */
+    public static final boolean USE_CAMERA_TOGGLE = true;
+
     /**
      * Unique Id of this manager to identify to the service
      * @hide
@@ -58,28 +72,39 @@
     public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
             + ".extra.sensor";
 
-    /** Microphone
-     * @hide */
-    @TestApi
-    public static final int INDIVIDUAL_SENSOR_MICROPHONE =
-            SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
-
-    /** Camera
-     * @hide */
-    @TestApi
-    public static final int INDIVIDUAL_SENSOR_CAMERA =
-            SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-
     /**
-     * Individual sensors not listed in {@link Sensor}
+     * Individual sensors not listed in {@link Sensors}
      * @hide
      */
-    @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = {
-            INDIVIDUAL_SENSOR_MICROPHONE,
-            INDIVIDUAL_SENSOR_CAMERA
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IndividualSensor {}
+    @SystemApi
+    @TestApi
+    public static class Sensors {
+
+        private Sensors() {}
+
+        /** Microphone
+         * @hide */
+        @SystemApi
+        @TestApi
+        public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+        /** Camera
+         * @hide */
+        @SystemApi
+        @TestApi
+        public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+
+        /**
+         * Individual sensors not listed in {@link Sensors}
+         *
+         * @hide
+         */
+        @IntDef(value = {
+                MICROPHONE,
+                CAMERA
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Sensor {}
+    }
 
     /**
      * A class implementing this interface can register with the {@link
@@ -88,6 +113,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
     public interface OnSensorPrivacyChangedListener {
         /**
          * Callback invoked when the sensor privacy state changes.
@@ -165,7 +192,8 @@
      *
      * @hide
      */
-    public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) {
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener == null) {
@@ -196,15 +224,37 @@
      *
      * @hide
      */
-    public void addSensorPrivacyListener(@IndividualSensor int sensor,
-            final OnSensorPrivacyChangedListener listener) {
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     *
+     * @param sensor the sensor to listen to changes to
+     * @param executor the executor to dispatch the callback on
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+     *                 privacy changes.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener == null) {
                 iListener = new ISensorPrivacyListener.Stub() {
                     @Override
                     public void onSensorPrivacyChanged(boolean enabled) {
-                        listener.onSensorPrivacyChanged(enabled);
+                        executor.execute(() -> listener.onSensorPrivacyChanged(enabled));
                     }
                 };
                 mListeners.put(listener, iListener);
@@ -228,7 +278,10 @@
      *
      * @hide
      */
-    public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) {
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener != null) {
@@ -249,6 +302,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled() {
         try {
             return mService.isSensorPrivacyEnabled();
@@ -264,8 +318,10 @@
      *
      * @hide
      */
+    @SystemApi
     @TestApi
-    public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
         try {
             return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
         } catch (RemoteException e) {
@@ -283,8 +339,7 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
-    public void setIndividualSensorPrivacy(@IndividualSensor int sensor,
-            boolean enable) {
+    public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
         try {
             mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable);
         } catch (RemoteException e) {
@@ -303,7 +358,7 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
-    public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor,
+    public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
             boolean enable) {
         try {
             mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor,
@@ -321,7 +376,8 @@
      *
      * @hide
      */
-    public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName,
+    @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+    public void suppressSensorPrivacyReminders(@NonNull String packageName,
             boolean suppress) {
         try {
             mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName,
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 8982519..55af060 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,7 @@
 import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
 import android.os.Build;
 import android.os.CarrierAssociatedAppEntry;
 import android.os.Environment;
@@ -1252,6 +1253,14 @@
             addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0);
         }
 
+        if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
+            addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
+        }
+
+        if (SensorPrivacyManager.USE_CAMERA_TOGGLE) {
+            addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0);
+        }
+
         for (String featureName : mUnavailableFeatures) {
             removeFeature(featureName);
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5dd8580..43d9651 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5421,6 +5421,12 @@
          @hide -->
     <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
                 android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows sensor privacy changes to be observed.
+         @hide -->
+    <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY"
+                android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES}
          intent.
          @hide -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2594840..53fbb00 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -398,6 +398,7 @@
 
     <!-- Permission required for CTS to test sensor privacy behavior -->
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+    <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
 
     <!-- Permission needed for CTS test - CallLogTest -->
     <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index fbe58c5..044216e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
     <uses-permission android:name="android.permission.MASTER_CLEAR" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+    <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
 
     <!-- ActivityManager -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9686c91f..79f0688 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.appops;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
 
 import android.Manifest;
@@ -137,8 +137,8 @@
         mAudioManager = audioManager;
         mSensorPrivacyController = sensorPrivacyController;
         mMicMuted = audioManager.isMicrophoneMute()
-                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
-        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+                || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
         mLocationManager = context.getSystemService(LocationManager.class);
         mPackageManager = context.getPackageManager();
         dumpManager.registerDumpable(TAG, this);
@@ -159,8 +159,8 @@
             mSensorPrivacyController.addCallback(this);
 
             mMicMuted = mAudioManager.isMicrophoneMute()
-                    || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
-            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+                    || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
 
             mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
                     mAudioManager.getActiveRecordingConfigurations()));
@@ -583,16 +583,16 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         mMicMuted = mAudioManager.isMicrophoneMute()
-                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+                || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
         updateSensorDisabledStatus();
     }
 
     @Override
     public void onSensorBlockedChanged(int sensor, boolean blocked) {
         mBGHandler.post(() -> {
-            if (sensor == INDIVIDUAL_SENSOR_CAMERA) {
+            if (sensor == CAMERA) {
                 mCameraDisabled = blocked;
-            } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) {
+            } else if (sensor == MICROPHONE) {
                 mMicMuted = mAudioManager.isMicrophoneMute() || blocked;
             }
             updateSensorDisabledStatus();
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 3841dac..70287cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -58,8 +61,8 @@
 
     @Override
     public boolean isAvailable() {
-        return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
-                && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
+                && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 "camera_toggle_enabled",
                 false));
     }
@@ -75,7 +78,7 @@
     }
 
     @Override
-    public int getSensorId() {
+    public @Sensor int getSensorId() {
         return CAMERA;
     }
 }
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 2f0071a..e9b712d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -58,7 +61,9 @@
 
     @Override
     public boolean isAvailable() {
-        return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        return getHost().getContext().getPackageManager()
+                .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE)
+                && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 "mic_toggle_enabled",
                 false));
     }
@@ -74,7 +79,7 @@
     }
 
     @Override
-    public int getSensorId() {
+    public @Sensor int getSensorId() {
         return MICROPHONE;
     }
 }
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 00703e7..0c582bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tiles;
 
 import android.content.Intent;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.Tile;
@@ -49,7 +49,7 @@
     /**
      * @return Id of the sensor that will be toggled
      */
-    public abstract @IndividualSensor int getSensorId();
+    public abstract @Sensor int getSensorId();
 
     /**
      * @return icon for the QS tile
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 9f182e1..6586137 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -25,8 +25,8 @@
 import android.content.res.Resources
 import android.hardware.SensorPrivacyManager
 import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
 import android.os.Bundle
 import android.os.Handler
 import android.text.Html
@@ -81,7 +81,7 @@
                 dismiss()
             }
         }
-        if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) {
+        if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) {
             finish()
             return
         }
@@ -89,9 +89,9 @@
         mAlertParams.apply {
             try {
                 mMessage = Html.fromHtml(getString(when (sensor) {
-                    INDIVIDUAL_SENSOR_MICROPHONE ->
+                    MICROPHONE ->
                         R.string.sensor_privacy_start_use_mic_dialog_content
-                    INDIVIDUAL_SENSOR_CAMERA ->
+                    CAMERA ->
                         R.string.sensor_privacy_start_use_camera_dialog_content
                     else -> Resources.ID_NULL
                 }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
@@ -102,9 +102,9 @@
             }
 
             mIconId = when (sensor) {
-                INDIVIDUAL_SENSOR_MICROPHONE ->
+                MICROPHONE ->
                     com.android.internal.R.drawable.perm_group_microphone
-                INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera
+                CAMERA -> com.android.internal.R.drawable.perm_group_camera
                 else -> Resources.ID_NULL
             }
 
@@ -121,7 +121,7 @@
     override fun onStart() {
         super.onStart()
 
-        sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true)
+        sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true)
         unsuppressImmediately = false
     }
 
@@ -156,11 +156,11 @@
 
         if (unsuppressImmediately) {
             sensorPrivacyManager
-                    .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+                    .suppressSensorPrivacyReminders(sensorUsePackageName, false)
         } else {
             Handler(mainLooper).postDelayed({
                 sensorPrivacyManager
-                        .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+                        .suppressSensorPrivacyReminders(sensorUsePackageName, false)
             }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
         }
     }
@@ -170,7 +170,7 @@
     }
 
     private fun disableSensorPrivacy() {
-        sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+        sensorPrivacyManager.setSensorPrivacyForProfileGroup(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 a76d08a..7f935d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 
 public interface IndividualSensorPrivacyController extends
         CallbackController<IndividualSensorPrivacyController.Callback> {
     void init();
 
-    boolean isSensorBlocked(@IndividualSensor int sensor);
+    boolean isSensorBlocked(@Sensor int sensor);
 
-    void setSensorBlocked(@IndividualSensor int sensor, boolean blocked);
+    void setSensorBlocked(@Sensor int sensor, boolean blocked);
 
     interface Callback {
-        void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked);
+        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 32d15ed..295df05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import android.hardware.SensorPrivacyManager;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.util.ArraySet;
 import android.util.SparseBooleanArray;
 
@@ -30,8 +30,7 @@
 
 public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
 
-    private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA,
-            INDIVIDUAL_SENSOR_MICROPHONE};
+    private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
 
     private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
     private final SparseBooleanArray mState = new SparseBooleanArray();
@@ -48,18 +47,18 @@
             mSensorPrivacyManager.addSensorPrivacyListener(sensor,
                     (enabled) -> onSensorPrivacyChanged(sensor, enabled));
 
-            mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor));
+            mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor));
         }
     }
 
     @Override
-    public boolean isSensorBlocked(@IndividualSensor int sensor) {
+    public boolean isSensorBlocked(@Sensor int sensor) {
         return mState.get(sensor, false);
     }
 
     @Override
-    public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) {
-        mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked);
+    public void setSensorBlocked(@Sensor int sensor, boolean blocked) {
+        mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked);
     }
 
     @Override
@@ -72,7 +71,7 @@
         mCallbacks.remove(listener);
     }
 
-    private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) {
+    private void onSensorPrivacyChanged(@Sensor int sensor, boolean blocked) {
         mState.put(sensor, blocked);
 
         for (Callback callback : mCallbacks) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 97cb873..2526990 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.appops;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import static junit.framework.TestCase.assertFalse;
 
@@ -125,9 +125,9 @@
         when(mAudioManager.getActiveRecordingConfigurations())
                 .thenReturn(List.of(mPausedMockRecording));
 
-        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+        when(mSensorPrivacyController.isSensorBlocked(CAMERA))
                 .thenReturn(false);
-        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+        when(mSensorPrivacyController.isSensorBlocked(CAMERA))
                 .thenReturn(false);
 
         mController = new AppOpsControllerImpl(
@@ -505,7 +505,7 @@
         assertFalse(list.get(0).isDisabled());
 
         // Add a camera op, and disable the microphone. The camera op should be the only op returned
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true);
+        mController.onSensorBlockedChanged(MICROPHONE, true);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -515,7 +515,7 @@
 
 
         // Re enable the microphone, and verify the op returns
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false);
+        mController.onSensorBlockedChanged(MICROPHONE, false);
         mTestableLooper.processAllMessages();
 
         list = mController.getActiveAppOps();
@@ -538,7 +538,7 @@
         assertFalse(list.get(0).isDisabled());
 
         // Add an audio op, and disable the camera. The audio op should be the only op returned
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true);
+        mController.onSensorBlockedChanged(CAMERA, true);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -547,7 +547,7 @@
         assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
 
         // Re enable the camera, and verify the op returns
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false);
+        mController.onSensorBlockedChanged(CAMERA, false);
         mTestableLooper.processAllMessages();
 
         list = mController.getActiveAppOps();
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 84e429d..f2782f6 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -25,9 +25,9 @@
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
 import android.annotation.NonNull;
@@ -406,12 +406,12 @@
          */
         @Override
         public void setSensorPrivacy(boolean enable) {
+            enforceManageSensorPrivacyPermission();
             // Keep the state consistent between all users to make it a single global state
             forAllUsers(userId -> setSensorPrivacy(userId, enable));
         }
 
         private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
-            enforceSensorPrivacyPermission();
             synchronized (mLock) {
                 mEnabled.put(userId, enable);
                 persistSensorPrivacyStateLocked();
@@ -421,7 +421,7 @@
 
         @Override
         public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
-            enforceSensorPrivacyPermission();
+            enforceManageSensorPrivacyPermission();
             synchronized (mLock) {
                 SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId,
                         new SparseBooleanArray());
@@ -448,6 +448,7 @@
         @Override
         public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor,
                 boolean enable) {
+            enforceManageSensorPrivacyPermission();
             int parentId = mUserManagerInternal.getProfileParentId(userId);
             forAllUsers(userId2 -> {
                 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -460,21 +461,35 @@
          * Enforces the caller contains the necessary permission to change the state of sensor
          * privacy.
          */
-        private void enforceSensorPrivacyPermission() {
-            if (mContext.checkCallingOrSelfPermission(
-                    MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
-                return;
-            }
-            throw new SecurityException(
+        private void enforceManageSensorPrivacyPermission() {
+            enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY,
                     "Changing sensor privacy requires the following permission: "
                             + MANAGE_SENSOR_PRIVACY);
         }
 
         /**
+         * Enforces the caller contains the necessary permission to observe changes to the sate of
+         * sensor privacy.
+         */
+        private void enforceObserveSensorPrivacyPermission() {
+            enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY,
+                    "Observing sensor privacy changes requires the following permission: "
+                            + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY);
+        }
+
+        private void enforcePermission(String permission, String message) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return;
+            }
+            throw new SecurityException(message);
+        }
+
+        /**
          * Returns whether sensor privacy is enabled.
          */
         @Override
         public boolean isSensorPrivacyEnabled() {
+            enforceObserveSensorPrivacyPermission();
             return isSensorPrivacyEnabled(USER_SYSTEM);
         }
 
@@ -486,6 +501,7 @@
 
         @Override
         public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+            enforceObserveSensorPrivacyPermission();
             synchronized (mLock) {
                 SparseBooleanArray states = mIndividualEnabled.get(userId);
                 if (states == null) {
@@ -703,6 +719,7 @@
          */
         @Override
         public void addSensorPrivacyListener(ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -715,6 +732,7 @@
         @Override
         public void addIndividualSensorPrivacyListener(int userId, int sensor,
                 ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -726,6 +744,7 @@
          */
         @Override
         public void removeSensorPrivacyListener(ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -735,6 +754,7 @@
         @Override
         public void suppressIndividualSensorPrivacyReminders(int userId, String packageName,
                 IBinder token, boolean suppress) {
+            enforceManageSensorPrivacyPermission();
             Objects.requireNonNull(packageName);
             Objects.requireNonNull(token);
 
@@ -886,13 +906,13 @@
         }
 
         /**
-         * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}.
+         * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}.
          *
          * @param sensor The name to convert
          *
          * @return The id corresponding to the name
          */
-        private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) {
+        private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) {
             if (sensor == null) {
                 return UNKNOWN;
             }
@@ -950,7 +970,7 @@
                                 return -1;
                             }
 
-                            enforceSensorPrivacyPermission();
+                            enforceManageSensorPrivacyPermission();
 
                             synchronized (mLock) {
                                 SparseBooleanArray individualEnabled =
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 81c1e45..dde45c4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -295,7 +295,7 @@
                 };
             SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
             manager.addSensorPrivacyListener(
-                    SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener);
+                    SensorPrivacyManager.Sensors.MICROPHONE, listener);
         }
     }
 
@@ -1079,8 +1079,8 @@
      */
     private void sendMicrophoneDisableSettingUpdate() {
         SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
-        boolean disabled = manager.isIndividualSensorPrivacyEnabled(
-                SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE);
+        boolean disabled = manager.isSensorPrivacyEnabled(
+                SensorPrivacyManager.Sensors.MICROPHONE);
         Log.d(TAG, "Mic Disabled Setting: " + disabled);
         mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
     }