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

Commit f52311f6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add dumpsys and shell cmd for sensor_privacy"

parents c6eec7e5 6a227526
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.util.ArrayMap;

import com.android.internal.annotations.GuardedBy;
@@ -43,18 +44,23 @@ import java.lang.annotation.RetentionPolicy;
public final class SensorPrivacyManager {

    /** Microphone */
    public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1;
    public static final int INDIVIDUAL_SENSOR_MICROPHONE =
            SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;

    /** Camera */
    public static final int INDIVIDUAL_SENSOR_CAMERA = 2;
    public static final int INDIVIDUAL_SENSOR_CAMERA =
            SensorPrivacyIndividualEnabledSensorProto.CAMERA;

    /**
     * Individual sensors not listed in {@link Sensor}
     * @hide
     */
    @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = {
            INDIVIDUAL_SENSOR_MICROPHONE,
            INDIVIDUAL_SENSOR_CAMERA
    })
    @Retention(RetentionPolicy.SOURCE)
    /** Individual sensors not listed in {@link Sensor} */
    @interface IndividualSensor {}
    public @interface IndividualSensor {}

    /**
     * A class implementing this interface can register with the {@link
+1 −2
Original line number Diff line number Diff line
@@ -18,11 +18,10 @@ package com.android.internal.util.dump;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.IndentingPrintWriter;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+50 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

syntax = "proto2";
package android.service;

option java_multiple_files = true;
option java_outer_classname = "SensorPrivacyServiceProto";

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

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

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

    // Per sensor privacy enabled
    repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 2;
}

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

    enum Sensor {
        UNKNOWN = 0;

        MICROPHONE = 1;
        CAMERA = 2;
    }

    // Sensor for which privacy might be enabled
    optional Sensor sensor = 1;

    // If sensor privacy is enabled for this sensor
    optional bool is_enabled = 2;
}
 No newline at end of file
+183 −0
Original line number Diff line number Diff line
@@ -17,36 +17,55 @@
package com.android.server;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
import android.hardware.SensorPrivacyManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyServiceDumpProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;

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

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.NoSuchElementException;
import java.util.Objects;

/** @hide */
public final class SensorPrivacyService extends SystemService {
@@ -239,6 +258,170 @@ public final class SensorPrivacyService extends SystemService {
            }
            mHandler.removeListener(listener);
        }

        @Override
        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            Objects.requireNonNull(fd);

            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;

            int opti = 0;
            boolean dumpAsProto = false;
            while (opti < args.length) {
                String opt = args[opti];
                if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
                    break;
                }
                opti++;
                if ("--proto".equals(opt)) {
                    dumpAsProto = true;
                } else {
                    pw.println("Unknown argument: " + opt + "; use -h for help");
                }
            }

            final long identity = Binder.clearCallingIdentity();
            try {
                if (dumpAsProto) {
                    dump(new DualDumpOutputStream(new ProtoOutputStream(fd)));
                } else {
                    pw.println("SENSOR PRIVACY MANAGER STATE (dumpsys "
                            + Context.SENSOR_PRIVACY_SERVICE + ")");

                    dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")));
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

        /**
         * Dump state to {@link DualDumpOutputStream}.
         *
         * @param dumpStream The destination to dump to
         */
        private void dump(@NonNull DualDumpOutputStream dumpStream) {
            synchronized (mLock) {
                dumpStream.write("is_enabled", SensorPrivacyServiceDumpProto.IS_ENABLED, mEnabled);

                int numIndividualEnabled = mIndividualEnabled.size();
                for (int i = 0; i < numIndividualEnabled; i++) {
                    long token = dumpStream.start("individual_enabled_sensor",
                            SensorPrivacyServiceDumpProto.INDIVIDUAL_ENABLED_SENSOR);

                    dumpStream.write("sensor",
                            SensorPrivacyIndividualEnabledSensorProto.SENSOR,
                            mIndividualEnabled.keyAt(i));
                    dumpStream.write("is_enabled",
                            SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
                            mIndividualEnabled.valueAt(i));

                    dumpStream.end(token);
                }
            }

            dumpStream.flush();
        }

        /**
         * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}.
         *
         * @param sensor The name to convert
         *
         * @return The id corresponding to the name
         */
        private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) {
            if (sensor == null) {
                return UNKNOWN;
            }

            switch (sensor) {
                case "microphone":
                    return MICROPHONE;
                case "camera":
                    return CAMERA;
                default: {
                    return UNKNOWN;
                }
            }
        }

        @Override
        public void onShellCommand(FileDescriptor in, FileDescriptor out,
                FileDescriptor err, String[] args, ShellCallback callback,
                ResultReceiver resultReceiver) {
            (new ShellCommand() {
                @Override
                public int onCommand(String cmd) {
                    if (cmd == null) {
                        return handleDefaultCommands(cmd);
                    }

                    final PrintWriter pw = getOutPrintWriter();
                    switch (cmd) {
                        case "enable" : {
                            int sensor = sensorStrToId(getNextArg());
                            if (sensor == UNKNOWN) {
                                pw.println("Invalid sensor");
                                return -1;
                            }

                            setIndividualSensorPrivacy(sensor, true);
                        }
                        break;
                        case "disable" : {
                            int sensor = sensorStrToId(getNextArg());
                            if (sensor == UNKNOWN) {
                                pw.println("Invalid sensor");
                                return -1;
                            }

                            setIndividualSensorPrivacy(sensor, false);
                        }
                        break;
                        case "reset": {
                            int sensor = sensorStrToId(getNextArg());
                            if (sensor == UNKNOWN) {
                                pw.println("Invalid sensor");
                                return -1;
                            }

                            enforceSensorPrivacyPermission();

                            synchronized (mLock) {
                                mIndividualEnabled.delete(sensor);
                                persistSensorPrivacyState();
                            }
                        }
                        break;
                        default:
                            return handleDefaultCommands(cmd);
                    }

                    return 0;
                }

                @Override
                public void onHelp() {
                    final PrintWriter pw = getOutPrintWriter();

                    pw.println("Sensor privacy manager (" + Context.SENSOR_PRIVACY_SERVICE
                            + ") commands:");
                    pw.println("  help");
                    pw.println("    Print this help text.");
                    pw.println("");
                    pw.println("  enable SENSOR");
                    pw.println("    Enable privacy for a certain sensor.");
                    pw.println("");
                    pw.println("  disable SENSOR");
                    pw.println("    Disable privacy for a certain sensor.");
                    pw.println("");
                    pw.println("  reset SENSOR");
                    pw.println("    Reset privacy state for a certain sensor.");
                    pw.println("");
                }
            }).exec(this, in, out, err, args, callback, resultReceiver);
        }
    }

    /**