Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 41d80b9b authored by Evan Severson's avatar Evan Severson Committed by Philip P. Moltmann
Browse files

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
parent 1599b648
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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
+45 −0
Original line number Diff line number Diff line
@@ -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();
        }
    }
}
+65 −38
Original line number Diff line number Diff line
@@ -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();
                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;
        }

        /**
@@ -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();
@@ -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);
@@ -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.