Loading core/java/android/hardware/ISensorPrivacyManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -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 core/java/android/hardware/SensorPrivacyManager.java +45 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.hardware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; Loading @@ -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 Loading @@ -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 Loading Loading @@ -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(); } } } services/core/java/com/android/server/SensorPrivacyService.java +65 −38 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; Loading @@ -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; Loading @@ -88,7 +89,7 @@ public final class SensorPrivacyService extends SystemService { SENSOR_PRIVACY_XML_FILE); mAtomicFile = new AtomicFile(sensorPrivacyFile); synchronized (mLock) { mEnabled = readPersistedSensorPrivacyEnabledLocked(); readPersistedSensorPrivacyStateLocked(); } } Loading @@ -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. Loading @@ -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(); mEnabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); XmlUtils.nextElement(parser); while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); 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; } /** Loading @@ -174,6 +188,11 @@ public final class SensorPrivacyService extends SystemService { */ private void persistSensorPrivacyState() { synchronized (mLock) { persistSensorPrivacyStateLocked(); } } private void persistSensorPrivacyStateLocked() { FileOutputStream outputStream = null; try { outputStream = mAtomicFile.startWrite(); Loading @@ -181,6 +200,15 @@ public final class SensorPrivacyService extends SystemService { 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); Loading @@ -189,7 +217,6 @@ public final class SensorPrivacyService extends SystemService { mAtomicFile.failWrite(outputStream); } } } /** * Registers a listener to be notified when the sensor privacy state changes. Loading Loading
core/java/android/hardware/ISensorPrivacyManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -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
core/java/android/hardware/SensorPrivacyManager.java +45 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.hardware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; Loading @@ -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 Loading @@ -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 Loading Loading @@ -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(); } } }
services/core/java/com/android/server/SensorPrivacyService.java +65 −38 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; Loading @@ -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; Loading @@ -88,7 +89,7 @@ public final class SensorPrivacyService extends SystemService { SENSOR_PRIVACY_XML_FILE); mAtomicFile = new AtomicFile(sensorPrivacyFile); synchronized (mLock) { mEnabled = readPersistedSensorPrivacyEnabledLocked(); readPersistedSensorPrivacyStateLocked(); } } Loading @@ -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. Loading @@ -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(); mEnabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); XmlUtils.nextElement(parser); while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); 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; } /** Loading @@ -174,6 +188,11 @@ public final class SensorPrivacyService extends SystemService { */ private void persistSensorPrivacyState() { synchronized (mLock) { persistSensorPrivacyStateLocked(); } } private void persistSensorPrivacyStateLocked() { FileOutputStream outputStream = null; try { outputStream = mAtomicFile.startWrite(); Loading @@ -181,6 +200,15 @@ public final class SensorPrivacyService extends SystemService { 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); Loading @@ -189,7 +217,6 @@ public final class SensorPrivacyService extends SystemService { mAtomicFile.failWrite(outputStream); } } } /** * Registers a listener to be notified when the sensor privacy state changes. Loading