summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java14
-rw-r--r--core/java/com/android/internal/util/dump/DualDumpOutputStream.java3
-rw-r--r--core/proto/android/hardware/sensorprivacy.proto50
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java183
4 files changed, 244 insertions, 6 deletions
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 980f1406acc5..c647239d9049 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -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
diff --git a/core/java/com/android/internal/util/dump/DualDumpOutputStream.java b/core/java/com/android/internal/util/dump/DualDumpOutputStream.java
index 3ac38edaddb4..20419db80e88 100644
--- a/core/java/com/android/internal/util/dump/DualDumpOutputStream.java
+++ b/core/java/com/android/internal/util/dump/DualDumpOutputStream.java
@@ -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;
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
new file mode 100644
index 000000000000..07e938ddfc5d
--- /dev/null
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -0,0 +1,50 @@
+/*
+ * 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
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 5ec3cf7cd9b0..938a3d283ca4 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -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);
+ }
}
/**