Clear USB device defaults when user is removed

If a user is removed it is either the parent user of the profile or a
child user. If the parent is removed the whole profile group settings
are removed, but if only a child user is removed we have to remove the
user settings from the groups settings.

Test: Registered a USB device default for a child and parent user and
removed them. Checked dumpsys usb before and after
Fixes: 31995672

Change-Id: I984cd294dc01437b042687684c058eb79332f520
This commit is contained in:
Philip P. Moltmann
2016-10-10 14:02:13 -07:00
parent 017f1dc54f
commit 880389e6cc
3 changed files with 141 additions and 55 deletions

View File

@@ -34,6 +34,7 @@ import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
@@ -42,6 +43,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
@@ -60,7 +62,9 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import libcore.io.IoUtils;
@@ -87,13 +91,23 @@ class UsbProfileGroupSettingsManager {
private final UserManager mUserManager;
private final @NonNull UsbSettingsManager mSettingsManager;
// Maps DeviceFilter to user preferred application package
/** Maps DeviceFilter to user preferred application package */
@GuardedBy("mLock")
private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
// Maps AccessoryFilter to user preferred application package
/** Maps AccessoryFilter to user preferred application package */
@GuardedBy("mLock")
private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
private final Object mLock = new Object();
/**
* If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently
* scheduled.
*/
@GuardedBy("mLock")
private boolean mIsWriteSettingsScheduled;
/**
* A package of a user.
*/
@@ -591,6 +605,42 @@ class UsbProfileGroupSettingsManager {
});
}
/**
* Remove all defaults for a user.
*
* @param userToRemove The user the defaults belong to.
*/
void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
synchronized (mLock) {
boolean needToPersist = false;
Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
.entrySet().iterator();
while (devicePreferenceIt.hasNext()) {
Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next();
if (entry.getValue().user.equals(userToRemove)) {
devicePreferenceIt.remove();
needToPersist = true;
}
}
Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
mAccessoryPreferenceMap.entrySet().iterator();
while (accessoryPreferenceIt.hasNext()) {
Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
if (entry.getValue().user.equals(userToRemove)) {
accessoryPreferenceIt.remove();
needToPersist = true;
}
}
if (needToPersist) {
scheduleWriteSettingsLocked();
}
}
}
private void readPreference(XmlPullParser parser)
throws XmlPullParserException, IOException {
String packageName = null;
@@ -657,7 +707,7 @@ class UsbProfileGroupSettingsManager {
IoUtils.closeQuietly(fis);
}
writeSettingsLocked();
scheduleWriteSettingsLocked();
// Success or failure, we delete single-user file
sSingleUserSettingsFile.delete();
@@ -695,48 +745,68 @@ class UsbProfileGroupSettingsManager {
}
}
private void writeSettingsLocked() {
if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
FileOutputStream fos = null;
try {
fos = mSettingsFile.startWrite();
FastXmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(fos, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, "settings");
for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
serializer.startTag(null, "preference");
serializer.attribute(null, "package", mDevicePreferenceMap.get(filter).packageName);
serializer.attribute(null, "user",
String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
filter.write(serializer);
serializer.endTag(null, "preference");
}
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
serializer.startTag(null, "preference");
serializer.attribute(null, "package",
mAccessoryPreferenceMap.get(filter).packageName);
serializer.attribute(null, "user",
String.valueOf(getSerial(mAccessoryPreferenceMap.get(filter).user)));
filter.write(serializer);
serializer.endTag(null, "preference");
}
serializer.endTag(null, "settings");
serializer.endDocument();
mSettingsFile.finishWrite(fos);
} catch (IOException e) {
Slog.e(TAG, "Failed to write settings", e);
if (fos != null) {
mSettingsFile.failWrite(fos);
}
/**
* Schedule a async task to persist {@link #mDevicePreferenceMap} and
* {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do
* nothing as the currently scheduled one will do the work.
* <p>Called with {@link #mLock} held.</p>
* <p>In the uncommon case that the system crashes in between the scheduling and the write the
* update is lost.</p>
*/
private void scheduleWriteSettingsLocked() {
if (mIsWriteSettingsScheduled) {
return;
} else {
mIsWriteSettingsScheduled = true;
}
AsyncTask.execute(() -> {
synchronized (mLock) {
FileOutputStream fos = null;
try {
fos = mSettingsFile.startWrite();
FastXmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(fos, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
true);
serializer.startTag(null, "settings");
for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
serializer.startTag(null, "preference");
serializer.attribute(null, "package",
mDevicePreferenceMap.get(filter).packageName);
serializer.attribute(null, "user",
String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
filter.write(serializer);
serializer.endTag(null, "preference");
}
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
serializer.startTag(null, "preference");
serializer.attribute(null, "package",
mAccessoryPreferenceMap.get(filter).packageName);
serializer.attribute(null, "user", String.valueOf(
getSerial(mAccessoryPreferenceMap.get(filter).user)));
filter.write(serializer);
serializer.endTag(null, "preference");
}
serializer.endTag(null, "settings");
serializer.endDocument();
mSettingsFile.finishWrite(fos);
} catch (IOException e) {
Slog.e(TAG, "Failed to write settings", e);
if (fos != null) {
mSettingsFile.failWrite(fos);
}
}
mIsWriteSettingsScheduled = false;
}
});
}
// Checks to see if a package matches a device or accessory.
@@ -1141,7 +1211,7 @@ class UsbProfileGroupSettingsManager {
}
if (changed) {
writeSettingsLocked();
scheduleWriteSettingsLocked();
}
}
}
@@ -1178,7 +1248,7 @@ class UsbProfileGroupSettingsManager {
}
}
if (changed) {
writeSettingsLocked();
scheduleWriteSettingsLocked();
}
}
}
@@ -1204,7 +1274,7 @@ class UsbProfileGroupSettingsManager {
}
}
if (changed) {
writeSettingsLocked();
scheduleWriteSettingsLocked();
}
}
}
@@ -1237,7 +1307,7 @@ class UsbProfileGroupSettingsManager {
synchronized (mLock) {
if (clearPackageDefaultsLocked(userPackage)) {
writeSettingsLocked();
scheduleWriteSettingsLocked();
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.usb;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
@@ -83,7 +84,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void onStopUser(int userHandle) {
mUsbService.onStopUser(userHandle);
mUsbService.onStopUser(UserHandle.of(userHandle));
}
}
@@ -177,10 +178,10 @@ public class UsbService extends IUsbManager.Stub {
/**
* Execute operations when a user is stopped.
*
* @param stoppedUserId The id of the used that is stopped
* @param stoppedUser The user that is stopped
*/
private void onStopUser(@UserIdInt int stoppedUserId) {
mSettingsManager.remove(stoppedUserId);
private void onStopUser(@NonNull UserHandle stoppedUser) {
mSettingsManager.remove(stoppedUser);
}
public void systemReady() {

View File

@@ -108,11 +108,26 @@ class UsbSettingsManager {
/**
* Remove the settings for a user.
*
* @param userIdToRemove The user o remove
* @param userToRemove The user to remove
*/
void remove(@UserIdInt int userIdToRemove) {
void remove(@NonNull UserHandle userToRemove) {
synchronized (mSettingsByUser) {
mSettingsByUser.remove(userIdToRemove);
mSettingsByUser.remove(userToRemove.getIdentifier());
}
synchronized (mSettingsByProfileGroup) {
if (mSettingsByProfileGroup.indexOfKey(userToRemove.getIdentifier()) >= 0) {
// The user to remove is the parent user of the group. The parent is the last user
// that gets removed. All state will be removed with the user
mSettingsByProfileGroup.remove(userToRemove.getIdentifier());
} else {
// We cannot find the parent user of the user that is removed, hence try to remove
// it from all profile groups.
int numProfileGroups = mSettingsByProfileGroup.size();
for (int i = 0; i < numProfileGroups; i++) {
mSettingsByProfileGroup.valueAt(i).removeAllDefaultsForUser(userToRemove);
}
}
}
}