From 880389e6cc8044f751dd8569f2172ca61eaf2cd3 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Mon, 10 Oct 2016 14:02:13 -0700 Subject: [PATCH] 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 --- .../server/usb/UsbProfileGroupSettingsManager.java | 156 +++++++++++++++------ .../java/com/android/server/usb/UsbService.java | 9 +- .../com/android/server/usb/UsbSettingsManager.java | 21 ++- 3 files changed, 136 insertions(+), 50 deletions(-) diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index cc0fb8d3e485..e03a14f41ce2 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -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,14 +91,24 @@ 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 mDevicePreferenceMap = new HashMap<>(); - // Maps AccessoryFilter to user preferred application package + + /** Maps AccessoryFilter to user preferred application package */ + @GuardedBy("mLock") private final HashMap 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. */ @Immutable @@ -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> devicePreferenceIt = mDevicePreferenceMap + .entrySet().iterator(); + while (devicePreferenceIt.hasNext()) { + Map.Entry entry = devicePreferenceIt.next(); + + if (entry.getValue().user.equals(userToRemove)) { + devicePreferenceIt.remove(); + needToPersist = true; + } + } + + Iterator> accessoryPreferenceIt = + mAccessoryPreferenceMap.entrySet().iterator(); + while (accessoryPreferenceIt.hasNext()) { + Map.Entry 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(); + /** + * 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. + *

Called with {@link #mLock} held.

+ *

In the uncommon case that the system crashes in between the scheduling and the write the + * update is lost.

+ */ + private void scheduleWriteSettingsLocked() { + if (mIsWriteSettingsScheduled) { + return; + } else { + mIsWriteSettingsScheduled = true; + } - 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"); + 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 (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"); + } - 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(); - 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); + } + } - 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(); } } } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 02c7214b2b19..c85b363494e8 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -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() { diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java index b251d261eb53..24d5f0955e9a 100644 --- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java @@ -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); + } + } } } -- 2.11.0