OSDN Git Service

Fixes for handling users + default for USB
[android-x86/frameworks-base.git] / services / usb / java / com / android / server / usb / UsbProfileGroupSettingsManager.java
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.server.usb;
18
19 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
20
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;
49
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;
56
57 import libcore.io.IoUtils;
58
59 import org.xmlpull.v1.XmlPullParser;
60 import org.xmlpull.v1.XmlPullParserException;
61 import org.xmlpull.v1.XmlSerializer;
62
63 import java.io.File;
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;
73 import java.util.Map;
74 import java.util.Objects;
75
76 class UsbProfileGroupSettingsManager {
77     private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
78     private static final boolean DEBUG = false;
79
80     /** Legacy settings file, before multi-user */
81     private static final File sSingleUserSettingsFile = new File(
82             "/data/system/usb_device_manager.xml");
83
84     /** The parent user (main user of the profile group) */
85     private final UserHandle mParentUser;
86
87     private final AtomicFile mSettingsFile;
88     private final boolean mDisablePermissionDialogs;
89
90     private final Context mContext;
91
92     private final PackageManager mPackageManager;
93
94     private final UserManager mUserManager;
95     private final @NonNull UsbSettingsManager mSettingsManager;
96
97     /** Maps DeviceFilter to user preferred application package */
98     @GuardedBy("mLock")
99     private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
100
101     /** Maps AccessoryFilter to user preferred application package */
102     @GuardedBy("mLock")
103     private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
104
105     private final Object mLock = new Object();
106
107     /**
108      * If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently
109      * scheduled.
110      */
111     @GuardedBy("mLock")
112     private boolean mIsWriteSettingsScheduled;
113
114     /**
115      * A package of a user.
116      */
117     @Immutable
118     private static class UserPackage {
119         /** User */
120         final @NonNull UserHandle user;
121
122         /** Package name */
123         final @NonNull String packageName;
124
125         /**
126          * Create a description of a per user package.
127          *
128          * @param packageName The name of the package
129          * @param user The user
130          */
131         private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
132             this.packageName = packageName;
133             this.user = user;
134         }
135
136         @Override
137         public boolean equals(Object obj) {
138             if (!(obj instanceof UserPackage)) {
139                 return false;
140             } else {
141                 UserPackage other = (UserPackage)obj;
142
143                 return user.equals(other.user) && packageName.equals(other.packageName);
144             }
145         }
146
147         @Override
148         public int hashCode() {
149             int result = user.hashCode();
150             result = 31 * result + packageName.hashCode();
151             return result;
152         }
153
154         @Override
155         public String toString() {
156             return user.getIdentifier() + "/" + packageName;
157         }
158     }
159
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;
181
182         public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
183                             String manufacturer, String product, String serialnum) {
184             mVendorId = vid;
185             mProductId = pid;
186             mClass = clasz;
187             mSubclass = subclass;
188             mProtocol = protocol;
189             mManufacturerName = manufacturer;
190             mProductName = product;
191             mSerialNumber = serialnum;
192         }
193
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();
203         }
204
205         public static DeviceFilter read(XmlPullParser parser)
206                 throws XmlPullParserException, IOException {
207             int vendorId = -1;
208             int productId = -1;
209             int deviceClass = -1;
210             int deviceSubclass = -1;
211             int deviceProtocol = -1;
212             String manufacturerName = null;
213             String productName = null;
214             String serialNumber = null;
215
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)) {
224                     productName = value;
225                 } else if ("serial-number".equals(name)) {
226                     serialNumber = value;
227                 } else {
228                     int intValue = -1;
229                     int radix = 10;
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
233                         radix = 16;
234                         value = value.substring(2);
235                     }
236                     try {
237                         intValue = Integer.parseInt(value, radix);
238                     } catch (NumberFormatException e) {
239                         Slog.e(TAG, "invalid number for field " + name, e);
240                         continue;
241                     }
242                     if ("vendor-id".equals(name)) {
243                         vendorId = intValue;
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;
252                     }
253                 }
254             }
255             return new DeviceFilter(vendorId, productId,
256                     deviceClass, deviceSubclass, deviceProtocol,
257                     manufacturerName, productName, serialNumber);
258         }
259
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));
264             }
265             if (mProductId != -1) {
266                 serializer.attribute(null, "product-id", Integer.toString(mProductId));
267             }
268             if (mClass != -1) {
269                 serializer.attribute(null, "class", Integer.toString(mClass));
270             }
271             if (mSubclass != -1) {
272                 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
273             }
274             if (mProtocol != -1) {
275                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
276             }
277             if (mManufacturerName != null) {
278                 serializer.attribute(null, "manufacturer-name", mManufacturerName);
279             }
280             if (mProductName != null) {
281                 serializer.attribute(null, "product-name", mProductName);
282             }
283             if (mSerialNumber != null) {
284                 serializer.attribute(null, "serial-number", mSerialNumber);
285             }
286             serializer.endTag(null, "usb-device");
287         }
288
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));
293         }
294
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;
307
308             // check device class/subclass/protocol
309             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
310                     device.getDeviceProtocol())) return true;
311
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;
318             }
319
320             return false;
321         }
322
323         /**
324          * If the device described by {@code device} covered by this filter?
325          *
326          * @param device The device
327          *
328          * @return {@code true} iff this filter covers the {@code device}
329          */
330         public boolean contains(DeviceFilter device) {
331             // -1 and null means "match anything"
332
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)) {
337                 return false;
338             }
339             if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
340                 return false;
341             }
342             if (mSerialNumber != null
343                     && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
344                 return false;
345             }
346
347             // check device class/subclass/protocol
348             return matches(device.mClass, device.mSubclass, device.mProtocol);
349         }
350
351         @Override
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) {
356                 return false;
357             }
358             if (obj instanceof DeviceFilter) {
359                 DeviceFilter filter = (DeviceFilter)obj;
360
361                 if (filter.mVendorId != mVendorId ||
362                         filter.mProductId != mProductId ||
363                         filter.mClass != mClass ||
364                         filter.mSubclass != mSubclass ||
365                         filter.mProtocol != mProtocol) {
366                     return(false);
367                 }
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)) {
380                     return(false);
381                 }
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))) {
391                     return(false);
392                 }
393                 return(true);
394             }
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) {
402                     return(false);
403                 }
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)) {
410                     return(false);
411                 }
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()))) {
418                     return(false);
419                 }
420                 return true;
421             }
422             return false;
423         }
424
425         @Override
426         public int hashCode() {
427             return (((mVendorId << 16) | mProductId) ^
428                     ((mClass << 16) | (mSubclass << 8) | mProtocol));
429         }
430
431         @Override
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 +
437                     "]";
438         }
439     }
440
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;
452
453         public AccessoryFilter(String manufacturer, String model, String version) {
454             mManufacturer = manufacturer;
455             mModel = model;
456             mVersion = version;
457         }
458
459         public AccessoryFilter(UsbAccessory accessory) {
460             mManufacturer = accessory.getManufacturer();
461             mModel = accessory.getModel();
462             mVersion = accessory.getVersion();
463         }
464
465         public static AccessoryFilter read(XmlPullParser parser)
466                 throws XmlPullParserException, IOException {
467             String manufacturer = null;
468             String model = null;
469             String version = null;
470
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);
475
476                 if ("manufacturer".equals(name)) {
477                     manufacturer = value;
478                 } else if ("model".equals(name)) {
479                     model = value;
480                 } else if ("version".equals(name)) {
481                     version = value;
482                 }
483              }
484              return new AccessoryFilter(manufacturer, model, version);
485         }
486
487         public void write(XmlSerializer serializer)throws IOException {
488             serializer.startTag(null, "usb-accessory");
489             if (mManufacturer != null) {
490                 serializer.attribute(null, "manufacturer", mManufacturer);
491             }
492             if (mModel != null) {
493                 serializer.attribute(null, "model", mModel);
494             }
495             if (mVersion != null) {
496                 serializer.attribute(null, "version", mVersion);
497             }
498             serializer.endTag(null, "usb-accessory");
499         }
500
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;
505             return true;
506         }
507
508         /**
509          * Is the accessories described {@code accessory} covered by this filter?
510          *
511          * @param accessory A filter describing the accessory
512          *
513          * @return {@code true} iff this the filter covers the accessory
514          */
515         public boolean contains(AccessoryFilter accessory) {
516             if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
517                 return false;
518             }
519             if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
520             if (mVersion != null && !Objects.equals(accessory.mVersion, mVersion)) return false;
521             return true;
522         }
523
524         @Override
525         public boolean equals(Object obj) {
526             // can't compare if we have wildcard strings
527             if (mManufacturer == null || mModel == null || mVersion == null) {
528                 return false;
529             }
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));
535             }
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()));
541             }
542             return false;
543         }
544
545         @Override
546         public int hashCode() {
547             return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
548                     (mModel == null ? 0 : mModel.hashCode()) ^
549                     (mVersion == null ? 0 : mVersion.hashCode()));
550         }
551
552         @Override
553         public String toString() {
554             return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
555                                 "\", mModel=\"" + mModel +
556                                 "\", mVersion=\"" + mVersion + "\"]";
557         }
558     }
559
560     private class MyPackageMonitor extends PackageMonitor {
561         @Override
562         public void onPackageAdded(String packageName, int uid) {
563             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
564                     UserHandle.getUserId(uid))) {
565                 return;
566             }
567
568             handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));
569         }
570
571         @Override
572         public void onPackageRemoved(String packageName, int uid) {
573             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
574                     UserHandle.getUserId(uid))) {
575                 return;
576             }
577
578             clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
579         }
580     }
581
582     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
583
584     private final MtpNotificationManager mMtpNotificationManager;
585
586     /**
587      * Create new settings manager for a profile group.
588      *
589      * @param context The context of the service
590      * @param user The parent profile
591      * @param settingsManager The settings manager of the service
592      */
593     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
594             @NonNull UsbSettingsManager settingsManager) {
595         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
596
597         Context parentUserContext;
598         try {
599             parentUserContext = context.createPackageContextAsUser("android", 0, user);
600         } catch (NameNotFoundException e) {
601             throw new RuntimeException("Missing android package");
602         }
603
604         mContext = context;
605         mPackageManager = context.getPackageManager();
606         mSettingsManager = settingsManager;
607         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
608
609         mParentUser = user;
610         mSettingsFile = new AtomicFile(new File(
611                 Environment.getUserSystemDirectory(user.getIdentifier()),
612                 "usb_device_manager.xml"));
613
614         mDisablePermissionDialogs = context.getResources().getBoolean(
615                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
616
617         synchronized (mLock) {
618             if (UserHandle.SYSTEM.equals(user)) {
619                 upgradeSingleUserLocked();
620             }
621             readSettingsLocked();
622         }
623
624         mPackageMonitor.register(context, null, UserHandle.ALL, true);
625         mMtpNotificationManager = new MtpNotificationManager(
626                 parentUserContext,
627                 new MtpNotificationManager.OnOpenInAppListener() {
628                     @Override
629                     public void onOpenInApp(UsbDevice device) {
630                         resolveActivity(createDeviceAttachedIntent(device),
631                                 device, false /* showMtpNotification */);
632                     }
633                 });
634     }
635
636     /**
637      * Remove all defaults for a user.
638      *
639      * @param userToRemove The user the defaults belong to.
640      */
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();
648
649                 if (entry.getValue().user.equals(userToRemove)) {
650                     devicePreferenceIt.remove();
651                     needToPersist = true;
652                 }
653             }
654
655             Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
656                     mAccessoryPreferenceMap.entrySet().iterator();
657             while (accessoryPreferenceIt.hasNext()) {
658                 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
659
660                 if (entry.getValue().user.equals(userToRemove)) {
661                     accessoryPreferenceIt.remove();
662                     needToPersist = true;
663                 }
664             }
665
666             if (needToPersist) {
667                 scheduleWriteSettingsLocked();
668             }
669         }
670     }
671
672     private void readPreference(XmlPullParser parser)
673             throws XmlPullParserException, IOException {
674         String packageName = null;
675
676         // If not set, assume it to be the parent profile
677         UserHandle user = mParentUser;
678
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);
683             }
684             if ("user".equals(parser.getAttributeName(i))) {
685                 // Might return null if user is not known anymore
686                 user = mUserManager
687                         .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
688             }
689         }
690
691         XmlUtils.nextElement(parser);
692         if ("usb-device".equals(parser.getName())) {
693             DeviceFilter filter = DeviceFilter.read(parser);
694             if (user != null) {
695                 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
696             }
697         } else if ("usb-accessory".equals(parser.getName())) {
698             AccessoryFilter filter = AccessoryFilter.read(parser);
699             if (user != null) {
700                 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
701             }
702         }
703         XmlUtils.nextElement(parser);
704     }
705
706     /**
707      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
708      * Should only by called by owner.
709      */
710     private void upgradeSingleUserLocked() {
711         if (sSingleUserSettingsFile.exists()) {
712             mDevicePreferenceMap.clear();
713             mAccessoryPreferenceMap.clear();
714
715             FileInputStream fis = null;
716             try {
717                 fis = new FileInputStream(sSingleUserSettingsFile);
718                 XmlPullParser parser = Xml.newPullParser();
719                 parser.setInput(fis, StandardCharsets.UTF_8.name());
720
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);
726                     } else {
727                         XmlUtils.nextElement(parser);
728                     }
729                 }
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);
734             } finally {
735                 IoUtils.closeQuietly(fis);
736             }
737
738             scheduleWriteSettingsLocked();
739
740             // Success or failure, we delete single-user file
741             sSingleUserSettingsFile.delete();
742         }
743     }
744
745     private void readSettingsLocked() {
746         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
747
748         mDevicePreferenceMap.clear();
749         mAccessoryPreferenceMap.clear();
750
751         FileInputStream stream = null;
752         try {
753             stream = mSettingsFile.openRead();
754             XmlPullParser parser = Xml.newPullParser();
755             parser.setInput(stream, StandardCharsets.UTF_8.name());
756
757             XmlUtils.nextElement(parser);
758             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
759                 String tagName = parser.getName();
760                 if ("preference".equals(tagName)) {
761                     readPreference(parser);
762                 } else {
763                     XmlUtils.nextElement(parser);
764                 }
765             }
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();
771         } finally {
772             IoUtils.closeQuietly(stream);
773         }
774     }
775
776     /**
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>
783      */
784     private void scheduleWriteSettingsLocked() {
785         if (mIsWriteSettingsScheduled) {
786             return;
787         } else {
788             mIsWriteSettingsScheduled = true;
789         }
790
791         AsyncTask.execute(() -> {
792             synchronized (mLock) {
793                 FileOutputStream fos = null;
794                 try {
795                     fos = mSettingsFile.startWrite();
796
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",
801                                     true);
802                     serializer.startTag(null, "settings");
803
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");
812                     }
813
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");
822                     }
823
824                     serializer.endTag(null, "settings");
825                     serializer.endDocument();
826
827                     mSettingsFile.finishWrite(fos);
828                 } catch (IOException e) {
829                     Slog.e(TAG, "Failed to write settings", e);
830                     if (fos != null) {
831                         mSettingsFile.failWrite(fos);
832                     }
833                 }
834
835                 mIsWriteSettingsScheduled = false;
836             }
837         });
838     }
839
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)) {
845             return true;
846         }
847
848         ActivityInfo ai = info.activityInfo;
849
850         XmlResourceParser parser = null;
851         try {
852             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
853             if (parser == null) {
854                 Slog.w(TAG, "no meta-data for " + info);
855                 return false;
856             }
857
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)) {
864                         return true;
865                     }
866                 }
867                 else if (accessory != null && "usb-accessory".equals(tagName)) {
868                     AccessoryFilter filter = AccessoryFilter.read(parser);
869                     if (filter.matches(accessory)) {
870                         return true;
871                     }
872                 }
873                 XmlUtils.nextElement(parser);
874             }
875         } catch (Exception e) {
876             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
877         } finally {
878             if (parser != null) parser.close();
879         }
880         return false;
881     }
882
883     /**
884      * Resolve all activities that match an intent for all profiles of this group.
885      *
886      * @param intent The intent to resolve
887      *
888      * @return The {@link ResolveInfo}s for all profiles of the group
889      */
890     private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
891             @NonNull Intent intent) {
892         List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
893
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));
899         }
900
901         return resolveInfos;
902     }
903
904     /**
905      * Only return those matches with the highest priority.
906      *
907      * @param matches All matches, some might have lower priority
908      *
909      * @return The matches with the highest priority
910      */
911     @NonNull
912     private ArrayList<ResolveInfo> preferHighPriority(
913             @NonNull ArrayList<ResolveInfo> matches) {
914         SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
915         SparseIntArray highestPriorityByUserId = new SparseIntArray();
916
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);
921
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<>());
926             }
927
928             // Find current highest priority matches for the current user
929             int highestPriority = highestPriorityByUserId.get(match.targetUserId);
930             ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get(
931                     match.targetUserId);
932
933             if (match.priority == highestPriority) {
934                 highestPriorityMatches.add(match);
935             } else if (match.priority > highestPriority) {
936                 highestPriorityByUserId.put(match.targetUserId, match.priority);
937
938                 highestPriorityMatches.clear();
939                 highestPriorityMatches.add(match);
940             }
941         }
942
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));
949         }
950
951         return combinedMatches;
952     }
953
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);
962             }
963         }
964         return preferHighPriority(matches);
965     }
966
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);
976             }
977         }
978         return preferHighPriority(matches);
979     }
980
981     public void deviceAttached(UsbDevice device) {
982         final Intent intent = createDeviceAttachedIntent(device);
983
984         // Send broadcast to running activities with registered intent
985         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
986
987         resolveActivity(intent, device, true /* showMtpNotification */);
988     }
989
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)));
997         }
998
999         if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
1000                 mPackageManager, device) && defaultActivity == null) {
1001             // Show notification if the device is MTP storage.
1002             mMtpNotificationManager.showNotification(device);
1003             return;
1004         }
1005
1006         // Start activity with registered intent
1007         resolveActivity(intent, matches, defaultActivity, device, null);
1008     }
1009
1010     public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
1011         final Intent intent = createDeviceAttachedIntent(device);
1012
1013         // Send broadcast to running activity with registered intent
1014         mContext.sendBroadcast(intent);
1015
1016         ApplicationInfo appInfo;
1017         try {
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);
1024             return;
1025         }
1026
1027         mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
1028                 .grantDevicePermission(device, appInfo.uid);
1029
1030         Intent activityIntent = new Intent(intent);
1031         activityIntent.setComponent(component);
1032         try {
1033             mContext.startActivityAsUser(activityIntent, mParentUser);
1034         } catch (ActivityNotFoundException e) {
1035             Slog.e(TAG, "unable to start activity " + activityIntent);
1036         }
1037     }
1038
1039     /**
1040      * Remove notifications for a usb device.
1041      *
1042      * @param device The device the notifications are for.
1043      */
1044     void usbDeviceRemoved(@NonNull UsbDevice device) {
1045         mMtpNotificationManager.hideNotification(device.getDeviceId());
1046     }
1047
1048     public void accessoryAttached(UsbAccessory accessory) {
1049         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
1050         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1051         intent.addFlags(
1052                 Intent.FLAG_ACTIVITY_NEW_TASK |
1053                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1054
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)));
1061         }
1062
1063         resolveActivity(intent, matches, defaultActivity, null, accessory);
1064     }
1065
1066     /**
1067      * Start the appropriate package when an device/accessory got attached.
1068      *
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
1074      */
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();
1079
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++;
1090                 } else {
1091                     numNonParentActivityMatches++;
1092                 }
1093             }
1094         }
1095
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);
1108                     try {
1109                         mContext.startActivityAsUser(dialogIntent, mParentUser);
1110                     } catch (ActivityNotFoundException e) {
1111                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
1112                     }
1113                 }
1114             }
1115
1116             // do nothing
1117             return;
1118         }
1119
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);
1124
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);
1129                 }
1130             }
1131         } else {
1132             matches = rawMatches;
1133         }
1134
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);
1145             }
1146
1147             // start default activity directly
1148             try {
1149                 intent.setComponent(
1150                         new ComponentName(defaultActivity.packageName, defaultActivity.name));
1151
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);
1157             }
1158         } else {
1159             Intent resolverIntent = new Intent();
1160             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1161             UserHandle user;
1162
1163             if (matches.size() == 1) {
1164                 ResolveInfo rInfo = matches.get(0);
1165
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);
1171
1172                 if (device != null) {
1173                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
1174                 } else {
1175                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1176                 }
1177             } else {
1178                 user = mParentUser;
1179
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);
1185             }
1186             try {
1187                 mContext.startActivityAsUser(resolverIntent, user);
1188             } catch (ActivityNotFoundException e) {
1189                 Slog.e(TAG, "unable to start activity " + resolverIntent, e);
1190             }
1191         }
1192     }
1193
1194     /**
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
1200      */
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;
1212                 }
1213             }
1214         }
1215
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;
1222                 }
1223                 if (activityInfo.applicationInfo != null
1224                         && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
1225                                 != 0) {
1226                     return activityInfo;
1227                 }
1228             }
1229         }
1230
1231         return null;
1232     }
1233
1234     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1235             @NonNull DeviceFilter filter) {
1236         ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
1237
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);
1244                 }
1245             }
1246         }
1247
1248         if (!keysToRemove.isEmpty()) {
1249             for (DeviceFilter keyToRemove : keysToRemove) {
1250                 mDevicePreferenceMap.remove(keyToRemove);
1251             }
1252         }
1253
1254         return !keysToRemove.isEmpty();
1255     }
1256
1257     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1258             @NonNull AccessoryFilter filter) {
1259         ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
1260
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);
1267                 }
1268             }
1269         }
1270
1271         if (!keysToRemove.isEmpty()) {
1272             for (AccessoryFilter keyToRemove : keysToRemove) {
1273                 mAccessoryPreferenceMap.remove(keyToRemove);
1274             }
1275         }
1276
1277         return !keysToRemove.isEmpty();
1278     }
1279
1280     private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
1281             String metaDataName) {
1282         XmlResourceParser parser = null;
1283         boolean changed = false;
1284
1285         try {
1286             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
1287             if (parser == null) return false;
1288
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)) {
1295                         changed = true;
1296                     }
1297                 }
1298                 else if ("usb-accessory".equals(tagName)) {
1299                     AccessoryFilter filter = AccessoryFilter.read(parser);
1300                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
1301                         changed = true;
1302                     }
1303                 }
1304                 XmlUtils.nextElement(parser);
1305             }
1306         } catch (Exception e) {
1307             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
1308         } finally {
1309             if (parser != null) parser.close();
1310         }
1311         return changed;
1312     }
1313
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) {
1318             PackageInfo info;
1319             boolean changed = false;
1320
1321             try {
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);
1327                 return;
1328             }
1329
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)) {
1336                     changed = true;
1337                 }
1338
1339                 if (handlePackageAddedLocked(userPackage, activities[i],
1340                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
1341                     changed = true;
1342                 }
1343             }
1344
1345             if (changed) {
1346                 scheduleWriteSettingsLocked();
1347             }
1348         }
1349     }
1350
1351     /**
1352      * Get the serial number for a user handle.
1353      *
1354      * @param user The user handle
1355      *
1356      * @return The serial number
1357      */
1358     private int getSerial(@NonNull UserHandle user) {
1359         return mUserManager.getUserSerialNumber(user.getIdentifier());
1360     }
1361
1362     /**
1363      * Set a package as default handler for a device.
1364      *
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
1368      */
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);
1376             } else {
1377                 UserPackage userPackage = new UserPackage(packageName, user);
1378
1379                 changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
1380                 if (changed) {
1381                     mDevicePreferenceMap.put(filter, userPackage);
1382                 }
1383             }
1384             if (changed) {
1385                 scheduleWriteSettingsLocked();
1386             }
1387         }
1388     }
1389
1390     /**
1391      * Set a package as default handler for a accessory.
1392      *
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
1396      */
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);
1404             } else {
1405                 UserPackage userPackage = new UserPackage(packageName, user);
1406
1407                 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));
1408                 if (changed) {
1409                     mAccessoryPreferenceMap.put(filter, userPackage);
1410                 }
1411             }
1412             if (changed) {
1413                 scheduleWriteSettingsLocked();
1414             }
1415         }
1416     }
1417
1418     /**
1419      * Check if a package has is the default handler for any usb device or accessory.
1420      *
1421      * @param packageName The package name
1422      * @param user The user the package belongs to
1423      *
1424      * @return {@code true} iff the package is default for any usb device or accessory
1425      */
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;
1431             return false;
1432         }
1433     }
1434
1435     /**
1436      * Clear defaults for a package from any preference.
1437      *
1438      * @param packageName The package to remove
1439      * @param user The user the package belongs to
1440      */
1441     void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1442         UserPackage userPackage = new UserPackage(packageName, user);
1443
1444         synchronized (mLock) {
1445             if (clearPackageDefaultsLocked(userPackage)) {
1446                 scheduleWriteSettingsLocked();
1447             }
1448         }
1449     }
1450
1451     /**
1452      * Clear defaults for a package from any preference (does not persist).
1453      *
1454      * @param userPackage The package to remove
1455      *
1456      * @return {@code true} iff at least one preference was cleared
1457      */
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);
1468                         cleared = true;
1469                     }
1470                 }
1471             }
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);
1479                         cleared = true;
1480                     }
1481                 }
1482             }
1483             return cleared;
1484         }
1485     }
1486
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));
1492             }
1493             pw.println("Accessory preferences:");
1494             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1495                 pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
1496             }
1497         }
1498     }
1499
1500     private static Intent createDeviceAttachedIntent(UsbDevice device) {
1501         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1502         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
1503         intent.addFlags(
1504                 Intent.FLAG_ACTIVITY_NEW_TASK |
1505                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1506         return intent;
1507     }
1508 }