• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
UserPackage(@onNull String packageName, @NonNull UserHandle user)131         private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
132             this.packageName = packageName;
133             this.user = user;
134         }
135 
136         @Override
equals(Object obj)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
hashCode()148         public int hashCode() {
149             int result = user.hashCode();
150             result = 31 * result + packageName.hashCode();
151             return result;
152         }
153 
154         @Override
toString()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 
DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum)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 
DeviceFilter(UsbDevice device)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 
read(XmlPullParser parser)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;
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 
write(XmlSerializer serializer)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 
matches(int clasz, int subclass, int protocol)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 
matches(UsbDevice device)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          */
contains(DeviceFilter device)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
equals(Object obj)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
hashCode()426         public int hashCode() {
427             return (((mVendorId << 16) | mProductId) ^
428                     ((mClass << 16) | (mSubclass << 8) | mProtocol));
429         }
430 
431         @Override
toString()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 
AccessoryFilter(String manufacturer, String model, String version)453         public AccessoryFilter(String manufacturer, String model, String version) {
454             mManufacturer = manufacturer;
455             mModel = model;
456             mVersion = version;
457         }
458 
AccessoryFilter(UsbAccessory accessory)459         public AccessoryFilter(UsbAccessory accessory) {
460             mManufacturer = accessory.getManufacturer();
461             mModel = accessory.getModel();
462             mVersion = accessory.getVersion();
463         }
464 
read(XmlPullParser parser)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 
write(XmlSerializer serializer)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 
matches(UsbAccessory acc)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             return !(mVersion != null && !acc.getVersion().equals(mVersion));
505         }
506 
507         /**
508          * Is the accessories described {@code accessory} covered by this filter?
509          *
510          * @param accessory A filter describing the accessory
511          *
512          * @return {@code true} iff this the filter covers the accessory
513          */
contains(AccessoryFilter accessory)514         public boolean contains(AccessoryFilter accessory) {
515             if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
516                 return false;
517             }
518             if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
519             return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
520         }
521 
522         @Override
equals(Object obj)523         public boolean equals(Object obj) {
524             // can't compare if we have wildcard strings
525             if (mManufacturer == null || mModel == null || mVersion == null) {
526                 return false;
527             }
528             if (obj instanceof AccessoryFilter) {
529                 AccessoryFilter filter = (AccessoryFilter)obj;
530                 return (mManufacturer.equals(filter.mManufacturer) &&
531                         mModel.equals(filter.mModel) &&
532                         mVersion.equals(filter.mVersion));
533             }
534             if (obj instanceof UsbAccessory) {
535                 UsbAccessory accessory = (UsbAccessory)obj;
536                 return (mManufacturer.equals(accessory.getManufacturer()) &&
537                         mModel.equals(accessory.getModel()) &&
538                         mVersion.equals(accessory.getVersion()));
539             }
540             return false;
541         }
542 
543         @Override
hashCode()544         public int hashCode() {
545             return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
546                     (mModel == null ? 0 : mModel.hashCode()) ^
547                     (mVersion == null ? 0 : mVersion.hashCode()));
548         }
549 
550         @Override
toString()551         public String toString() {
552             return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
553                                 "\", mModel=\"" + mModel +
554                                 "\", mVersion=\"" + mVersion + "\"]";
555         }
556     }
557 
558     private class MyPackageMonitor extends PackageMonitor {
559         @Override
onPackageAdded(String packageName, int uid)560         public void onPackageAdded(String packageName, int uid) {
561             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
562                     UserHandle.getUserId(uid))) {
563                 return;
564             }
565 
566             handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));
567         }
568 
569         @Override
onPackageRemoved(String packageName, int uid)570         public void onPackageRemoved(String packageName, int uid) {
571             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
572                     UserHandle.getUserId(uid))) {
573                 return;
574             }
575 
576             clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
577         }
578     }
579 
580     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
581 
582     private final MtpNotificationManager mMtpNotificationManager;
583 
584     /**
585      * Create new settings manager for a profile group.
586      *
587      * @param context The context of the service
588      * @param user The parent profile
589      * @param settingsManager The settings manager of the service
590      */
UsbProfileGroupSettingsManager(@onNull Context context, @NonNull UserHandle user, @NonNull UsbSettingsManager settingsManager)591     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
592             @NonNull UsbSettingsManager settingsManager) {
593         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
594 
595         Context parentUserContext;
596         try {
597             parentUserContext = context.createPackageContextAsUser("android", 0, user);
598         } catch (NameNotFoundException e) {
599             throw new RuntimeException("Missing android package");
600         }
601 
602         mContext = context;
603         mPackageManager = context.getPackageManager();
604         mSettingsManager = settingsManager;
605         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
606 
607         mParentUser = user;
608         mSettingsFile = new AtomicFile(new File(
609                 Environment.getUserSystemDirectory(user.getIdentifier()),
610                 "usb_device_manager.xml"));
611 
612         mDisablePermissionDialogs = context.getResources().getBoolean(
613                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
614 
615         synchronized (mLock) {
616             if (UserHandle.SYSTEM.equals(user)) {
617                 upgradeSingleUserLocked();
618             }
619             readSettingsLocked();
620         }
621 
622         mPackageMonitor.register(context, null, UserHandle.ALL, true);
623         mMtpNotificationManager = new MtpNotificationManager(
624                 parentUserContext,
625                 device -> resolveActivity(createDeviceAttachedIntent(device),
626                         device, false /* showMtpNotification */));
627     }
628 
629     /**
630      * Remove all defaults for a user.
631      *
632      * @param userToRemove The user the defaults belong to.
633      */
removeAllDefaultsForUser(@onNull UserHandle userToRemove)634     void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
635         synchronized (mLock) {
636             boolean needToPersist = false;
637             Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
638                     .entrySet().iterator();
639             while (devicePreferenceIt.hasNext()) {
640                 Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next();
641 
642                 if (entry.getValue().user.equals(userToRemove)) {
643                     devicePreferenceIt.remove();
644                     needToPersist = true;
645                 }
646             }
647 
648             Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
649                     mAccessoryPreferenceMap.entrySet().iterator();
650             while (accessoryPreferenceIt.hasNext()) {
651                 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
652 
653                 if (entry.getValue().user.equals(userToRemove)) {
654                     accessoryPreferenceIt.remove();
655                     needToPersist = true;
656                 }
657             }
658 
659             if (needToPersist) {
660                 scheduleWriteSettingsLocked();
661             }
662         }
663     }
664 
readPreference(XmlPullParser parser)665     private void readPreference(XmlPullParser parser)
666             throws XmlPullParserException, IOException {
667         String packageName = null;
668 
669         // If not set, assume it to be the parent profile
670         UserHandle user = mParentUser;
671 
672         int count = parser.getAttributeCount();
673         for (int i = 0; i < count; i++) {
674             if ("package".equals(parser.getAttributeName(i))) {
675                 packageName = parser.getAttributeValue(i);
676             }
677             if ("user".equals(parser.getAttributeName(i))) {
678                 // Might return null if user is not known anymore
679                 user = mUserManager
680                         .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
681             }
682         }
683 
684         XmlUtils.nextElement(parser);
685         if ("usb-device".equals(parser.getName())) {
686             DeviceFilter filter = DeviceFilter.read(parser);
687             if (user != null) {
688                 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
689             }
690         } else if ("usb-accessory".equals(parser.getName())) {
691             AccessoryFilter filter = AccessoryFilter.read(parser);
692             if (user != null) {
693                 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
694             }
695         }
696         XmlUtils.nextElement(parser);
697     }
698 
699     /**
700      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
701      * Should only by called by owner.
702      */
upgradeSingleUserLocked()703     private void upgradeSingleUserLocked() {
704         if (sSingleUserSettingsFile.exists()) {
705             mDevicePreferenceMap.clear();
706             mAccessoryPreferenceMap.clear();
707 
708             FileInputStream fis = null;
709             try {
710                 fis = new FileInputStream(sSingleUserSettingsFile);
711                 XmlPullParser parser = Xml.newPullParser();
712                 parser.setInput(fis, StandardCharsets.UTF_8.name());
713 
714                 XmlUtils.nextElement(parser);
715                 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
716                     final String tagName = parser.getName();
717                     if ("preference".equals(tagName)) {
718                         readPreference(parser);
719                     } else {
720                         XmlUtils.nextElement(parser);
721                     }
722                 }
723             } catch (IOException | XmlPullParserException e) {
724                 Log.wtf(TAG, "Failed to read single-user settings", e);
725             } finally {
726                 IoUtils.closeQuietly(fis);
727             }
728 
729             scheduleWriteSettingsLocked();
730 
731             // Success or failure, we delete single-user file
732             sSingleUserSettingsFile.delete();
733         }
734     }
735 
readSettingsLocked()736     private void readSettingsLocked() {
737         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
738 
739         mDevicePreferenceMap.clear();
740         mAccessoryPreferenceMap.clear();
741 
742         FileInputStream stream = null;
743         try {
744             stream = mSettingsFile.openRead();
745             XmlPullParser parser = Xml.newPullParser();
746             parser.setInput(stream, StandardCharsets.UTF_8.name());
747 
748             XmlUtils.nextElement(parser);
749             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
750                 String tagName = parser.getName();
751                 if ("preference".equals(tagName)) {
752                     readPreference(parser);
753                 } else {
754                     XmlUtils.nextElement(parser);
755                 }
756             }
757         } catch (FileNotFoundException e) {
758             if (DEBUG) Slog.d(TAG, "settings file not found");
759         } catch (Exception e) {
760             Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
761             mSettingsFile.delete();
762         } finally {
763             IoUtils.closeQuietly(stream);
764         }
765     }
766 
767     /**
768      * Schedule a async task to persist {@link #mDevicePreferenceMap} and
769      * {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do
770      * nothing as the currently scheduled one will do the work.
771      * <p>Called with {@link #mLock} held.</p>
772      * <p>In the uncommon case that the system crashes in between the scheduling and the write the
773      * update is lost.</p>
774      */
scheduleWriteSettingsLocked()775     private void scheduleWriteSettingsLocked() {
776         if (mIsWriteSettingsScheduled) {
777             return;
778         } else {
779             mIsWriteSettingsScheduled = true;
780         }
781 
782         AsyncTask.execute(() -> {
783             synchronized (mLock) {
784                 FileOutputStream fos = null;
785                 try {
786                     fos = mSettingsFile.startWrite();
787 
788                     FastXmlSerializer serializer = new FastXmlSerializer();
789                     serializer.setOutput(fos, StandardCharsets.UTF_8.name());
790                     serializer.startDocument(null, true);
791                     serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
792                                     true);
793                     serializer.startTag(null, "settings");
794 
795                     for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
796                         serializer.startTag(null, "preference");
797                         serializer.attribute(null, "package",
798                                 mDevicePreferenceMap.get(filter).packageName);
799                         serializer.attribute(null, "user",
800                                 String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
801                         filter.write(serializer);
802                         serializer.endTag(null, "preference");
803                     }
804 
805                     for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
806                         serializer.startTag(null, "preference");
807                         serializer.attribute(null, "package",
808                                 mAccessoryPreferenceMap.get(filter).packageName);
809                         serializer.attribute(null, "user", String.valueOf(
810                                         getSerial(mAccessoryPreferenceMap.get(filter).user)));
811                         filter.write(serializer);
812                         serializer.endTag(null, "preference");
813                     }
814 
815                     serializer.endTag(null, "settings");
816                     serializer.endDocument();
817 
818                     mSettingsFile.finishWrite(fos);
819                 } catch (IOException e) {
820                     Slog.e(TAG, "Failed to write settings", e);
821                     if (fos != null) {
822                         mSettingsFile.failWrite(fos);
823                     }
824                 }
825 
826                 mIsWriteSettingsScheduled = false;
827             }
828         });
829     }
830 
831     // Checks to see if a package matches a device or accessory.
832     // Only one of device and accessory should be non-null.
packageMatchesLocked(ResolveInfo info, String metaDataName, UsbDevice device, UsbAccessory accessory)833     private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
834             UsbDevice device, UsbAccessory accessory) {
835         if (isForwardMatch(info)) {
836             return true;
837         }
838 
839         ActivityInfo ai = info.activityInfo;
840 
841         XmlResourceParser parser = null;
842         try {
843             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
844             if (parser == null) {
845                 Slog.w(TAG, "no meta-data for " + info);
846                 return false;
847             }
848 
849             XmlUtils.nextElement(parser);
850             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
851                 String tagName = parser.getName();
852                 if (device != null && "usb-device".equals(tagName)) {
853                     DeviceFilter filter = DeviceFilter.read(parser);
854                     if (filter.matches(device)) {
855                         return true;
856                     }
857                 }
858                 else if (accessory != null && "usb-accessory".equals(tagName)) {
859                     AccessoryFilter filter = AccessoryFilter.read(parser);
860                     if (filter.matches(accessory)) {
861                         return true;
862                     }
863                 }
864                 XmlUtils.nextElement(parser);
865             }
866         } catch (Exception e) {
867             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
868         } finally {
869             if (parser != null) parser.close();
870         }
871         return false;
872     }
873 
874     /**
875      * Resolve all activities that match an intent for all profiles of this group.
876      *
877      * @param intent The intent to resolve
878      *
879      * @return The {@link ResolveInfo}s for all profiles of the group
880      */
queryIntentActivitiesForAllProfiles( @onNull Intent intent)881     private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
882             @NonNull Intent intent) {
883         List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
884 
885         ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
886         int numProfiles = profiles.size();
887         for (int i = 0; i < numProfiles; i++) {
888             resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent,
889                     PackageManager.GET_META_DATA, profiles.get(i).id));
890         }
891 
892         return resolveInfos;
893     }
894 
895     /**
896      * If this match used to forward the intent to another profile?
897      *
898      * @param match The match
899      *
900      * @return {@code true} iff this is such a forward match
901      */
isForwardMatch(@onNull ResolveInfo match)902     private boolean isForwardMatch(@NonNull ResolveInfo match) {
903         return match.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE);
904     }
905 
906     /**
907      * Only return those matches with the highest priority.
908      *
909      * @param matches All matches, some might have lower priority
910      *
911      * @return The matches with the highest priority
912      */
913     @NonNull
preferHighPriority(@onNull ArrayList<ResolveInfo> matches)914     private ArrayList<ResolveInfo> preferHighPriority(@NonNull ArrayList<ResolveInfo> matches) {
915         SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
916         SparseIntArray highestPriorityByUserId = new SparseIntArray();
917         ArrayList<ResolveInfo> forwardMatches = new ArrayList<>();
918 
919         // Create list of highest priority matches per user in highestPriorityMatchesByUserId
920         int numMatches = matches.size();
921         for (int matchNum = 0; matchNum < numMatches; matchNum++) {
922             ResolveInfo match = matches.get(matchNum);
923 
924             // Unnecessary forward matches are filtered out later, hence collect them all to add
925             // them below
926             if (isForwardMatch(match)) {
927                 forwardMatches.add(match);
928                 continue;
929             }
930 
931             // If this a previously unknown user?
932             if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) {
933                 highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE);
934                 highestPriorityMatchesByUserId.put(match.targetUserId, new ArrayList<>());
935             }
936 
937             // Find current highest priority matches for the current user
938             int highestPriority = highestPriorityByUserId.get(match.targetUserId);
939             ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get(
940                     match.targetUserId);
941 
942             if (match.priority == highestPriority) {
943                 highestPriorityMatches.add(match);
944             } else if (match.priority > highestPriority) {
945                 highestPriorityByUserId.put(match.targetUserId, match.priority);
946 
947                 highestPriorityMatches.clear();
948                 highestPriorityMatches.add(match);
949             }
950         }
951 
952         // Combine all users (+ forward matches) back together. This means that all non-forward
953         // matches have the same priority for a user. Matches for different users might have
954         // different priority.
955         ArrayList<ResolveInfo> combinedMatches = new ArrayList<>(forwardMatches);
956         int numMatchArrays = highestPriorityMatchesByUserId.size();
957         for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) {
958             combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum));
959         }
960 
961         return combinedMatches;
962     }
963 
964     /**
965      * If there are no matches for a profile, remove the forward intent to this profile.
966      *
967      * @param rawMatches The matches that contain all forward intents
968      *
969      * @return The matches with the unnecessary forward intents removed
970      */
removeForwardIntentIfNotNeeded( @onNull ArrayList<ResolveInfo> rawMatches)971     @NonNull private ArrayList<ResolveInfo> removeForwardIntentIfNotNeeded(
972             @NonNull ArrayList<ResolveInfo> rawMatches) {
973         final int numRawMatches = rawMatches.size();
974 
975         // The raw matches contain the activities that can be started but also the intents to
976         // forward the intent to the other profile
977         int numParentActivityMatches = 0;
978         int numNonParentActivityMatches = 0;
979         for (int i = 0; i < numRawMatches; i++) {
980             final ResolveInfo rawMatch = rawMatches.get(i);
981             if (!isForwardMatch(rawMatch)) {
982                 if (UserHandle.getUserHandleForUid(
983                         rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
984                     numParentActivityMatches++;
985                 } else {
986                     numNonParentActivityMatches++;
987                 }
988             }
989         }
990 
991         // If only one profile has activity matches, we need to remove all switch intents
992         if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
993             ArrayList<ResolveInfo> matches = new ArrayList<>(
994                     numParentActivityMatches + numNonParentActivityMatches);
995 
996             for (int i = 0; i < numRawMatches; i++) {
997                 ResolveInfo rawMatch = rawMatches.get(i);
998                 if (!isForwardMatch(rawMatch)) {
999                     matches.add(rawMatch);
1000                 }
1001             }
1002             return matches;
1003 
1004         } else {
1005             return rawMatches;
1006         }
1007     }
1008 
getDeviceMatchesLocked(UsbDevice device, Intent intent)1009     private ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
1010         ArrayList<ResolveInfo> matches = new ArrayList<>();
1011         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
1012         int count = resolveInfos.size();
1013         for (int i = 0; i < count; i++) {
1014             ResolveInfo resolveInfo = resolveInfos.get(i);
1015             if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
1016                 matches.add(resolveInfo);
1017             }
1018         }
1019 
1020         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
1021     }
1022 
getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)1023     private ArrayList<ResolveInfo> getAccessoryMatchesLocked(
1024             UsbAccessory accessory, Intent intent) {
1025         ArrayList<ResolveInfo> matches = new ArrayList<>();
1026         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
1027         int count = resolveInfos.size();
1028         for (int i = 0; i < count; i++) {
1029             ResolveInfo resolveInfo = resolveInfos.get(i);
1030             if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
1031                 matches.add(resolveInfo);
1032             }
1033         }
1034 
1035         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
1036     }
1037 
deviceAttached(UsbDevice device)1038     public void deviceAttached(UsbDevice device) {
1039         final Intent intent = createDeviceAttachedIntent(device);
1040 
1041         // Send broadcast to running activities with registered intent
1042         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1043 
1044         resolveActivity(intent, device, true /* showMtpNotification */);
1045     }
1046 
resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification)1047     private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
1048         final ArrayList<ResolveInfo> matches;
1049         final ActivityInfo defaultActivity;
1050         synchronized (mLock) {
1051             matches = getDeviceMatchesLocked(device, intent);
1052             defaultActivity = getDefaultActivityLocked(
1053                     matches, mDevicePreferenceMap.get(new DeviceFilter(device)));
1054         }
1055 
1056         if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
1057                 mPackageManager, device) && defaultActivity == null) {
1058             // Show notification if the device is MTP storage.
1059             mMtpNotificationManager.showNotification(device);
1060             return;
1061         }
1062 
1063         // Start activity with registered intent
1064         resolveActivity(intent, matches, defaultActivity, device, null);
1065     }
1066 
deviceAttachedForFixedHandler(UsbDevice device, ComponentName component)1067     public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
1068         final Intent intent = createDeviceAttachedIntent(device);
1069 
1070         // Send broadcast to running activity with registered intent
1071         mContext.sendBroadcast(intent);
1072 
1073         ApplicationInfo appInfo;
1074         try {
1075             // Fixed handlers are always for parent user
1076             appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0,
1077                     mParentUser.getIdentifier());
1078         } catch (NameNotFoundException e) {
1079             Slog.e(TAG, "Default USB handling package (" + component.getPackageName()
1080                     + ") not found  for user " + mParentUser);
1081             return;
1082         }
1083 
1084         mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
1085                 .grantDevicePermission(device, appInfo.uid);
1086 
1087         Intent activityIntent = new Intent(intent);
1088         activityIntent.setComponent(component);
1089         try {
1090             mContext.startActivityAsUser(activityIntent, mParentUser);
1091         } catch (ActivityNotFoundException e) {
1092             Slog.e(TAG, "unable to start activity " + activityIntent);
1093         }
1094     }
1095 
1096     /**
1097      * Remove notifications for a usb device.
1098      *
1099      * @param device The device the notifications are for.
1100      */
usbDeviceRemoved(@onNull UsbDevice device)1101     void usbDeviceRemoved(@NonNull UsbDevice device) {
1102         mMtpNotificationManager.hideNotification(device.getDeviceId());
1103     }
1104 
accessoryAttached(UsbAccessory accessory)1105     public void accessoryAttached(UsbAccessory accessory) {
1106         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
1107         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1108         intent.addFlags(
1109                 Intent.FLAG_ACTIVITY_NEW_TASK |
1110                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1111 
1112         final ArrayList<ResolveInfo> matches;
1113         final ActivityInfo defaultActivity;
1114         synchronized (mLock) {
1115             matches = getAccessoryMatchesLocked(accessory, intent);
1116             defaultActivity = getDefaultActivityLocked(
1117                     matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
1118         }
1119 
1120         resolveActivity(intent, matches, defaultActivity, null, accessory);
1121     }
1122 
1123     /**
1124      * Start the appropriate package when an device/accessory got attached.
1125      *
1126      * @param intent The intent to start the package
1127      * @param matches The available resolutions of the intent
1128      * @param defaultActivity The default activity for the device (if set)
1129      * @param device The device if a device was attached
1130      * @param accessory The accessory if a device was attached
1131      */
resolveActivity(@onNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory)1132     private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
1133             @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
1134             @Nullable UsbAccessory accessory) {
1135         // don't show the resolver activity if there are no choices available
1136         if (matches.size() == 0) {
1137             if (accessory != null) {
1138                 String uri = accessory.getUri();
1139                 if (uri != null && uri.length() > 0) {
1140                     // display URI to user
1141                     Intent dialogIntent = new Intent();
1142                     dialogIntent.setClassName("com.android.systemui",
1143                             "com.android.systemui.usb.UsbAccessoryUriActivity");
1144                     dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1145                     dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1146                     dialogIntent.putExtra("uri", uri);
1147                     try {
1148                         mContext.startActivityAsUser(dialogIntent, mParentUser);
1149                     } catch (ActivityNotFoundException e) {
1150                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
1151                     }
1152                 }
1153             }
1154 
1155             // do nothing
1156             return;
1157         }
1158 
1159         if (defaultActivity != null) {
1160             UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
1161                     UserHandle.getUserId(defaultActivity.applicationInfo.uid));
1162             // grant permission for default activity
1163             if (device != null) {
1164                 defaultRIUserSettings.
1165                         grantDevicePermission(device, defaultActivity.applicationInfo.uid);
1166             } else if (accessory != null) {
1167                 defaultRIUserSettings.grantAccessoryPermission(accessory,
1168                         defaultActivity.applicationInfo.uid);
1169             }
1170 
1171             // start default activity directly
1172             try {
1173                 intent.setComponent(
1174                         new ComponentName(defaultActivity.packageName, defaultActivity.name));
1175 
1176                 UserHandle user = UserHandle.getUserHandleForUid(
1177                         defaultActivity.applicationInfo.uid);
1178                 mContext.startActivityAsUser(intent, user);
1179             } catch (ActivityNotFoundException e) {
1180                 Slog.e(TAG, "startActivity failed", e);
1181             }
1182         } else {
1183             Intent resolverIntent = new Intent();
1184             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1185             UserHandle user;
1186 
1187             if (matches.size() == 1) {
1188                 ResolveInfo rInfo = matches.get(0);
1189 
1190                 // start UsbConfirmActivity if there is only one choice
1191                 resolverIntent.setClassName("com.android.systemui",
1192                         "com.android.systemui.usb.UsbConfirmActivity");
1193                 resolverIntent.putExtra("rinfo", rInfo);
1194                 user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
1195 
1196                 if (device != null) {
1197                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
1198                 } else {
1199                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
1200                 }
1201             } else {
1202                 user = mParentUser;
1203 
1204                 // start UsbResolverActivity so user can choose an activity
1205                 resolverIntent.setClassName("com.android.systemui",
1206                         "com.android.systemui.usb.UsbResolverActivity");
1207                 resolverIntent.putParcelableArrayListExtra("rlist", matches);
1208                 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
1209             }
1210             try {
1211                 mContext.startActivityAsUser(resolverIntent, user);
1212             } catch (ActivityNotFoundException e) {
1213                 Slog.e(TAG, "unable to start activity " + resolverIntent, e);
1214             }
1215         }
1216     }
1217 
1218     /**
1219      * Returns a default activity for matched ResolveInfo.
1220      * @param matches Resolved activities matched with connected device/accesary.
1221      * @param userPackage Default activity choosed by a user before. Should be null if no activity
1222      *     is choosed by a user.
1223      * @return Default activity
1224      */
getDefaultActivityLocked( @onNull ArrayList<ResolveInfo> matches, @Nullable UserPackage userPackage)1225     private @Nullable ActivityInfo getDefaultActivityLocked(
1226             @NonNull ArrayList<ResolveInfo> matches,
1227             @Nullable UserPackage userPackage) {
1228         if (userPackage != null) {
1229             // look for default activity
1230             for (final ResolveInfo info : matches) {
1231                 if (info.activityInfo != null && userPackage.equals(
1232                         new UserPackage(info.activityInfo.packageName,
1233                                 UserHandle.getUserHandleForUid(
1234                                         info.activityInfo.applicationInfo.uid)))) {
1235                     return info.activityInfo;
1236                 }
1237             }
1238         }
1239 
1240         if (matches.size() == 1) {
1241             final ActivityInfo activityInfo = matches.get(0).activityInfo;
1242             if (activityInfo != null) {
1243                 if (mDisablePermissionDialogs) {
1244                     return activityInfo;
1245                 }
1246                 // System apps are considered default unless there are other matches
1247                 if (activityInfo.applicationInfo != null
1248                         && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
1249                                 != 0) {
1250                     return activityInfo;
1251                 }
1252             }
1253         }
1254 
1255         return null;
1256     }
1257 
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull DeviceFilter filter)1258     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1259             @NonNull DeviceFilter filter) {
1260         ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
1261 
1262         // The keys in mDevicePreferenceMap are filters that match devices very narrowly
1263         for (DeviceFilter device : mDevicePreferenceMap.keySet()) {
1264             if (filter.contains(device)) {
1265                 UserPackage currentMatch = mDevicePreferenceMap.get(device);
1266                 if (!currentMatch.equals(userPackage)) {
1267                     keysToRemove.add(device);
1268                 }
1269             }
1270         }
1271 
1272         if (!keysToRemove.isEmpty()) {
1273             for (DeviceFilter keyToRemove : keysToRemove) {
1274                 mDevicePreferenceMap.remove(keyToRemove);
1275             }
1276         }
1277 
1278         return !keysToRemove.isEmpty();
1279     }
1280 
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull AccessoryFilter filter)1281     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1282             @NonNull AccessoryFilter filter) {
1283         ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
1284 
1285         // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly
1286         for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) {
1287             if (filter.contains(accessory)) {
1288                 UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory);
1289                 if (!currentMatch.equals(userPackage)) {
1290                     keysToRemove.add(accessory);
1291                 }
1292             }
1293         }
1294 
1295         if (!keysToRemove.isEmpty()) {
1296             for (AccessoryFilter keyToRemove : keysToRemove) {
1297                 mAccessoryPreferenceMap.remove(keyToRemove);
1298             }
1299         }
1300 
1301         return !keysToRemove.isEmpty();
1302     }
1303 
handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo, String metaDataName)1304     private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
1305             String metaDataName) {
1306         XmlResourceParser parser = null;
1307         boolean changed = false;
1308 
1309         try {
1310             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
1311             if (parser == null) return false;
1312 
1313             XmlUtils.nextElement(parser);
1314             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
1315                 String tagName = parser.getName();
1316                 if ("usb-device".equals(tagName)) {
1317                     DeviceFilter filter = DeviceFilter.read(parser);
1318                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
1319                         changed = true;
1320                     }
1321                 }
1322                 else if ("usb-accessory".equals(tagName)) {
1323                     AccessoryFilter filter = AccessoryFilter.read(parser);
1324                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
1325                         changed = true;
1326                     }
1327                 }
1328                 XmlUtils.nextElement(parser);
1329             }
1330         } catch (Exception e) {
1331             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
1332         } finally {
1333             if (parser != null) parser.close();
1334         }
1335         return changed;
1336     }
1337 
1338     // Check to see if the package supports any USB devices or accessories.
1339     // If so, clear any preferences for matching devices/accessories.
handlePackageAdded(@onNull UserPackage userPackage)1340     private void handlePackageAdded(@NonNull UserPackage userPackage) {
1341         synchronized (mLock) {
1342             PackageInfo info;
1343             boolean changed = false;
1344 
1345             try {
1346                 info = mPackageManager.getPackageInfoAsUser(userPackage.packageName,
1347                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
1348                         userPackage.user.getIdentifier());
1349             } catch (NameNotFoundException e) {
1350                 Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e);
1351                 return;
1352             }
1353 
1354             ActivityInfo[] activities = info.activities;
1355             if (activities == null) return;
1356             for (int i = 0; i < activities.length; i++) {
1357                 // check for meta-data, both for devices and accessories
1358                 if (handlePackageAddedLocked(userPackage, activities[i],
1359                         UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
1360                     changed = true;
1361                 }
1362 
1363                 if (handlePackageAddedLocked(userPackage, activities[i],
1364                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
1365                     changed = true;
1366                 }
1367             }
1368 
1369             if (changed) {
1370                 scheduleWriteSettingsLocked();
1371             }
1372         }
1373     }
1374 
1375     /**
1376      * Get the serial number for a user handle.
1377      *
1378      * @param user The user handle
1379      *
1380      * @return The serial number
1381      */
getSerial(@onNull UserHandle user)1382     private int getSerial(@NonNull UserHandle user) {
1383         return mUserManager.getUserSerialNumber(user.getIdentifier());
1384     }
1385 
1386     /**
1387      * Set a package as default handler for a device.
1388      *
1389      * @param device The device that should be handled by default
1390      * @param packageName The default handler package
1391      * @param user The user the package belongs to
1392      */
setDevicePackage(@onNull UsbDevice device, @Nullable String packageName, @NonNull UserHandle user)1393     void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
1394             @NonNull UserHandle user) {
1395         DeviceFilter filter = new DeviceFilter(device);
1396         boolean changed;
1397         synchronized (mLock) {
1398             if (packageName == null) {
1399                 changed = (mDevicePreferenceMap.remove(filter) != null);
1400             } else {
1401                 UserPackage userPackage = new UserPackage(packageName, user);
1402 
1403                 changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
1404                 if (changed) {
1405                     mDevicePreferenceMap.put(filter, userPackage);
1406                 }
1407             }
1408             if (changed) {
1409                 scheduleWriteSettingsLocked();
1410             }
1411         }
1412     }
1413 
1414     /**
1415      * Set a package as default handler for a accessory.
1416      *
1417      * @param accessory The accessory that should be handled by default
1418      * @param packageName The default handler package
1419      * @param user The user the package belongs to
1420      */
setAccessoryPackage(@onNull UsbAccessory accessory, @Nullable String packageName, @NonNull UserHandle user)1421     void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName,
1422             @NonNull UserHandle user) {
1423         AccessoryFilter filter = new AccessoryFilter(accessory);
1424         boolean changed;
1425         synchronized (mLock) {
1426             if (packageName == null) {
1427                 changed = (mAccessoryPreferenceMap.remove(filter) != null);
1428             } else {
1429                 UserPackage userPackage = new UserPackage(packageName, user);
1430 
1431                 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));
1432                 if (changed) {
1433                     mAccessoryPreferenceMap.put(filter, userPackage);
1434                 }
1435             }
1436             if (changed) {
1437                 scheduleWriteSettingsLocked();
1438             }
1439         }
1440     }
1441 
1442     /**
1443      * Check if a package has is the default handler for any usb device or accessory.
1444      *
1445      * @param packageName The package name
1446      * @param user The user the package belongs to
1447      *
1448      * @return {@code true} iff the package is default for any usb device or accessory
1449      */
hasDefaults(@onNull String packageName, @NonNull UserHandle user)1450     boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1451         UserPackage userPackage = new UserPackage(packageName, user);
1452         synchronized (mLock) {
1453             if (mDevicePreferenceMap.values().contains(userPackage)) return true;
1454             return mAccessoryPreferenceMap.values().contains(userPackage);
1455         }
1456     }
1457 
1458     /**
1459      * Clear defaults for a package from any preference.
1460      *
1461      * @param packageName The package to remove
1462      * @param user The user the package belongs to
1463      */
clearDefaults(@onNull String packageName, @NonNull UserHandle user)1464     void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1465         UserPackage userPackage = new UserPackage(packageName, user);
1466 
1467         synchronized (mLock) {
1468             if (clearPackageDefaultsLocked(userPackage)) {
1469                 scheduleWriteSettingsLocked();
1470             }
1471         }
1472     }
1473 
1474     /**
1475      * Clear defaults for a package from any preference (does not persist).
1476      *
1477      * @param userPackage The package to remove
1478      *
1479      * @return {@code true} iff at least one preference was cleared
1480      */
clearPackageDefaultsLocked(@onNull UserPackage userPackage)1481     private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) {
1482         boolean cleared = false;
1483         synchronized (mLock) {
1484             if (mDevicePreferenceMap.containsValue(userPackage)) {
1485                 // make a copy of the key set to avoid ConcurrentModificationException
1486                 DeviceFilter[] keys = mDevicePreferenceMap.keySet().toArray(new DeviceFilter[0]);
1487                 for (int i = 0; i < keys.length; i++) {
1488                     DeviceFilter key = keys[i];
1489                     if (userPackage.equals(mDevicePreferenceMap.get(key))) {
1490                         mDevicePreferenceMap.remove(key);
1491                         cleared = true;
1492                     }
1493                 }
1494             }
1495             if (mAccessoryPreferenceMap.containsValue(userPackage)) {
1496                 // make a copy of the key set to avoid ConcurrentModificationException
1497                 AccessoryFilter[] keys =
1498                         mAccessoryPreferenceMap.keySet().toArray(new AccessoryFilter[0]);
1499                 for (int i = 0; i < keys.length; i++) {
1500                     AccessoryFilter key = keys[i];
1501                     if (userPackage.equals(mAccessoryPreferenceMap.get(key))) {
1502                         mAccessoryPreferenceMap.remove(key);
1503                         cleared = true;
1504                     }
1505                 }
1506             }
1507             return cleared;
1508         }
1509     }
1510 
dump(IndentingPrintWriter pw)1511     public void dump(IndentingPrintWriter pw) {
1512         synchronized (mLock) {
1513             pw.println("Device preferences:");
1514             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1515                 pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
1516             }
1517             pw.println("Accessory preferences:");
1518             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1519                 pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
1520             }
1521         }
1522     }
1523 
createDeviceAttachedIntent(UsbDevice device)1524     private static Intent createDeviceAttachedIntent(UsbDevice device) {
1525         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1526         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
1527         intent.addFlags(
1528                 Intent.FLAG_ACTIVITY_NEW_TASK |
1529                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1530         return intent;
1531     }
1532 }
1533