Usb launch preference denial able to be remembered

When a device is connected the user might be prompted with
a dialog to launch an application. This change creates the
api that the system UI will use to give the user a
"deny [for this device+package] and remember" option.

Test: Manual:
      Edit usb_device_manager.xml and observe correct behavior
      Call new methods and observe correct changes in xml file
Bug: 136496922

Change-Id: I20e377d601ec11b8d42c79e4c726b9a4cb68c8b0
This commit is contained in:
Evan Severson
2019-09-06 10:56:23 -07:00
parent fcedf73656
commit d9f045ebac
4 changed files with 396 additions and 5 deletions

View File

@@ -55,6 +55,18 @@ interface IUsbManager
*/
void setAccessoryPackage(in UsbAccessory accessory, String packageName, int userId);
/* Adds packages to the set of "denied and don't ask again" launch preferences for a device */
void addDevicePackagesToPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user);
/* Adds packages to the set of "denied and don't ask again" launch preferences for an accessory */
void addAccessoryPackagesToPreferenceDenied(in UsbAccessory accessory, in String[] packageNames, in UserHandle user);
/* Removes packages from the set of "denied and don't ask again" launch preferences for a device */
void removeDevicePackagesFromPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user);
/* Removes packages from the set of "denied and don't ask again" launch preferences for an accessory */
void removeAccessoryPackagesFromPreferenceDenied(in UsbAccessory device, in String[] packageNames, in UserHandle user);
/* Sets the persistent permission granted state for USB device
*/
void setDevicePersistentPermission(in UsbDevice device, int uid, in UserHandle user, boolean shouldBeGranted);

View File

