Merge changes from topic "CameraMicToggleListeners"

* changes:
  Add listeners for individual camera and microphone toggles
  Make the sensor privacy state per user
This commit is contained in:
Evan Severson
2021-01-09 01:49:09 +00:00
committed by Android (Google) Code Review
4 changed files with 433 additions and 81 deletions

View File

@@ -26,18 +26,19 @@ interface ISensorPrivacyManager {
// =============== Beginning of transactions used on native side as well ======================
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
void addIndividualSensorPrivacyListener(int userId, int sensor,
in ISensorPrivacyListener listener);
void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
boolean isSensorPrivacyEnabled();
boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
void setSensorPrivacy(boolean enable);
void setIndividualSensorPrivacy(int userId, int sensor, boolean enable);
void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, 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
}

View File

@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -159,6 +160,37 @@ public final class SensorPrivacyManager {
}
}
/**
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
* @param userId the user's id
* @param sensor the sensor to listen to changes to
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*/
public void addSensorPrivacyListener(@UserIdInt int userId, @IndividualSensor int sensor,
final OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
listener.onSensorPrivacyChanged(enabled);
}
};
mListeners.put(listener, iListener);
}
try {
mService.addIndividualSensorPrivacyListener(userId, sensor, iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters the specified listener from receiving notifications when the state of sensor
* privacy changes.
@@ -198,9 +230,10 @@ public final class SensorPrivacyManager {
*
* @return true if sensor privacy is currently enabled, false otherwise.
*/
public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId,
@IndividualSensor int sensor) {
try {
return mService.isIndividualSensorPrivacyEnabled(sensor);
return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -212,9 +245,26 @@ public final class SensorPrivacyManager {
* @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) {
public void setIndividualSensorPrivacy(@UserIdInt int userId, @IndividualSensor int sensor,
boolean enable) {
try {
mService.setIndividualSensorPrivacy(sensor, enable);
mService.setIndividualSensorPrivacy(userId, sensor, enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets sensor privacy to the specified state for an individual sensor for the profile group of
* the given user.
*
* @param enable the state to which sensor privacy should be set.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId,
@IndividualSensor int sensor, boolean enable) {
try {
mService.setIndividualSensorPrivacyForProfileGroup(userId, sensor, enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -25,11 +25,29 @@ import "frameworks/base/core/proto/android/privacy.proto";
message SensorPrivacyServiceDumpProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// DEPRECATED
// Is global sensor privacy enabled
optional bool is_enabled = 1;
// DEPRECATED
// Per sensor privacy enabled
repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 2;
// Per user settings for sensor privacy
repeated SensorPrivacyUserProto user = 3;
}
message SensorPrivacyUserProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// User id
optional int32 user_id = 1;
// Is global sensor privacy enabled
optional bool is_enabled = 2;
// Per sensor privacy enabled
repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 3;
}
message SensorPrivacyIndividualEnabledSensorProto {

View File

@@ -20,12 +20,14 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
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.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -49,12 +51,15 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyServiceDumpProto;
import android.service.SensorPrivacyUserProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -63,9 +68,11 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
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.pm.UserManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,9 +91,19 @@ public final class SensorPrivacyService extends SystemService {
private static final String TAG = "SensorPrivacyService";
/** Version number indicating compatibility parsing the persisted file */
private static final int CURRENT_PERSISTENCE_VERSION = 1;
/** Version number indicating the persisted data needs upgraded to match new internal data
* structures and features */
private static final int CURRENT_VERSION = 1;
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_USER = "user";
private static final String XML_TAG_INDIVIDUAL_SENSOR_PRIVACY = "individual-sensor-privacy";
private static final String XML_ATTRIBUTE_ID = "id";
private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
private static final String XML_ATTRIBUTE_VERSION = "version";
private static final String XML_ATTRIBUTE_ENABLED = "enabled";
private static final String XML_ATTRIBUTE_SENSOR = "sensor";
@@ -96,10 +113,18 @@ public final class SensorPrivacyService extends SystemService {
private static final String EXTRA_SENSOR = SensorPrivacyService.class.getName()
+ ".extra.sensor";
// These are associated with fields that existed for older persisted versions of files
private static final int VER0_ENABLED = 0;
private static final int VER0_INDIVIDUAL_ENABLED = 1;
private static final int VER1_ENABLED = 0;
private static final int VER1_INDIVIDUAL_ENABLED = 1;
private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
private final UserManagerInternal mUserManagerInternal;
public SensorPrivacyService(Context context) {
super(context);
mUserManagerInternal = getLocalService(UserManagerInternal.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(context);
}
@@ -117,8 +142,9 @@ public final class SensorPrivacyService extends SystemService {
@GuardedBy("mLock")
private final AtomicFile mAtomicFile;
@GuardedBy("mLock")
private boolean mEnabled;
private SparseBooleanArray mIndividualEnabled = new SparseBooleanArray();
private SparseBooleanArray mEnabled = new SparseBooleanArray();
@GuardedBy("mLock")
private SparseArray<SparseBooleanArray> mIndividualEnabled = new SparseArray<>();
SensorPrivacyServiceImpl(Context context) {
mContext = context;
@@ -127,7 +153,9 @@ public final class SensorPrivacyService extends SystemService {
SENSOR_PRIVACY_XML_FILE);
mAtomicFile = new AtomicFile(sensorPrivacyFile);
synchronized (mLock) {
readPersistedSensorPrivacyStateLocked();
if (readPersistedSensorPrivacyStateLocked()) {
persistSensorPrivacyStateLocked();
}
}
int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA};
@@ -138,7 +166,8 @@ public final class SensorPrivacyService extends SystemService {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setIndividualSensorPrivacy(intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
setIndividualSensorPrivacy(intent.getIntExtra(Intent.EXTRA_USER_ID, -1),
intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
}
}, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY));
}
@@ -174,7 +203,8 @@ public final class SensorPrivacyService extends SystemService {
* @param sensor The sensor that is attempting to be used
*/
private void onSensorUseStarted(int uid, String packageName, int sensor) {
if (!isIndividualSensorPrivacyEnabled(sensor)) {
int userId = UserHandle.getUserId(uid);
if (!isIndividualSensorPrivacyEnabled(userId, sensor)) {
return;
}
@@ -216,7 +246,8 @@ public final class SensorPrivacyService extends SystemService {
PendingIntent.getBroadcast(mContext, sensor,
new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
.setPackage(mContext.getPackageName())
.putExtra(EXTRA_SENSOR, sensor),
.putExtra(EXTRA_SENSOR, sensor)
.putExtra(Intent.EXTRA_USER_ID, userId),
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT))
.build())
@@ -229,18 +260,27 @@ public final class SensorPrivacyService extends SystemService {
*/
@Override
public void setSensorPrivacy(boolean enable) {
// Keep the state consistent between all users to make it a single global state
forAllUsers(userId -> setSensorPrivacy(userId, enable));
}
private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
enforceSensorPrivacyPermission();
synchronized (mLock) {
mEnabled = enable;
mEnabled.put(userId, enable);
persistSensorPrivacyStateLocked();
}
mHandler.onSensorPrivacyChanged(enable);
}
public void setIndividualSensorPrivacy(int sensor, boolean enable) {
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
enforceSensorPrivacyPermission();
synchronized (mLock) {
mIndividualEnabled.put(sensor, enable);
SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId,
new SparseBooleanArray());
userIndividualEnabled.put(sensor, enable);
mIndividualEnabled.put(userId, userIndividualEnabled);
if (!enable) {
// Remove any notifications prompting the user to disable sensory privacy
@@ -249,9 +289,20 @@ public final class SensorPrivacyService extends SystemService {
notificationManager.cancel(sensor);
}
persistSensorPrivacyState();
}
mHandler.onSensorPrivacyChanged(userId, sensor, enable);
}
@Override
public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor,
boolean enable) {
int parentId = mUserManagerInternal.getProfileParentId(userId);
forAllUsers(userId2 -> {
if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
setIndividualSensorPrivacy(userId2, sensor, enable);
}
});
}
/**
@@ -273,53 +324,179 @@ public final class SensorPrivacyService extends SystemService {
*/
@Override
public boolean isSensorPrivacyEnabled() {
return isSensorPrivacyEnabled(USER_SYSTEM);
}
private boolean isSensorPrivacyEnabled(@UserIdInt int userId) {
synchronized (mLock) {
return mEnabled;
return mEnabled.get(userId, false);
}
}
@Override
public boolean isIndividualSensorPrivacyEnabled(int sensor) {
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
synchronized (mLock) {
return mIndividualEnabled.get(sensor, false);
SparseBooleanArray states = mIndividualEnabled.get(userId);
if (states == null) {
return false;
}
return states.get(sensor, false);
}
}
/**
* Returns the state of sensor privacy from persistent storage.
*/
private void readPersistedSensorPrivacyStateLocked() {
private boolean readPersistedSensorPrivacyStateLocked() {
// if the file does not exist then sensor privacy has not yet been enabled on
// the device.
if (!mAtomicFile.exists()) {
return;
}
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();
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 {
SparseArray<Object> map = new SparseArray<>();
int version = -1;
if (mAtomicFile.exists()) {
try (FileInputStream inputStream = mAtomicFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
final int persistenceVersion = parser.getAttributeInt(null,
XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
// Use inline string literals for xml tags/attrs when parsing old versions since
// these should never be changed even with refactorings.
if (persistenceVersion == 0) {
boolean enabled = parser.getAttributeBoolean(null, "enabled", false);
SparseBooleanArray individualEnabled = new SparseBooleanArray();
version = 0;
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if ("individual-sensor-privacy".equals(tagName)) {
int sensor = XmlUtils.readIntAttribute(parser, "sensor");
boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
"enabled");
individualEnabled.put(sensor, indEnabled);
XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.nextElement(parser);
}
}
map.put(VER0_ENABLED, enabled);
map.put(VER0_INDIVIDUAL_ENABLED, individualEnabled);
} else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
SparseBooleanArray enabled = new SparseBooleanArray();
SparseArray<SparseBooleanArray> individualEnabled = new SparseArray<>();
version = parser.getAttributeInt(null,
XML_ATTRIBUTE_VERSION, 1);
int currentUserId = -1;
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
XmlUtils.nextElement(parser);
String tagName = parser.getName();
if (XML_TAG_USER.equals(tagName)) {
currentUserId = parser.getAttributeInt(null, XML_ATTRIBUTE_ID);
boolean isEnabled = parser.getAttributeBoolean(null,
XML_ATTRIBUTE_ENABLED);
if (enabled.indexOfKey(currentUserId) >= 0) {
Log.e(TAG, "User listed multiple times in file.",
new RuntimeException());
mAtomicFile.delete();
version = -1;
break;
}
if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
// User may no longer exist, skip this user
currentUserId = -1;
continue;
}
enabled.put(currentUserId, isEnabled);
}
if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) {
if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
// User may no longer exist or isn't set
continue;
}
int sensor = parser.getAttributeIndex(null, XML_ATTRIBUTE_SENSOR);
boolean isEnabled = parser.getAttributeBoolean(null,
XML_ATTRIBUTE_ENABLED);
SparseBooleanArray userIndividualEnabled = individualEnabled.get(
currentUserId, new SparseBooleanArray());
userIndividualEnabled.put(sensor, isEnabled);
individualEnabled.put(currentUserId, userIndividualEnabled);
}
}
map.put(VER1_ENABLED, enabled);
map.put(VER1_INDIVIDUAL_ENABLED, individualEnabled);
} else {
Log.e(TAG, "Unknown persistence version: " + persistenceVersion
+ ". Deleting.",
new RuntimeException());
mAtomicFile.delete();
version = -1;
}
} 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();
version = -1;
}
}
return upgradeAndInit(version, map);
}
private boolean upgradeAndInit(int version, SparseArray map) {
if (version == -1) {
// New file, default state for current version goes here.
mEnabled = new SparseBooleanArray();
mIndividualEnabled = new SparseArray<>();
forAllUsers(userId -> mEnabled.put(userId, false));
forAllUsers(userId -> mIndividualEnabled.put(userId, new SparseBooleanArray()));
return true;
}
boolean upgraded = false;
final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
if (version == 0) {
final boolean enabled = (boolean) map.get(VER0_ENABLED);
final SparseBooleanArray individualEnabled =
(SparseBooleanArray) map.get(VER0_INDIVIDUAL_ENABLED);
final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
final SparseArray<SparseBooleanArray> perUserIndividualEnabled =
new SparseArray<>();
// Copy global state to each user
for (int i = 0; i < users.length; i++) {
int user = users[i];
perUserEnabled.put(user, enabled);
SparseBooleanArray userIndividualSensorEnabled = new SparseBooleanArray();
perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
for (int j = 0; j < individualEnabled.size(); j++) {
final int sensor = individualEnabled.keyAt(j);
final boolean isSensorEnabled = individualEnabled.valueAt(j);
userIndividualSensorEnabled.put(sensor, isSensorEnabled);
}
}
} 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();
map.clear();
map.put(VER1_ENABLED, perUserEnabled);
map.put(VER1_INDIVIDUAL_ENABLED, perUserIndividualEnabled);
version = 1;
upgraded = true;
}
if (version == CURRENT_VERSION) {
mEnabled = (SparseBooleanArray) map.get(VER1_ENABLED);
mIndividualEnabled =
(SparseArray<SparseBooleanArray>) map.get(VER1_INDIVIDUAL_ENABLED);
}
return upgraded;
}
/**
@@ -338,16 +515,29 @@ public final class SensorPrivacyService extends SystemService {
TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
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.attributeInt(
null, XML_ATTRIBUTE_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
forAllUsers(userId -> {
serializer.startTag(null, XML_TAG_USER);
serializer.attributeInt(null, XML_ATTRIBUTE_ID, userId);
serializer.attributeBoolean(
null, XML_ATTRIBUTE_ENABLED, isSensorPrivacyEnabled(userId));
SparseBooleanArray individualEnabled =
mIndividualEnabled.get(userId, new SparseBooleanArray());
int numIndividual = individualEnabled.size();
for (int i = 0; i < numIndividual; i++) {
serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
int sensor = individualEnabled.keyAt(i);
boolean enabled = individualEnabled.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_USER);
});
serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.endDocument();
mAtomicFile.finishWrite(outputStream);
@@ -368,6 +558,18 @@ public final class SensorPrivacyService extends SystemService {
mHandler.addListener(listener);
}
/**
* Registers a listener to be notified when the sensor privacy state changes.
*/
@Override
public void addIndividualSensorPrivacyListener(int userId, int sensor,
ISensorPrivacyListener listener) {
if (listener == null) {
throw new NullPointerException("listener cannot be null");
}
mHandler.addListener(userId, sensor, listener);
}
/**
* Unregisters a listener from sensor privacy state change notifications.
*/
@@ -422,22 +624,32 @@ public final class SensorPrivacyService extends SystemService {
*/
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);
forAllUsers(userId -> {
long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
dumpStream.write("is_enabled", SensorPrivacyUserProto.IS_ENABLED,
mEnabled.get(userId, false));
dumpStream.write("sensor",
SensorPrivacyIndividualEnabledSensorProto.SENSOR,
mIndividualEnabled.keyAt(i));
dumpStream.write("is_enabled",
SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
mIndividualEnabled.valueAt(i));
SparseBooleanArray individualEnabled = mIndividualEnabled.get(userId);
if (individualEnabled != null) {
int numIndividualEnabled = individualEnabled.size();
for (int i = 0; i < numIndividualEnabled; i++) {
long individualToken = dumpStream.start("individual_enabled_sensor",
SensorPrivacyUserProto.INDIVIDUAL_ENABLED_SENSOR);
dumpStream.end(token);
}
dumpStream.write("sensor",
SensorPrivacyIndividualEnabledSensorProto.SENSOR,
individualEnabled.keyAt(i));
dumpStream.write("is_enabled",
SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
individualEnabled.valueAt(i));
dumpStream.end(individualToken);
}
}
dumpStream.end(userToken);
});
}
dumpStream.flush();
@@ -477,30 +689,32 @@ public final class SensorPrivacyService extends SystemService {
return handleDefaultCommands(cmd);
}
int userId = Integer.parseInt(getNextArgRequired());
final PrintWriter pw = getOutPrintWriter();
switch (cmd) {
case "enable" : {
int sensor = sensorStrToId(getNextArg());
int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
}
setIndividualSensorPrivacy(sensor, true);
setIndividualSensorPrivacy(userId, sensor, true);
}
break;
case "disable" : {
int sensor = sensorStrToId(getNextArg());
int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
}
setIndividualSensorPrivacy(sensor, false);
setIndividualSensorPrivacy(userId, sensor, false);
}
break;
case "reset": {
int sensor = sensorStrToId(getNextArg());
int sensor = sensorStrToId(getNextArgRequired());
if (sensor == UNKNOWN) {
pw.println("Invalid sensor");
return -1;
@@ -509,7 +723,11 @@ public final class SensorPrivacyService extends SystemService {
enforceSensorPrivacyPermission();
synchronized (mLock) {
mIndividualEnabled.delete(sensor);
SparseBooleanArray individualEnabled =
mIndividualEnabled.get(userId);
if (individualEnabled != null) {
individualEnabled.delete(sensor);
}
persistSensorPrivacyState();
}
}
@@ -530,13 +748,13 @@ public final class SensorPrivacyService extends SystemService {
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
pw.println(" enable SENSOR");
pw.println(" enable USER_ID SENSOR");
pw.println(" Enable privacy for a certain sensor.");
pw.println("");
pw.println(" disable SENSOR");
pw.println(" disable USER_ID SENSOR");
pw.println(" Disable privacy for a certain sensor.");
pw.println("");
pw.println(" reset SENSOR");
pw.println(" reset USER_ID SENSOR");
pw.println(" Reset privacy state for a certain sensor.");
pw.println("");
}
@@ -555,6 +773,9 @@ public final class SensorPrivacyService extends SystemService {
@GuardedBy("mListenerLock")
private final RemoteCallbackList<ISensorPrivacyListener> mListeners =
new RemoteCallbackList<>();
@GuardedBy("mListenerLock")
private final SparseArray<SparseArray<RemoteCallbackList<ISensorPrivacyListener>>>
mIndividualSensorListeners = new SparseArray<>();
private final ArrayMap<ISensorPrivacyListener, DeathRecipient> mDeathRecipients;
private final Context mContext;
@@ -572,6 +793,14 @@ public final class SensorPrivacyService extends SystemService {
mSensorPrivacyServiceImpl));
}
public void onSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
this, userId, sensor, enabled));
sendMessage(
PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
mSensorPrivacyServiceImpl));
}
public void addListener(ISensorPrivacyListener listener) {
synchronized (mListenerLock) {
DeathRecipient deathRecipient = new DeathRecipient(listener);
@@ -580,6 +809,25 @@ public final class SensorPrivacyService extends SystemService {
}
}
public void addListener(int userId, int sensor, ISensorPrivacyListener listener) {
synchronized (mListenerLock) {
DeathRecipient deathRecipient = new DeathRecipient(listener);
mDeathRecipients.put(listener, deathRecipient);
SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
mIndividualSensorListeners.get(userId);
if (listenersForUser == null) {
listenersForUser = new SparseArray<>();
mIndividualSensorListeners.put(userId, listenersForUser);
}
RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
if (listeners == null) {
listeners = new RemoteCallbackList<>();
listenersForUser.put(sensor, listeners);
}
listeners.register(listener);
}
}
public void removeListener(ISensorPrivacyListener listener) {
synchronized (mListenerLock) {
DeathRecipient deathRecipient = mDeathRecipients.remove(listener);
@@ -587,6 +835,12 @@ public final class SensorPrivacyService extends SystemService {
deathRecipient.destroy();
}
mListeners.unregister(listener);
for (int i = 0, numUsers = mIndividualSensorListeners.size(); i < numUsers; i++) {
for (int j = 0, numListeners = mIndividualSensorListeners.valueAt(i).size();
j < numListeners; j++) {
mIndividualSensorListeners.valueAt(i).valueAt(j).unregister(listener);
}
}
}
}
@@ -602,6 +856,28 @@ public final class SensorPrivacyService extends SystemService {
}
mListeners.finishBroadcast();
}
public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
mIndividualSensorListeners.get(userId);
if (listenersForUser == null) {
return;
}
RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
if (listeners == null) {
return;
}
final int count = listeners.beginBroadcast();
for (int i = 0; i < count; i++) {
ISensorPrivacyListener listener = listeners.getBroadcastItem(i);
try {
listener.onSensorPrivacyChanged(enabled);
} catch (RemoteException e) {
Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
}
}
listeners.finishBroadcast();
}
}
private final class DeathRecipient implements IBinder.DeathRecipient {
@@ -628,4 +904,11 @@ public final class SensorPrivacyService extends SystemService {
}
}
}
private void forAllUsers(FunctionalUtils.ThrowingConsumer<Integer> c) {
int[] userIds = mUserManagerInternal.getUserIds();
for (int i = 0; i < userIds.length; i++) {
c.accept(userIds[i]);
}
}
}