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

Commit e5c32305 authored by Evan Severson's avatar Evan Severson Committed by Android (Google) Code Review
Browse files

Merge "Refactor SensorPrivacyService to separate state from service"

parents aad20ce5 0cefec2d
Loading
Loading
Loading
Loading
+66 −5
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacySensorProto;
import android.service.SensorPrivacyToggleSourceProto;
import android.util.ArrayMap;
import android.util.Log;
@@ -75,7 +76,7 @@ public final class SensorPrivacyManager {
    private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();

    /**
     * Individual sensors not listed in {@link Sensors}
     * Sensor constants which are used in {@link SensorPrivacyManager}
     */
    public static class Sensors {

@@ -84,12 +85,12 @@ public final class SensorPrivacyManager {
        /**
         * Constant for the microphone
         */
        public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
        public static final int MICROPHONE = SensorPrivacySensorProto.MICROPHONE;

        /**
         * Constant for the camera
         */
        public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
        public static final int CAMERA = SensorPrivacySensorProto.CAMERA;

        /**
         * Individual sensors not listed in {@link Sensors}
@@ -160,6 +161,68 @@ public final class SensorPrivacyManager {

    }

    /**
     * Types of toggles which can exist for sensor privacy
     * @hide
     */
    public static class ToggleTypes {
        private ToggleTypes() {}

        /**
         * Constant for software toggle.
         */
        public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;

        /**
         * Constant for hardware toggle.
         */
        public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;

        /**
         * Types of toggles which can exist for sensor privacy
         *
         * @hide
         */
        @IntDef(value = {
                SOFTWARE,
                HARDWARE
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface ToggleType {}

    }

    /**
     * Types of state which can exist for the sensor privacy toggle
     * @hide
     */
    public static class StateTypes {
        private StateTypes() {}

        /**
         * Constant indicating privacy is enabled.
         */
        public static final int ENABLED = SensorPrivacyIndividualEnabledSensorProto.ENABLED;

        /**
         * Constant indicating privacy is disabled.
         */
        public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED;

        /**
         * Types of state which can exist for a sensor privacy toggle
         *
         * @hide
         */
        @IntDef(value = {
                ENABLED,
                DISABLED
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface StateType {}

    }

    /**
     * A class implementing this interface can register with the {@link
     * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
@@ -507,7 +570,6 @@ public final class SensorPrivacyManager {
    /**
     * Don't show dialogs to turn off sensor privacy for this package.
     *
     * @param packageName Package name not to show dialogs for
     * @param suppress Whether to suppress or re-enable.
     *
     * @hide
@@ -521,7 +583,6 @@ public final class SensorPrivacyManager {
    /**
     * Don't show dialogs to turn off sensor privacy for this package.
     *
     * @param packageName Package name not to show dialogs for
     * @param suppress Whether to suppress or re-enable.
     * @param userId the user's id
     *
+51 −1
Original line number Diff line number Diff line
@@ -22,6 +22,13 @@ option java_outer_classname = "SensorPrivacyServiceProto";

import "frameworks/base/core/proto/android/privacy.proto";

message AllSensorPrivacyServiceDumpProto {
    option (android.msg_privacy).dest = DEST_AUTOMATIC;

    // Is global sensor privacy enabled
    optional bool is_enabled = 1;
}

message SensorPrivacyServiceDumpProto {
    option (android.msg_privacy).dest = DEST_AUTOMATIC;

@@ -35,6 +42,9 @@ message SensorPrivacyServiceDumpProto {

    // Per user settings for sensor privacy
    repeated SensorPrivacyUserProto user = 3;

    // Implementation
    optional string storage_implementation = 4;
}

message SensorPrivacyUserProto {
@@ -43,16 +53,47 @@ message SensorPrivacyUserProto {
    // User id
    optional int32 user_id = 1;

    // DEPRECATED
    // Is global sensor privacy enabled
    optional bool is_enabled = 2;

    // Per sensor privacy enabled
    // DEPRECATED
    repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 3;

    // Per toggle type sensor privacy
    repeated SensorPrivacySensorProto sensors = 4;
}

message SensorPrivacySensorProto {
    option (android.msg_privacy).dest = DEST_AUTOMATIC;

    enum Sensor {
        UNKNOWN = 0;

        MICROPHONE = 1;
        CAMERA = 2;
    }

    optional int32 sensor = 1;

    repeated SensorPrivacyIndividualEnabledSensorProto toggles = 2;
}

message SensorPrivacyIndividualEnabledSensorProto {
    option (android.msg_privacy).dest = DEST_AUTOMATIC;

    enum ToggleType {
        SOFTWARE = 1;
        HARDWARE = 2;
    }

    enum StateType {
        ENABLED = 1;
        DISABLED = 2;
    }

    // DEPRECATED
    enum Sensor {
        UNKNOWN = 0;

@@ -63,8 +104,17 @@ message SensorPrivacyIndividualEnabledSensorProto {
    // Sensor for which privacy might be enabled
    optional Sensor sensor = 1;

    // If sensor privacy is enabled for this sensor
    // DEPRECATED
    optional bool is_enabled = 2;

    // Timestamp of the last time the sensor was changed
    optional int64 last_change = 3;

    // The toggle type for this state
    optional ToggleType toggle_type = 4;

    // If sensor privacy state for this sensor
    optional StateType state_type = 5;
}

message SensorPrivacyToggleSourceProto {
+0 −3
Original line number Diff line number Diff line
@@ -10,9 +10,6 @@ per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
# Userspace reboot
per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com

# Sensor Privacy
per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS

# ServiceWatcher
per-file ServiceWatcher.java = sooniln@google.com

+155 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.sensorprivacy;

import android.annotation.NonNull;
import android.os.Environment;
import android.os.Handler;
import android.util.AtomicFile;
import android.util.Log;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;

import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;

class AllSensorStateController {

    private static final String LOG_TAG = AllSensorStateController.class.getSimpleName();

    private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
    private static final String XML_TAG_SENSOR_PRIVACY = "all-sensor-privacy";
    private static final String XML_TAG_SENSOR_PRIVACY_LEGACY = "sensor-privacy";
    private static final String XML_ATTRIBUTE_ENABLED = "enabled";

    private static AllSensorStateController sInstance;

    private final AtomicFile mAtomicFile =
            new AtomicFile(new File(Environment.getDataSystemDirectory(), SENSOR_PRIVACY_XML_FILE));

    private boolean mEnabled;
    private SensorPrivacyStateController.AllSensorPrivacyListener mListener;
    private Handler mListenerHandler;

    static AllSensorStateController getInstance() {
        if (sInstance == null) {
            sInstance = new AllSensorStateController();
        }
        return sInstance;
    }

    private AllSensorStateController() {
        if (!mAtomicFile.exists()) {
            return;
        }
        try (FileInputStream inputStream = mAtomicFile.openRead()) {
            TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);

            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
                String tagName = parser.getName();
                if (XML_TAG_SENSOR_PRIVACY.equals(tagName)) {
                    mEnabled |= XmlUtils
                            .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
                    break;
                }
                if (XML_TAG_SENSOR_PRIVACY_LEGACY.equals(tagName)) {
                    mEnabled |= XmlUtils
                            .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
                }
                if ("user".equals(tagName)) { // Migrate from mic/cam toggles format
                    int user = XmlUtils.readIntAttribute(parser, "id", -1);
                    if (user == 0) {
                        mEnabled |=
                                XmlUtils.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED);
                    }
                }
                XmlUtils.nextElement(parser);
            }
        } catch (IOException | XmlPullParserException e) {
            Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
            mEnabled = false;
        }
    }

    public boolean getAllSensorStateLocked() {
        return mEnabled;
    }

    public void setAllSensorStateLocked(boolean enabled) {
        if (mEnabled != enabled) {
            mEnabled = enabled;
            if (mListener != null && mListenerHandler != null) {
                mListenerHandler.sendMessage(
                        PooledLambda.obtainMessage(mListener::onAllSensorPrivacyChanged, enabled));
            }
        }
    }

    void setAllSensorPrivacyListenerLocked(Handler handler,
            SensorPrivacyStateController.AllSensorPrivacyListener listener) {
        Objects.requireNonNull(handler);
        Objects.requireNonNull(listener);
        if (mListener != null) {
            throw new IllegalStateException("Listener is already set");
        }
        mListener = listener;
        mListenerHandler = handler;
    }

    public void schedulePersistLocked() {
        IoThread.getHandler().sendMessage(PooledLambda.obtainMessage(this::persist, mEnabled));
    }

    private void persist(boolean enabled) {
        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, enabled);
            serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
            serializer.endDocument();
            mAtomicFile.finishWrite(outputStream);
        } catch (IOException e) {
            Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
            mAtomicFile.failWrite(outputStream);
        }
    }

    void resetForTesting() {
        mListener = null;
        mListenerHandler = null;
        mEnabled = false;
    }

    void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
        // TODO stub
    }
}
+1 −0
Original line number Diff line number Diff line
include platform/frameworks/native:/libs/sensorprivacy/OWNERS
 No newline at end of file
Loading