@@ -46,6 +46,8 @@ import android.service.usb.UsbProfileGroupSettingsManagerProto;
import android.service.usb.UsbSettingsAccessoryPreferenceProto;
import android.service.usb.UsbSettingsDevicePreferenceProto;
import android.service.usb.UserPackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -70,6 +72,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -102,10 +105,20 @@ class UsbProfileGroupSettingsManager {
@GuardedBy("mLock")
private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
/** Maps DeviceFilter to set of UserPackages not to ask for launch preference anymore */
@GuardedBy("mLock")
private final ArrayMap<DeviceFilter, ArraySet<UserPackage>> mDevicePreferenceDeniedMap =
new ArrayMap<>();
/** Maps AccessoryFilter to user preferred application package */
@GuardedBy("mLock")
private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
/** Maps AccessoryFilter to set of UserPackages not to ask for launch preference anymore */
@GuardedBy("mLock")
private final ArrayMap<AccessoryFilter, ArraySet<UserPackage>> mAccessoryPreferenceDeniedMap =
new ArrayMap<>();
private final Object mLock = new Object();
/**
@@ -248,11 +261,11 @@ class UsbProfileGroupSettingsManager {
}
/**
* Remove all defaults for a user.
* Remove all defaults and denied packages for a user.
*
* @param userToRemove The user the defaults belong to.
* @param userToRemove The user
*/
void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
void removeUser(@NonNull UserHandle userToRemove) {
synchronized (mLock) {
boolean needToPersist = false;
Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
@@ -277,6 +290,28 @@ class UsbProfileGroupSettingsManager {
}
}
int numEntries = mDevicePreferenceDeniedMap.size();
for (int i = 0; i < numEntries; i++) {
ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.valueAt(i);
for (int j = userPackages.size() - 1; j >= 0; j--) {
if (userPackages.valueAt(j).user.equals(userToRemove)) {
userPackages.removeAt(j);
needToPersist = true;
}
}
}
numEntries = mAccessoryPreferenceDeniedMap.size();
for (int i = 0; i < numEntries; i++) {
ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.valueAt(i);
for (int j = userPackages.size() - 1; j >= 0; j--) {
if (userPackages.valueAt(j).user.equals(userToRemove)) {
userPackages.removeAt(j);
needToPersist = true;
}
}
}
if (needToPersist) {
scheduleWriteSettingsLocked();
}
@@ -284,7 +319,7 @@ class UsbProfileGroupSettingsManager {
}
private void readPreference(XmlPullParser parser)
throws XmlPullParserException, IOException {
throws IOException, XmlPullParserException {
String packageName = null;
// If not set, assume it to be the parent profile
@@ -317,6 +352,67 @@ class UsbProfileGroupSettingsManager {
XmlUtils.nextElement(parser);
}
private void readPreferenceDeniedList(@NonNull XmlPullParser parser)
throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
if (!XmlUtils.nextElementWithin(parser, outerDepth)) {
return;
}
if ("usb-device".equals(parser.getName())) {
DeviceFilter filter = DeviceFilter.read(parser);
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if ("user-package".equals(parser.getName())) {
try {
int userId = XmlUtils.readIntAttribute(parser, "user");
String packageName = XmlUtils.readStringAttribute(parser, "package");
if (packageName == null) {
Slog.e(TAG, "Unable to parse package name");
}
ArraySet<UserPackage> set = mDevicePreferenceDeniedMap.get(filter);
if (set == null) {
set = new ArraySet<>();
mDevicePreferenceDeniedMap.put(filter, set);
}
set.add(new UserPackage(packageName, UserHandle.of(userId)));
} catch (ProtocolException e) {
Slog.e(TAG, "Unable to parse user id", e);
}
}
}
} else if ("usb-accessory".equals(parser.getName())) {
AccessoryFilter filter = AccessoryFilter.read(parser);
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if ("user-package".equals(parser.getName())) {
try {
int userId = XmlUtils.readIntAttribute(parser, "user");
String packageName = XmlUtils.readStringAttribute(parser, "package");
if (packageName == null) {
Slog.e(TAG, "Unable to parse package name");
}
ArraySet<UserPackage> set = mAccessoryPreferenceDeniedMap.get(filter);
if (set == null) {
set = new ArraySet<>();
mAccessoryPreferenceDeniedMap.put(filter, set);
}
set.add(new UserPackage(packageName, UserHandle.of(userId)));
} catch (ProtocolException e) {
Slog.e(TAG, "Unable to parse user id", e);
}
}
}
}
while (parser.getDepth() > outerDepth) {
parser.nextTag(); // ignore unknown tags
}
}
/**
* Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
* Should only be called by owner.
@@ -373,6 +469,8 @@ class UsbProfileGroupSettingsManager {
String tagName = parser.getName();
if ("preference".equals(tagName)) {
readPreference(parser);
} else if ("preference-denied-list".equals(tagName)) {
readPreferenceDeniedList(parser);
} else {
XmlUtils.nextElement(parser);
}
@@ -436,6 +534,46 @@ class UsbProfileGroupSettingsManager {
serializer.endTag(null, "preference");
}
int numEntries = mDevicePreferenceDeniedMap.size();
for (int i = 0; i < numEntries; i++) {
DeviceFilter filter = mDevicePreferenceDeniedMap.keyAt(i);
ArraySet<UserPackage> userPackageSet = mDevicePreferenceDeniedMap
.valueAt(i);
serializer.startTag(null, "preference-denied-list");
filter.write(serializer);
int numUserPackages = userPackageSet.size();
for (int j = 0; j < numUserPackages; j++) {
UserPackage userPackage = userPackageSet.valueAt(j);
serializer.startTag(null, "user-package");
serializer.attribute(null, "user",
String.valueOf(getSerial(userPackage.user)));
serializer.attribute(null, "package", userPackage.packageName);
serializer.endTag(null, "user-package");
}
serializer.endTag(null, "preference-denied-list");
}
numEntries = mAccessoryPreferenceDeniedMap.size();
for (int i = 0; i < numEntries; i++) {
AccessoryFilter filter = mAccessoryPreferenceDeniedMap.keyAt(i);
ArraySet<UserPackage> userPackageSet =
mAccessoryPreferenceDeniedMap.valueAt(i);
serializer.startTag(null, "preference-denied-list");
filter.write(serializer);
int numUserPackages = userPackageSet.size();
for (int j = 0; j < numUserPackages; j++) {
UserPackage userPackage = userPackageSet.valueAt(j);
serializer.startTag(null, "user-package");
serializer.attribute(null, "user",
String.valueOf(getSerial(userPackage.user)));
serializer.attribute(null, "package", userPackage.packageName);
serializer.endTag(null, "user-package");
}
serializer.endTag(null, "preference-denied-list");
}
serializer.endTag(null, "settings");
serializer.endDocument();
@@ -834,6 +972,25 @@ class UsbProfileGroupSettingsManager {
private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
@Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
@Nullable UsbAccessory accessory) {
// Remove all matches which are on the denied list
ArraySet deniedPackages = null;
if (device != null) {
deniedPackages = mDevicePreferenceDeniedMap.get(new DeviceFilter(device));
} else if (accessory != null) {
deniedPackages = mAccessoryPreferenceDeniedMap.get(new AccessoryFilter(accessory));
}
if (deniedPackages != null) {
for (int i = matches.size() - 1; i >= 0; i--) {
ResolveInfo match = matches.get(i);
String packageName = match.activityInfo.packageName;
UserHandle user = UserHandle
.getUserHandleForUid(match.activityInfo.applicationInfo.uid);
if (deniedPackages.contains(new UserPackage(packageName, user))) {
matches.remove(i);
}
}
}
// don't show the resolver activity if there are no choices available
if (matches.size() == 0) {
if (accessory != null) {
@@ -1075,6 +1232,156 @@ class UsbProfileGroupSettingsManager {
}
}
/**
* Add package to the denied for handling a device
*
* @param device the device to add to the denied
* @param packageNames the packages to not become handler
* @param user the user
*/
void addDevicePackagesToDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
@NonNull UserHandle user) {
if (packageNames.length == 0) {
return;
}
DeviceFilter filter = new DeviceFilter(device);
synchronized (mLock) {
ArraySet<UserPackage> userPackages;
if (mDevicePreferenceDeniedMap.containsKey(filter)) {
userPackages = mDevicePreferenceDeniedMap.get(filter);
} else {
userPackages = new ArraySet<>();
mDevicePreferenceDeniedMap.put(filter, userPackages);
}
boolean shouldWrite = false;
for (String packageName : packageNames) {
UserPackage userPackage = new UserPackage(packageName, user);
if (!userPackages.contains(userPackage)) {
userPackages.add(userPackage);
shouldWrite = true;
}
}
if (shouldWrite) {
scheduleWriteSettingsLocked();
}
}
}
/**
* Add package to the denied for handling a accessory
*
* @param accessory the accessory to add to the denied
* @param packageNames the packages to not become handler
* @param user the user
*/
void addAccessoryPackagesToDenied(@NonNull UsbAccessory accessory,
@NonNull String[] packageNames, @NonNull UserHandle user) {
if (packageNames.length == 0) {
return;
}
AccessoryFilter filter = new AccessoryFilter(accessory);
synchronized (mLock) {
ArraySet<UserPackage> userPackages;
if (mAccessoryPreferenceDeniedMap.containsKey(filter)) {
userPackages = mAccessoryPreferenceDeniedMap.get(filter);
} else {
userPackages = new ArraySet<>();
mAccessoryPreferenceDeniedMap.put(filter, userPackages);
}
boolean shouldWrite = false;
for (String packageName : packageNames) {
UserPackage userPackage = new UserPackage(packageName, user);
if (!userPackages.contains(userPackage)) {
userPackages.add(userPackage);
shouldWrite = true;
}
}
if (shouldWrite) {
scheduleWriteSettingsLocked();
}
}
}
/**
* Remove UserPackage from the denied for handling a device
*
* @param device the device to remove denied packages from
* @param packageName the packages to remove
* @param user the user
*/
void removeDevicePackagesFromDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
@NonNull UserHandle user) {
DeviceFilter filter = new DeviceFilter(device);
synchronized (mLock) {
ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.get(filter);
if (userPackages != null) {
boolean shouldWrite = false;
for (String packageName : packageNames) {
UserPackage userPackage = new UserPackage(packageName, user);
if (userPackages.contains(userPackage)) {
userPackages.remove(userPackage);
shouldWrite = true;
if (userPackages.size() == 0) {
mDevicePreferenceDeniedMap.remove(filter);
break;
}
}
}
if (shouldWrite) {
scheduleWriteSettingsLocked();
}
}
}
}
/**
* Remove UserPackage from the denied for handling a accessory
*
* @param accessory the accessory to remove denied packages from
* @param packageName the packages to remove
* @param user the user
*/
void removeAccessoryPackagesFromDenied(@NonNull UsbAccessory accessory,
@NonNull String[] packageNames, @NonNull UserHandle user) {
AccessoryFilter filter = new AccessoryFilter(accessory);
synchronized (mLock) {
ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.get(filter);
if (userPackages != null) {
boolean shouldWrite = false;
for (String packageName : packageNames) {
UserPackage userPackage = new UserPackage(packageName, user);
if (userPackages.contains(userPackage)) {
userPackages.remove(userPackage);
shouldWrite = true;
if (userPackages.size() == 0) {
mAccessoryPreferenceDeniedMap.remove(filter);
break;
}
}
}
if (shouldWrite) {
scheduleWriteSettingsLocked();
}
}
}
}
/**
* Set a package as default handler for a accessory.
*

View File

@@ -360,6 +360,78 @@ public class UsbService extends IUsbManager.Stub {
}
}
@Override
public void addDevicePackagesToPreferenceDenied(UsbDevice device, String[] packageNames,
UserHandle user) {
device = Preconditions.checkNotNull(device);
packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
user = Preconditions.checkNotNull(user);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long token = Binder.clearCallingIdentity();
try {
mSettingsManager.getSettingsForProfileGroup(user)
.addDevicePackagesToDenied(device, packageNames, user);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void addAccessoryPackagesToPreferenceDenied(UsbAccessory accessory,
String[] packageNames, UserHandle user) {
accessory = Preconditions.checkNotNull(accessory);
packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
user = Preconditions.checkNotNull(user);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long token = Binder.clearCallingIdentity();
try {
mSettingsManager.getSettingsForProfileGroup(user)
.addAccessoryPackagesToDenied(accessory, packageNames, user);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void removeDevicePackagesFromPreferenceDenied(UsbDevice device, String[] packageNames,
UserHandle user) {
device = Preconditions.checkNotNull(device);
packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
user = Preconditions.checkNotNull(user);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long token = Binder.clearCallingIdentity();
try {
mSettingsManager.getSettingsForProfileGroup(user)
.removeDevicePackagesFromDenied(device, packageNames, user);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void removeAccessoryPackagesFromPreferenceDenied(UsbAccessory accessory,
String[] packageNames, UserHandle user) {
accessory = Preconditions.checkNotNull(accessory);
packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
user = Preconditions.checkNotNull(user);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long token = Binder.clearCallingIdentity();
try {
mSettingsManager.getSettingsForProfileGroup(user)
.removeAccessoryPackagesFromDenied(accessory, packageNames, user);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setDevicePersistentPermission(UsbDevice device, int uid, UserHandle user,
boolean shouldBeGranted) {

View File

@@ -130,7 +130,7 @@ class UsbSettingsManager {
// it from all profile groups.
int numProfileGroups = mSettingsByProfileGroup.size();
for (int i = 0; i < numProfileGroups; i++) {
mSettingsByProfileGroup.valueAt(i).removeAllDefaultsForUser(userToRemove);
mSettingsByProfileGroup.valueAt(i).removeUser(userToRemove);
}
}
}