From 41d80b9bb0353115d9f7dd5d3c9ad672320e86b6 Mon Sep 17 00:00:00 2001 From: Evan Severson Date: Mon, 7 Dec 2020 16:53:45 +0000 Subject: [PATCH] Create a state for the camera and microhpone toggles This change adds the simple getters and setters for the state of the toggles. The persisted xml format is backwards compatible so no migration is needed. Test: None for new state Verify old sensor mute works the same Bug: 162549680 Change-Id: I6a1ef9c703cc5d175c1a5f788a3559eabf0054ad --- .../hardware/ISensorPrivacyManager.aidl | 8 ++ .../hardware/SensorPrivacyManager.java | 45 ++++++++ .../android/server/SensorPrivacyService.java | 103 +++++++++++------- 3 files changed, 118 insertions(+), 38 deletions(-) diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl index 1ba7b989a7647..4e368d0610b36 100644 --- a/core/java/android/hardware/ISensorPrivacyManager.aidl +++ b/core/java/android/hardware/ISensorPrivacyManager.aidl @@ -32,4 +32,12 @@ interface ISensorPrivacyManager { void setSensorPrivacy(boolean enable); // =============== End of transactions used on native side as well ============================ + + // TODO(evanseverson) add to native interface + boolean isIndividualSensorPrivacyEnabled(int sensor); + + // TODO(evanseverson) add to native interface + void setIndividualSensorPrivacy(int sensor, boolean enable); + + // TODO(evanseverson) listeners } \ No newline at end of file diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index 274202f7c1901..980f1406acc5c 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -16,6 +16,7 @@ package android.hardware; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; @@ -27,6 +28,9 @@ import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * This class provides access to the sensor privacy services; sensor privacy allows the * user to disable access to all sensors on the device. This class provides methods to query the @@ -38,6 +42,20 @@ import com.android.internal.annotations.GuardedBy; @SystemService(Context.SENSOR_PRIVACY_SERVICE) public final class SensorPrivacyManager { + /** Microphone */ + public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; + + /** Camera */ + public static final int INDIVIDUAL_SENSOR_CAMERA = 2; + + @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = { + INDIVIDUAL_SENSOR_MICROPHONE, + INDIVIDUAL_SENSOR_CAMERA + }) + @Retention(RetentionPolicy.SOURCE) + /** Individual sensors not listed in {@link Sensor} */ + @interface IndividualSensor {} + /** * A class implementing this interface can register with the {@link * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy @@ -168,4 +186,31 @@ public final class SensorPrivacyManager { throw e.rethrowFromSystemServer(); } } + + /** + * Returns whether sensor privacy is currently enabled for a specific sensor. + * + * @return true if sensor privacy is currently enabled, false otherwise. + */ + public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) { + try { + return mService.isIndividualSensorPrivacyEnabled(sensor); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets sensor privacy to the specified state for an individual sensor. + * + * @param enable the state to which sensor privacy should be set. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) + public void setIndividualSensorPrivacy(@IndividualSensor int sensor, boolean enable) { + try { + mService.setIndividualSensorPrivacy(sensor, enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 51e2b12bcee43..5ec3cf7cd9b01 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -30,24 +30,22 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Log; +import android.util.SparseBooleanArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.NoSuchElementException; /** @hide */ @@ -57,7 +55,9 @@ public final class SensorPrivacyService extends SystemService { private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml"; private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy"; + private static final String XML_TAG_INDIVIDUAL_SENSOR_PRIVACY = "individual-sensor-privacy"; private static final String XML_ATTRIBUTE_ENABLED = "enabled"; + private static final String XML_ATTRIBUTE_SENSOR = "sensor"; private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl; @@ -80,6 +80,7 @@ public final class SensorPrivacyService extends SystemService { private final AtomicFile mAtomicFile; @GuardedBy("mLock") private boolean mEnabled; + private SparseBooleanArray mIndividualEnabled = new SparseBooleanArray(); SensorPrivacyServiceImpl(Context context) { mContext = context; @@ -88,7 +89,7 @@ public final class SensorPrivacyService extends SystemService { SENSOR_PRIVACY_XML_FILE); mAtomicFile = new AtomicFile(sensorPrivacyFile); synchronized (mLock) { - mEnabled = readPersistedSensorPrivacyEnabledLocked(); + readPersistedSensorPrivacyStateLocked(); } } @@ -101,24 +102,19 @@ public final class SensorPrivacyService extends SystemService { enforceSensorPrivacyPermission(); synchronized (mLock) { mEnabled = enable; - FileOutputStream outputStream = null; - try { - outputStream = mAtomicFile.startWrite(); - TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); - serializer.startDocument(null, true); - serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enable); - serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.endDocument(); - mAtomicFile.finishWrite(outputStream); - } catch (IOException e) { - Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e); - mAtomicFile.failWrite(outputStream); - } + persistSensorPrivacyStateLocked(); } mHandler.onSensorPrivacyChanged(enable); } + public void setIndividualSensorPrivacy(int sensor, boolean enable) { + enforceSensorPrivacyPermission(); + synchronized (mLock) { + mIndividualEnabled.put(sensor, enable); + persistSensorPrivacyState(); + } + } + /** * Enforces the caller contains the necessary permission to change the state of sensor * privacy. @@ -143,30 +139,48 @@ public final class SensorPrivacyService extends SystemService { } } + @Override + public boolean isIndividualSensorPrivacyEnabled(int sensor) { + synchronized (mLock) { + return mIndividualEnabled.get(sensor, false); + } + } + /** * Returns the state of sensor privacy from persistent storage. */ - private boolean readPersistedSensorPrivacyEnabledLocked() { + private void readPersistedSensorPrivacyStateLocked() { // if the file does not exist then sensor privacy has not yet been enabled on // the device. if (!mAtomicFile.exists()) { - return false; + return; } - boolean enabled; try (FileInputStream inputStream = mAtomicFile.openRead()) { TypedXmlPullParser parser = Xml.resolvePullParser(inputStream); XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY); parser.next(); - String tagName = parser.getName(); - enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); + mEnabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); + + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) { + int sensor = XmlUtils.readIntAttribute(parser, XML_ATTRIBUTE_SENSOR); + boolean enabled = XmlUtils.readBooleanAttribute(parser, + XML_ATTRIBUTE_ENABLED); + mIndividualEnabled.put(sensor, enabled); + XmlUtils.skipCurrentTag(parser); + } else { + XmlUtils.nextElement(parser); + } + } + } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Caught an exception reading the state from storage: ", e); // Delete the file to prevent the same error on subsequent calls and assume sensor // privacy is not enabled. mAtomicFile.delete(); - enabled = false; } - return enabled; } /** @@ -174,20 +188,33 @@ public final class SensorPrivacyService extends SystemService { */ private void persistSensorPrivacyState() { synchronized (mLock) { - FileOutputStream outputStream = null; - try { - outputStream = mAtomicFile.startWrite(); - TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); - serializer.startDocument(null, true); - serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled); - serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.endDocument(); - mAtomicFile.finishWrite(outputStream); - } catch (IOException e) { - Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e); - mAtomicFile.failWrite(outputStream); + persistSensorPrivacyStateLocked(); + } + } + + private void persistSensorPrivacyStateLocked() { + FileOutputStream outputStream = null; + try { + outputStream = mAtomicFile.startWrite(); + TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); + serializer.startDocument(null, true); + serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled); + int numIndividual = mIndividualEnabled.size(); + for (int i = 0; i < numIndividual; i++) { + serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY); + int sensor = mIndividualEnabled.keyAt(i); + boolean enabled = mIndividualEnabled.valueAt(i); + serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR, sensor); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled); + serializer.endTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY); } + serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); + serializer.endDocument(); + mAtomicFile.finishWrite(outputStream); + } catch (IOException e) { + Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e); + mAtomicFile.failWrite(outputStream); } }