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); } }