2 * Copyright (C) 2016 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.usb;
19 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ActivityNotFoundException;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.content.pm.ResolveInfo;
33 import android.content.pm.UserInfo;
34 import android.content.res.XmlResourceParser;
35 import android.hardware.usb.UsbAccessory;
36 import android.hardware.usb.UsbDevice;
37 import android.hardware.usb.UsbInterface;
38 import android.hardware.usb.UsbManager;
39 import android.os.AsyncTask;
40 import android.os.Environment;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.util.AtomicFile;
44 import android.util.Log;
45 import android.util.Slog;
46 import android.util.SparseArray;
47 import android.util.SparseIntArray;
48 import android.util.Xml;
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.annotations.Immutable;
52 import com.android.internal.content.PackageMonitor;
53 import com.android.internal.util.FastXmlSerializer;
54 import com.android.internal.util.IndentingPrintWriter;
55 import com.android.internal.util.XmlUtils;
57 import libcore.io.IoUtils;
59 import org.xmlpull.v1.XmlPullParser;
60 import org.xmlpull.v1.XmlPullParserException;
61 import org.xmlpull.v1.XmlSerializer;
64 import java.io.FileInputStream;
65 import java.io.FileNotFoundException;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 import java.nio.charset.StandardCharsets;
69 import java.util.ArrayList;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.List;
74 import java.util.Objects;
76 class UsbProfileGroupSettingsManager {
77 private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
78 private static final boolean DEBUG = false;
80 /** Legacy settings file, before multi-user */
81 private static final File sSingleUserSettingsFile = new File(
82 "/data/system/usb_device_manager.xml");
84 /** The parent user (main user of the profile group) */
85 private final UserHandle mParentUser;
87 private final AtomicFile mSettingsFile;
88 private final boolean mDisablePermissionDialogs;
90 private final Context mContext;
92 private final PackageManager mPackageManager;
94 private final UserManager mUserManager;
95 private final @NonNull UsbSettingsManager mSettingsManager;
97 /** Maps DeviceFilter to user preferred application package */
99 private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
101 /** Maps AccessoryFilter to user preferred application package */
103 private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
105 private final Object mLock = new Object();
108 * If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently
112 private boolean mIsWriteSettingsScheduled;
115 * A package of a user.
118 private static class UserPackage {
120 final @NonNull UserHandle user;
123 final @NonNull String packageName;
126 * Create a description of a per user package.
128 * @param packageName The name of the package
129 * @param user The user
131 private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
132 this.packageName = packageName;
137 public boolean equals(Object obj) {
138 if (!(obj instanceof UserPackage)) {
141 UserPackage other = (UserPackage)obj;
143 return user.equals(other.user) && packageName.equals(other.packageName);
148 public int hashCode() {
149 int result = user.hashCode();
150 result = 31 * result + packageName.hashCode();
155 public String toString() {
156 return user.getIdentifier() + "/" + packageName;
160 // This class is used to describe a USB device.
161 // When used in HashMaps all values must be specified,
162 // but wildcards can be used for any of the fields in
163 // the package meta-data.
164 private static class DeviceFilter {
165 // USB Vendor ID (or -1 for unspecified)
166 public final int mVendorId;
167 // USB Product ID (or -1 for unspecified)
168 public final int mProductId;
169 // USB device or interface class (or -1 for unspecified)
170 public final int mClass;
171 // USB device subclass (or -1 for unspecified)
172 public final int mSubclass;
173 // USB device protocol (or -1 for unspecified)
174 public final int mProtocol;
175 // USB device manufacturer name string (or null for unspecified)
176 public final String mManufacturerName;
177 // USB device product name string (or null for unspecified)
178 public final String mProductName;
179 // USB device serial number string (or null for unspecified)
180 public final String mSerialNumber;
182 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
183 String manufacturer, String product, String serialnum) {
187 mSubclass = subclass;
188 mProtocol = protocol;
189 mManufacturerName = manufacturer;
190 mProductName = product;
191 mSerialNumber = serialnum;
194 public DeviceFilter(UsbDevice device) {
195 mVendorId = device.getVendorId();
196 mProductId = device.getProductId();
197 mClass = device.getDeviceClass();
198 mSubclass = device.getDeviceSubclass();
199 mProtocol = device.getDeviceProtocol();
200 mManufacturerName = device.getManufacturerName();
201 mProductName = device.getProductName();
202 mSerialNumber = device.getSerialNumber();
205 public static DeviceFilter read(XmlPullParser parser)
206 throws XmlPullParserException, IOException {
209 int deviceClass = -1;
210 int deviceSubclass = -1;
211 int deviceProtocol = -1;
212 String manufacturerName = null;
213 String productName = null;
214 String serialNumber = null;
216 int count = parser.getAttributeCount();
217 for (int i = 0; i < count; i++) {
218 String name = parser.getAttributeName(i);
219 String value = parser.getAttributeValue(i);
220 // Attribute values are ints or strings
221 if ("manufacturer-name".equals(name)) {
222 manufacturerName = value;
223 } else if ("product-name".equals(name)) {
225 } else if ("serial-number".equals(name)) {
226 serialNumber = value;
230 if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
231 (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
232 // allow hex values starting with 0x or 0X
234 value = value.substring(2);
237 intValue = Integer.parseInt(value, radix);
238 } catch (NumberFormatException e) {
239 Slog.e(TAG, "invalid number for field " + name, e);
242 if ("vendor-id".equals(name)) {
244 } else if ("product-id".equals(name)) {
245 productId = intValue;
246 } else if ("class".equals(name)) {
247 deviceClass = intValue;
248 } else if ("subclass".equals(name)) {
249 deviceSubclass = intValue;
250 } else if ("protocol".equals(name)) {
251 deviceProtocol = intValue;
255 return new DeviceFilter(vendorId, productId,
256 deviceClass, deviceSubclass, deviceProtocol,
257 manufacturerName, productName, serialNumber);
260 public void write(XmlSerializer serializer) throws IOException {
261 serializer.startTag(null, "usb-device");
262 if (mVendorId != -1) {
263 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
265 if (mProductId != -1) {
266 serializer.attribute(null, "product-id", Integer.toString(mProductId));
269 serializer.attribute(null, "class", Integer.toString(mClass));
271 if (mSubclass != -1) {
272 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
274 if (mProtocol != -1) {
275 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
277 if (mManufacturerName != null) {
278 serializer.attribute(null, "manufacturer-name", mManufacturerName);
280 if (mProductName != null) {
281 serializer.attribute(null, "product-name", mProductName);
283 if (mSerialNumber != null) {
284 serializer.attribute(null, "serial-number", mSerialNumber);
286 serializer.endTag(null, "usb-device");
289 private boolean matches(int clasz, int subclass, int protocol) {
290 return ((mClass == -1 || clasz == mClass) &&
291 (mSubclass == -1 || subclass == mSubclass) &&
292 (mProtocol == -1 || protocol == mProtocol));
295 public boolean matches(UsbDevice device) {
296 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
297 if (mProductId != -1 && device.getProductId() != mProductId) return false;
298 if (mManufacturerName != null && device.getManufacturerName() == null) return false;
299 if (mProductName != null && device.getProductName() == null) return false;
300 if (mSerialNumber != null && device.getSerialNumber() == null) return false;
301 if (mManufacturerName != null && device.getManufacturerName() != null &&
302 !mManufacturerName.equals(device.getManufacturerName())) return false;
303 if (mProductName != null && device.getProductName() != null &&
304 !mProductName.equals(device.getProductName())) return false;
305 if (mSerialNumber != null && device.getSerialNumber() != null &&
306 !mSerialNumber.equals(device.getSerialNumber())) return false;
308 // check device class/subclass/protocol
309 if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
310 device.getDeviceProtocol())) return true;
312 // if device doesn't match, check the interfaces
313 int count = device.getInterfaceCount();
314 for (int i = 0; i < count; i++) {
315 UsbInterface intf = device.getInterface(i);
316 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
317 intf.getInterfaceProtocol())) return true;
324 * If the device described by {@code device} covered by this filter?
326 * @param device The device
328 * @return {@code true} iff this filter covers the {@code device}
330 public boolean contains(DeviceFilter device) {
331 // -1 and null means "match anything"
333 if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
334 if (mProductId != -1 && device.mProductId != mProductId) return false;
335 if (mManufacturerName != null && !Objects.equals(mManufacturerName,
336 device.mManufacturerName)) {
339 if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
342 if (mSerialNumber != null
343 && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
347 // check device class/subclass/protocol
348 return matches(device.mClass, device.mSubclass, device.mProtocol);
352 public boolean equals(Object obj) {
353 // can't compare if we have wildcard strings
354 if (mVendorId == -1 || mProductId == -1 ||
355 mClass == -1 || mSubclass == -1 || mProtocol == -1) {
358 if (obj instanceof DeviceFilter) {
359 DeviceFilter filter = (DeviceFilter)obj;
361 if (filter.mVendorId != mVendorId ||
362 filter.mProductId != mProductId ||
363 filter.mClass != mClass ||
364 filter.mSubclass != mSubclass ||
365 filter.mProtocol != mProtocol) {
368 if ((filter.mManufacturerName != null &&
369 mManufacturerName == null) ||
370 (filter.mManufacturerName == null &&
371 mManufacturerName != null) ||
372 (filter.mProductName != null &&
373 mProductName == null) ||
374 (filter.mProductName == null &&
375 mProductName != null) ||
376 (filter.mSerialNumber != null &&
377 mSerialNumber == null) ||
378 (filter.mSerialNumber == null &&
379 mSerialNumber != null)) {
382 if ((filter.mManufacturerName != null &&
383 mManufacturerName != null &&
384 !mManufacturerName.equals(filter.mManufacturerName)) ||
385 (filter.mProductName != null &&
386 mProductName != null &&
387 !mProductName.equals(filter.mProductName)) ||
388 (filter.mSerialNumber != null &&
389 mSerialNumber != null &&
390 !mSerialNumber.equals(filter.mSerialNumber))) {
395 if (obj instanceof UsbDevice) {
396 UsbDevice device = (UsbDevice)obj;
397 if (device.getVendorId() != mVendorId ||
398 device.getProductId() != mProductId ||
399 device.getDeviceClass() != mClass ||
400 device.getDeviceSubclass() != mSubclass ||
401 device.getDeviceProtocol() != mProtocol) {
404 if ((mManufacturerName != null && device.getManufacturerName() == null) ||
405 (mManufacturerName == null && device.getManufacturerName() != null) ||
406 (mProductName != null && device.getProductName() == null) ||
407 (mProductName == null && device.getProductName() != null) ||
408 (mSerialNumber != null && device.getSerialNumber() == null) ||
409 (mSerialNumber == null && device.getSerialNumber() != null)) {
412 if ((device.getManufacturerName() != null &&
413 !mManufacturerName.equals(device.getManufacturerName())) ||
414 (device.getProductName() != null &&
415 !mProductName.equals(device.getProductName())) ||
416 (device.getSerialNumber() != null &&
417 !mSerialNumber.equals(device.getSerialNumber()))) {
426 public int hashCode() {
427 return (((mVendorId << 16) | mProductId) ^
428 ((mClass << 16) | (mSubclass << 8) | mProtocol));
432 public String toString() {
433 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
434 ",mClass=" + mClass + ",mSubclass=" + mSubclass +
435 ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
436 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
441 // This class is used to describe a USB accessory.
442 // When used in HashMaps all values must be specified,
443 // but wildcards can be used for any of the fields in
444 // the package meta-data.
445 private static class AccessoryFilter {
446 // USB accessory manufacturer (or null for unspecified)
447 public final String mManufacturer;
448 // USB accessory model (or null for unspecified)
449 public final String mModel;
450 // USB accessory version (or null for unspecified)
451 public final String mVersion;
453 public AccessoryFilter(String manufacturer, String model, String version) {
454 mManufacturer = manufacturer;
459 public AccessoryFilter(UsbAccessory accessory) {
460 mManufacturer = accessory.getManufacturer();
461 mModel = accessory.getModel();
462 mVersion = accessory.getVersion();
465 public static AccessoryFilter read(XmlPullParser parser)
466 throws XmlPullParserException, IOException {
467 String manufacturer = null;
469 String version = null;
471 int count = parser.getAttributeCount();
472 for (int i = 0; i < count; i++) {
473 String name = parser.getAttributeName(i);
474 String value = parser.getAttributeValue(i);
476 if ("manufacturer".equals(name)) {
477 manufacturer = value;
478 } else if ("model".equals(name)) {
480 } else if ("version".equals(name)) {
484 return new AccessoryFilter(manufacturer, model, version);
487 public void write(XmlSerializer serializer)throws IOException {
488 serializer.startTag(null, "usb-accessory");
489 if (mManufacturer != null) {
490 serializer.attribute(null, "manufacturer", mManufacturer);
492 if (mModel != null) {
493 serializer.attribute(null, "model", mModel);
495 if (mVersion != null) {
496 serializer.attribute(null, "version", mVersion);
498 serializer.endTag(null, "usb-accessory");
501 public boolean matches(UsbAccessory acc) {
502 if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
503 if (mModel != null && !acc.getModel().equals(mModel)) return false;
504 if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
509 * Is the accessories described {@code accessory} covered by this filter?
511 * @param accessory A filter describing the accessory
513 * @return {@code true} iff this the filter covers the accessory
515 public boolean contains(AccessoryFilter accessory) {
516 if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
519 if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
520 if (mVersion != null && !Objects.equals(accessory.mVersion, mVersion)) return false;
525 public boolean equals(Object obj) {
526 // can't compare if we have wildcard strings
527 if (mManufacturer == null || mModel == null || mVersion == null) {
530 if (obj instanceof AccessoryFilter) {
531 AccessoryFilter filter = (AccessoryFilter)obj;
532 return (mManufacturer.equals(filter.mManufacturer) &&
533 mModel.equals(filter.mModel) &&
534 mVersion.equals(filter.mVersion));
536 if (obj instanceof UsbAccessory) {
537 UsbAccessory accessory = (UsbAccessory)obj;
538 return (mManufacturer.equals(accessory.getManufacturer()) &&
539 mModel.equals(accessory.getModel()) &&
540 mVersion.equals(accessory.getVersion()));
546 public int hashCode() {
547 return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
548 (mModel == null ? 0 : mModel.hashCode()) ^
549 (mVersion == null ? 0 : mVersion.hashCode()));
553 public String toString() {
554 return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
555 "\", mModel=\"" + mModel +
556 "\", mVersion=\"" + mVersion + "\"]";
560 private class MyPackageMonitor extends PackageMonitor {
562 public void onPackageAdded(String packageName, int uid) {
563 if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
564 UserHandle.getUserId(uid))) {
568 handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));
572 public void onPackageRemoved(String packageName, int uid) {
573 if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
574 UserHandle.getUserId(uid))) {
578 clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
582 MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
584 private final MtpNotificationManager mMtpNotificationManager;
587 * Create new settings manager for a profile group.
589 * @param context The context of the service
590 * @param user The parent profile
591 * @param settingsManager The settings manager of the service
593 UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
594 @NonNull UsbSettingsManager settingsManager) {
595 if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
597 Context parentUserContext;
599 parentUserContext = context.createPackageContextAsUser("android", 0, user);
600 } catch (NameNotFoundException e) {
601 throw new RuntimeException("Missing android package");
605 mPackageManager = context.getPackageManager();
606 mSettingsManager = settingsManager;
607 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
610 mSettingsFile = new AtomicFile(new File(
611 Environment.getUserSystemDirectory(user.getIdentifier()),
612 "usb_device_manager.xml"));
614 mDisablePermissionDialogs = context.getResources().getBoolean(
615 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
617 synchronized (mLock) {
618 if (UserHandle.SYSTEM.equals(user)) {
619 upgradeSingleUserLocked();
621 readSettingsLocked();
624 mPackageMonitor.register(context, null, UserHandle.ALL, true);
625 mMtpNotificationManager = new MtpNotificationManager(
627 new MtpNotificationManager.OnOpenInAppListener() {
629 public void onOpenInApp(UsbDevice device) {
630 resolveActivity(createDeviceAttachedIntent(device),
631 device, false /* showMtpNotification */);
637 * Remove all defaults for a user.
639 * @param userToRemove The user the defaults belong to.
641 void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
642 synchronized (mLock) {
643 boolean needToPersist = false;
644 Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
645 .entrySet().iterator();
646 while (devicePreferenceIt.hasNext()) {
647 Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next();
649 if (entry.getValue().user.equals(userToRemove)) {
650 devicePreferenceIt.remove();
651 needToPersist = true;
655 Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
656 mAccessoryPreferenceMap.entrySet().iterator();
657 while (accessoryPreferenceIt.hasNext()) {
658 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
660 if (entry.getValue().user.equals(userToRemove)) {
661 accessoryPreferenceIt.remove();
662 needToPersist = true;
667 scheduleWriteSettingsLocked();
672 private void readPreference(XmlPullParser parser)
673 throws XmlPullParserException, IOException {
674 String packageName = null;
676 // If not set, assume it to be the parent profile
677 UserHandle user = mParentUser;
679 int count = parser.getAttributeCount();
680 for (int i = 0; i < count; i++) {
681 if ("package".equals(parser.getAttributeName(i))) {
682 packageName = parser.getAttributeValue(i);
684 if ("user".equals(parser.getAttributeName(i))) {
685 // Might return null if user is not known anymore
687 .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
691 XmlUtils.nextElement(parser);
692 if ("usb-device".equals(parser.getName())) {
693 DeviceFilter filter = DeviceFilter.read(parser);
695 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
697 } else if ("usb-accessory".equals(parser.getName())) {
698 AccessoryFilter filter = AccessoryFilter.read(parser);
700 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
703 XmlUtils.nextElement(parser);
707 * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
708 * Should only by called by owner.
710 private void upgradeSingleUserLocked() {
711 if (sSingleUserSettingsFile.exists()) {
712 mDevicePreferenceMap.clear();
713 mAccessoryPreferenceMap.clear();
715 FileInputStream fis = null;
717 fis = new FileInputStream(sSingleUserSettingsFile);
718 XmlPullParser parser = Xml.newPullParser();
719 parser.setInput(fis, StandardCharsets.UTF_8.name());
721 XmlUtils.nextElement(parser);
722 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
723 final String tagName = parser.getName();
724 if ("preference".equals(tagName)) {
725 readPreference(parser);
727 XmlUtils.nextElement(parser);
730 } catch (IOException e) {
731 Log.wtf(TAG, "Failed to read single-user settings", e);
732 } catch (XmlPullParserException e) {
733 Log.wtf(TAG, "Failed to read single-user settings", e);
735 IoUtils.closeQuietly(fis);
738 scheduleWriteSettingsLocked();
740 // Success or failure, we delete single-user file
741 sSingleUserSettingsFile.delete();
745 private void readSettingsLocked() {
746 if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
748 mDevicePreferenceMap.clear();
749 mAccessoryPreferenceMap.clear();
751 FileInputStream stream = null;
753 stream = mSettingsFile.openRead();
754 XmlPullParser parser = Xml.newPullParser();
755 parser.setInput(stream, StandardCharsets.UTF_8.name());
757 XmlUtils.nextElement(parser);
758 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
759 String tagName = parser.getName();
760 if ("preference".equals(tagName)) {
761 readPreference(parser);
763 XmlUtils.nextElement(parser);
766 } catch (FileNotFoundException e) {
767 if (DEBUG) Slog.d(TAG, "settings file not found");
768 } catch (Exception e) {
769 Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
770 mSettingsFile.delete();
772 IoUtils.closeQuietly(stream);
777 * Schedule a async task to persist {@link #mDevicePreferenceMap} and
778 * {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do
779 * nothing as the currently scheduled one will do the work.
780 * <p>Called with {@link #mLock} held.</p>
781 * <p>In the uncommon case that the system crashes in between the scheduling and the write the
782 * update is lost.</p>
784 private void scheduleWriteSettingsLocked() {
785 if (mIsWriteSettingsScheduled) {
788 mIsWriteSettingsScheduled = true;
791 AsyncTask.execute(() -> {
792 synchronized (mLock) {
793 FileOutputStream fos = null;
795 fos = mSettingsFile.startWrite();
797 FastXmlSerializer serializer = new FastXmlSerializer();
798 serializer.setOutput(fos, StandardCharsets.UTF_8.name());
799 serializer.startDocument(null, true);
800 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
802 serializer.startTag(null, "settings");
804 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
805 serializer.startTag(null, "preference");
806 serializer.attribute(null, "package",
807 mDevicePreferenceMap.get(filter).packageName);
808 serializer.attribute(null, "user",
809 String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
810 filter.write(serializer);
811 serializer.endTag(null, "preference");
814 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
815 serializer.startTag(null, "preference");
816 serializer.attribute(null, "package",
817 mAccessoryPreferenceMap.get(filter).packageName);
818 serializer.attribute(null, "user", String.valueOf(
819 getSerial(mAccessoryPreferenceMap.get(filter).user)));
820 filter.write(serializer);
821 serializer.endTag(null, "preference");
824 serializer.endTag(null, "settings");
825 serializer.endDocument();
827 mSettingsFile.finishWrite(fos);
828 } catch (IOException e) {
829 Slog.e(TAG, "Failed to write settings", e);
831 mSettingsFile.failWrite(fos);
835 mIsWriteSettingsScheduled = false;
840 // Checks to see if a package matches a device or accessory.
841 // Only one of device and accessory should be non-null.
842 private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
843 UsbDevice device, UsbAccessory accessory) {
844 if (info.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
848 ActivityInfo ai = info.activityInfo;
850 XmlResourceParser parser = null;
852 parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
853 if (parser == null) {
854 Slog.w(TAG, "no meta-data for " + info);
858 XmlUtils.nextElement(parser);
859 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
860 String tagName = parser.getName();
861 if (device != null && "usb-device".equals(tagName)) {
862 DeviceFilter filter = DeviceFilter.read(parser);
863 if (filter.matches(device)) {
867 else if (accessory != null && "usb-accessory".equals(tagName)) {
868 AccessoryFilter filter = AccessoryFilter.read(parser);
869 if (filter.matches(accessory)) {
873 XmlUtils.nextElement(parser);
875 } catch (Exception e) {
876 Slog.w(TAG, "Unable to load component info " + info.toString(), e);
878 if (parser != null) parser.close();
884 * Resolve all activities that match an intent for all profiles of this group.
886 * @param intent The intent to resolve
888 * @return The {@link ResolveInfo}s for all profiles of the group
890 private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
891 @NonNull Intent intent) {
892 List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
894 ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
895 int numProfiles = profiles.size();
896 for (int i = 0; i < numProfiles; i++) {
897 resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent,
898 PackageManager.GET_META_DATA, profiles.get(i).id));
905 * Only return those matches with the highest priority.
907 * @param matches All matches, some might have lower priority
909 * @return The matches with the highest priority
912 private ArrayList<ResolveInfo> preferHighPriority(
913 @NonNull ArrayList<ResolveInfo> matches) {
914 SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
915 SparseIntArray highestPriorityByUserId = new SparseIntArray();
917 // Create list of highest priority matches per user in highestPriorityMatchesByUserId
918 int numMatches = matches.size();
919 for (int matchNum = 0; matchNum < numMatches; matchNum++) {
920 ResolveInfo match = matches.get(matchNum);
922 // If this a previously unknown user?
923 if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) {
924 highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE);
925 highestPriorityMatchesByUserId.put(match.targetUserId, new ArrayList<>());
928 // Find current highest priority matches for the current user
929 int highestPriority = highestPriorityByUserId.get(match.targetUserId);
930 ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get(
933 if (match.priority == highestPriority) {
934 highestPriorityMatches.add(match);
935 } else if (match.priority > highestPriority) {
936 highestPriorityByUserId.put(match.targetUserId, match.priority);
938 highestPriorityMatches.clear();
939 highestPriorityMatches.add(match);
943 // Combine all users back together. This means that all matches have the same priority for a
944 // user. Matches for different users might have different priority.
945 ArrayList<ResolveInfo> combinedMatches = new ArrayList<>();
946 int numMatchArrays = highestPriorityMatchesByUserId.size();
947 for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) {
948 combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum));
951 return combinedMatches;
954 private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
955 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
956 List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
957 int count = resolveInfos.size();
958 for (int i = 0; i < count; i++) {
959 ResolveInfo resolveInfo = resolveInfos.get(i);
960 if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
961 matches.add(resolveInfo);
964 return preferHighPriority(matches);
967 private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
968 UsbAccessory accessory, Intent intent) {
969 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
970 List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
971 int count = resolveInfos.size();
972 for (int i = 0; i < count; i++) {
973 ResolveInfo resolveInfo = resolveInfos.get(i);
974 if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
975 matches.add(resolveInfo);
978 return preferHighPriority(matches);
981 public void deviceAttached(UsbDevice device) {
982 final Intent intent = createDeviceAttachedIntent(device);
984 // Send broadcast to running activities with registered intent
985 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
987 resolveActivity(intent, device, true /* showMtpNotification */);
990 private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
991 final ArrayList<ResolveInfo> matches;
992 final ActivityInfo defaultActivity;
993 synchronized (mLock) {
994 matches = getDeviceMatchesLocked(device, intent);
995 defaultActivity = getDefaultActivityLocked(
996 matches, mDevicePreferenceMap.get(new DeviceFilter(device)));
999 if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
1000 mPackageManager, device) && defaultActivity == null) {
1001 // Show notification if the device is MTP storage.
1002 mMtpNotificationManager.showNotification(device);
1006 // Start activity with registered intent
1007 resolveActivity(intent, matches, defaultActivity, device, null);
1010 public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
1011 final Intent intent = createDeviceAttachedIntent(device);
1013 // Send broadcast to running activity with registered intent
1014 mContext.sendBroadcast(intent);
1016 ApplicationInfo appInfo;
1018 // Fixed handlers are always for parent user
1019 appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0,
1020 mParentUser.getIdentifier());
1021 } catch (NameNotFoundException e) {
1022 Slog.e(TAG, "Default USB handling package (" + component.getPackageName()
1023 + ") not found for user " + mParentUser);
1027 mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
1028 .grantDevicePermission(device, appInfo.uid);
1030 Intent activityIntent = new Intent(intent);
1031 activityIntent.setComponent(component);
1033 mContext.startActivityAsUser(activityIntent, mParentUser);
1034 } catch (ActivityNotFoundException e) {
1035 Slog.e(TAG, "unable to start activity " + activityIntent);
1040 * Remove notifications for a usb device.
1042 * @param device The device the notifications are for.
1044 void usbDeviceRemoved(@NonNull UsbDevice device) {
1045 mMtpNotificationManager.hideNotification(device.getDeviceId());
1048 public void accessoryAttached(UsbAccessory accessory) {
1049 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
1050 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1052 Intent.FLAG_ACTIVITY_NEW_TASK |
1053 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1055 final ArrayList<ResolveInfo> matches;
1056 final ActivityInfo defaultActivity;
1057 synchronized (mLock) {
1058 matches = getAccessoryMatchesLocked(accessory, intent);
1059 defaultActivity = getDefaultActivityLocked(
1060 matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
1063 resolveActivity(intent, matches, defaultActivity, null, accessory);
1067 * Start the appropriate package when an device/accessory got attached.
1069 * @param intent The intent to start the package
1070 * @param rawMatches The available resolutions of the intent
1071 * @param defaultActivity The default activity for the device (if set)
1072 * @param device The device if a device was attached
1073 * @param accessory The accessory if a device was attached
1075 private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> rawMatches,
1076 @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
1077 @Nullable UsbAccessory accessory) {
1078 final int numRawMatches = rawMatches.size();
1080 // The raw matches contain the activities that can be started but also the intents to switch
1081 // between the profiles
1082 int numParentActivityMatches = 0;
1083 int numNonParentActivityMatches = 0;
1084 for (int i = 0; i < numRawMatches; i++) {
1085 final ResolveInfo rawMatch = rawMatches.get(i);
1086 if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
1087 if (UserHandle.getUserHandleForUid(
1088 rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
1089 numParentActivityMatches++;
1091 numNonParentActivityMatches++;
1096 // don't show the resolver activity if there are no choices available
1097 if (numParentActivityMatches + numNonParentActivityMatches == 0) {
1098 if (accessory != null) {
1099 String uri = accessory.getUri();
1100 if (uri != null && uri.length() > 0) {
1101 // display URI to user
1102 Intent dialogIntent = new Intent();
1103 dialogIntent.setClassName("com.android.systemui",
1104 "com.android.systemui.usb.UsbAccessoryUriActivity");
1105 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1106 dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1107 dialogIntent.putExtra("uri", uri);
1109 mContext.startActivityAsUser(dialogIntent, mParentUser);
1110 } catch (ActivityNotFoundException e) {
1111 Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
1120 // If only one profile has activity matches, we need to remove all switch intents
1121 ArrayList<ResolveInfo> matches;
1122 if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
1123 matches = new ArrayList<>(numParentActivityMatches + numNonParentActivityMatches);
1125 for (int i = 0; i < numRawMatches; i++) {
1126 ResolveInfo rawMatch = rawMatches.get(i);
1127 if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
1128 matches.add(rawMatch);
1132 matches = rawMatches;
1135 if (defaultActivity != null) {
1136 UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
1137 UserHandle.getUserId(defaultActivity.applicationInfo.uid));
1138 // grant permission for default activity
1139 if (device != null) {
1140 defaultRIUserSettings.
1141 grantDevicePermission(device, defaultActivity.applicationInfo.uid);
1142 } else if (accessory != null) {
1143 defaultRIUserSettings.grantAccessoryPermission(accessory,
1144 defaultActivity.applicationInfo.uid);
1147 // start default activity directly
1149 intent.setComponent(
1150 new ComponentName(defaultActivity.packageName, defaultActivity.name));
1152 UserHandle user = UserHandle.getUserHandleForUid(
1153 defaultActivity.applicationInfo.uid);
1154 mContext.startActivityAsUser(intent, user);
1155 } catch (ActivityNotFoundException e) {
1156 Slog.e(TAG, "startActivity failed", e);
1159 Intent resolverIntent = new Intent();
1160 resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1163 if (matches.size() == 1) {
1164 ResolveInfo rInfo = matches.get(0);
1166 // start UsbConfirmActivity if there is only one choice
1167 resolverIntent.setClassName("com.android.systemui",
1168 "com.android.systemui.usb.UsbConfirmActivity");
1169 resolverIntent.putExtra("rinfo", rInfo);
1170 user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
1172 if (device != null) {
1173 resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
1175 resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1180 // start UsbResolverActivity so user can choose an activity
1181 resolverIntent.setClassName("com.android.systemui",
1182 "com.android.systemui.usb.UsbResolverActivity");
1183 resolverIntent.putParcelableArrayListExtra("rlist", matches);
1184 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
1187 mContext.startActivityAsUser(resolverIntent, user);
1188 } catch (ActivityNotFoundException e) {
1189 Slog.e(TAG, "unable to start activity " + resolverIntent, e);
1195 * Returns a default activity for matched ResolveInfo.
1196 * @param matches Resolved activities matched with connected device/accesary.
1197 * @param userPackage Default activity choosed by a user before. Should be null if no activity
1198 * is choosed by a user.
1199 * @return Default activity
1201 private @Nullable ActivityInfo getDefaultActivityLocked(
1202 @NonNull ArrayList<ResolveInfo> matches,
1203 @Nullable UserPackage userPackage) {
1204 if (userPackage != null) {
1205 // look for default activity
1206 for (final ResolveInfo info : matches) {
1207 if (info.activityInfo != null && userPackage.equals(
1208 new UserPackage(info.activityInfo.packageName,
1209 UserHandle.getUserHandleForUid(
1210 info.activityInfo.applicationInfo.uid)))) {
1211 return info.activityInfo;
1216 if (matches.size() == 1) {
1217 final ActivityInfo activityInfo = matches.get(0).activityInfo;
1218 if (activityInfo != null) {
1219 // bypass dialog and launch the only matching activity
1220 if (mDisablePermissionDialogs) {
1221 return activityInfo;
1223 if (activityInfo.applicationInfo != null
1224 && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
1226 return activityInfo;
1234 private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1235 @NonNull DeviceFilter filter) {
1236 ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
1238 // The keys in mDevicePreferenceMap are filters that match devices very narrowly
1239 for (DeviceFilter device : mDevicePreferenceMap.keySet()) {
1240 if (filter.contains(device)) {
1241 UserPackage currentMatch = mDevicePreferenceMap.get(device);
1242 if (!currentMatch.equals(userPackage)) {
1243 keysToRemove.add(device);
1248 if (!keysToRemove.isEmpty()) {
1249 for (DeviceFilter keyToRemove : keysToRemove) {
1250 mDevicePreferenceMap.remove(keyToRemove);
1254 return !keysToRemove.isEmpty();
1257 private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1258 @NonNull AccessoryFilter filter) {
1259 ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
1261 // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly
1262 for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) {
1263 if (filter.contains(accessory)) {
1264 UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory);
1265 if (!currentMatch.equals(userPackage)) {
1266 keysToRemove.add(accessory);
1271 if (!keysToRemove.isEmpty()) {
1272 for (AccessoryFilter keyToRemove : keysToRemove) {
1273 mAccessoryPreferenceMap.remove(keyToRemove);
1277 return !keysToRemove.isEmpty();
1280 private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
1281 String metaDataName) {
1282 XmlResourceParser parser = null;
1283 boolean changed = false;
1286 parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
1287 if (parser == null) return false;
1289 XmlUtils.nextElement(parser);
1290 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
1291 String tagName = parser.getName();
1292 if ("usb-device".equals(tagName)) {
1293 DeviceFilter filter = DeviceFilter.read(parser);
1294 if (clearCompatibleMatchesLocked(userPackage, filter)) {
1298 else if ("usb-accessory".equals(tagName)) {
1299 AccessoryFilter filter = AccessoryFilter.read(parser);
1300 if (clearCompatibleMatchesLocked(userPackage, filter)) {
1304 XmlUtils.nextElement(parser);
1306 } catch (Exception e) {
1307 Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
1309 if (parser != null) parser.close();
1314 // Check to see if the package supports any USB devices or accessories.
1315 // If so, clear any preferences for matching devices/accessories.
1316 private void handlePackageAdded(@NonNull UserPackage userPackage) {
1317 synchronized (mLock) {
1319 boolean changed = false;
1322 info = mPackageManager.getPackageInfoAsUser(userPackage.packageName,
1323 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
1324 userPackage.user.getIdentifier());
1325 } catch (NameNotFoundException e) {
1326 Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e);
1330 ActivityInfo[] activities = info.activities;
1331 if (activities == null) return;
1332 for (int i = 0; i < activities.length; i++) {
1333 // check for meta-data, both for devices and accessories
1334 if (handlePackageAddedLocked(userPackage, activities[i],
1335 UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
1339 if (handlePackageAddedLocked(userPackage, activities[i],
1340 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
1346 scheduleWriteSettingsLocked();
1352 * Get the serial number for a user handle.
1354 * @param user The user handle
1356 * @return The serial number
1358 private int getSerial(@NonNull UserHandle user) {
1359 return mUserManager.getUserSerialNumber(user.getIdentifier());
1363 * Set a package as default handler for a device.
1365 * @param device The device that should be handled by default
1366 * @param packageName The default handler package
1367 * @param user The user the package belongs to
1369 void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
1370 @NonNull UserHandle user) {
1371 DeviceFilter filter = new DeviceFilter(device);
1372 boolean changed = false;
1373 synchronized (mLock) {
1374 if (packageName == null) {
1375 changed = (mDevicePreferenceMap.remove(filter) != null);
1377 UserPackage userPackage = new UserPackage(packageName, user);
1379 changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
1381 mDevicePreferenceMap.put(filter, userPackage);
1385 scheduleWriteSettingsLocked();
1391 * Set a package as default handler for a accessory.
1393 * @param accessory The accessory that should be handled by default
1394 * @param packageName The default handler package
1395 * @param user The user the package belongs to
1397 void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName,
1398 @NonNull UserHandle user) {
1399 AccessoryFilter filter = new AccessoryFilter(accessory);
1400 boolean changed = false;
1401 synchronized (mLock) {
1402 if (packageName == null) {
1403 changed = (mAccessoryPreferenceMap.remove(filter) != null);
1405 UserPackage userPackage = new UserPackage(packageName, user);
1407 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));
1409 mAccessoryPreferenceMap.put(filter, userPackage);
1413 scheduleWriteSettingsLocked();
1419 * Check if a package has is the default handler for any usb device or accessory.
1421 * @param packageName The package name
1422 * @param user The user the package belongs to
1424 * @return {@code true} iff the package is default for any usb device or accessory
1426 boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1427 UserPackage userPackage = new UserPackage(packageName, user);
1428 synchronized (mLock) {
1429 if (mDevicePreferenceMap.values().contains(userPackage)) return true;
1430 if (mAccessoryPreferenceMap.values().contains(userPackage)) return true;
1436 * Clear defaults for a package from any preference.
1438 * @param packageName The package to remove
1439 * @param user The user the package belongs to
1441 void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1442 UserPackage userPackage = new UserPackage(packageName, user);
1444 synchronized (mLock) {
1445 if (clearPackageDefaultsLocked(userPackage)) {
1446 scheduleWriteSettingsLocked();
1452 * Clear defaults for a package from any preference (does not persist).
1454 * @param userPackage The package to remove
1456 * @return {@code true} iff at least one preference was cleared
1458 private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) {
1459 boolean cleared = false;
1460 synchronized (mLock) {
1461 if (mDevicePreferenceMap.containsValue(userPackage)) {
1462 // make a copy of the key set to avoid ConcurrentModificationException
1463 Object[] keys = mDevicePreferenceMap.keySet().toArray();
1464 for (int i = 0; i < keys.length; i++) {
1465 Object key = keys[i];
1466 if (userPackage.equals(mDevicePreferenceMap.get(key))) {
1467 mDevicePreferenceMap.remove(key);
1472 if (mAccessoryPreferenceMap.containsValue(userPackage)) {
1473 // make a copy of the key set to avoid ConcurrentModificationException
1474 Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
1475 for (int i = 0; i < keys.length; i++) {
1476 Object key = keys[i];
1477 if (userPackage.equals(mAccessoryPreferenceMap.get(key))) {
1478 mAccessoryPreferenceMap.remove(key);
1487 public void dump(IndentingPrintWriter pw) {
1488 synchronized (mLock) {
1489 pw.println("Device preferences:");
1490 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1491 pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter));
1493 pw.println("Accessory preferences:");
1494 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1495 pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter));
1500 private static Intent createDeviceAttachedIntent(UsbDevice device) {
1501 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1502 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
1504 Intent.FLAG_ACTIVITY_NEW_TASK |
1505 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);