diff options
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); + } } /** |