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.app.ActivityManager;
24 import android.content.ActivityNotFoundException;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.content.pm.ResolveInfo;
34 import android.content.pm.UserInfo;
35 import android.content.res.XmlResourceParser;
36 import android.hardware.usb.AccessoryFilter;
37 import android.hardware.usb.DeviceFilter;
38 import android.hardware.usb.UsbAccessory;
39 import android.hardware.usb.UsbDevice;
40 import android.hardware.usb.UsbManager;
41 import android.os.AsyncTask;
42 import android.os.Environment;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.service.usb.UsbProfileGroupSettingsManagerProto;
46 import android.service.usb.UsbSettingsAccessoryPreferenceProto;
47 import android.service.usb.UsbSettingsDevicePreferenceProto;
48 import android.service.usb.UserPackageProto;
49 import android.util.ArrayMap;
50 import android.util.ArraySet;
51 import android.util.AtomicFile;
52 import android.util.Log;
53 import android.util.Slog;
54 import android.util.SparseArray;
55 import android.util.SparseIntArray;
56 import android.util.TypedXmlPullParser;
57 import android.util.TypedXmlSerializer;
58 import android.util.Xml;
59 
60 import com.android.internal.annotations.GuardedBy;
61 import com.android.internal.annotations.Immutable;
62 import com.android.internal.content.PackageMonitor;
63 import com.android.internal.util.FastXmlSerializer;
64 import com.android.internal.util.XmlUtils;
65 import com.android.internal.util.dump.DualDumpOutputStream;
66 
67 import libcore.io.IoUtils;
68 
69 import org.xmlpull.v1.XmlPullParser;
70 import org.xmlpull.v1.XmlPullParserException;
71 
72 import java.io.File;
73 import java.io.FileInputStream;
74 import java.io.FileNotFoundException;
75 import java.io.FileOutputStream;
76 import java.io.IOException;
77 import java.net.ProtocolException;
78 import java.nio.charset.StandardCharsets;
79 import java.util.ArrayList;
80 import java.util.HashMap;
81 import java.util.Iterator;
82 import java.util.List;
83 import java.util.Map;
84 
85 class UsbProfileGroupSettingsManager {
86     private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
87     private static final boolean DEBUG = false;
88 
89     /** Legacy settings file, before multi-user */
90     private static final File sSingleUserSettingsFile = new File(
91             "/data/system/usb_device_manager.xml");
92 
93     /** The parent user (main user of the profile group) */
94     private final UserHandle mParentUser;
95 
96     private final AtomicFile mSettingsFile;
97     private final boolean mDisablePermissionDialogs;
98 
99     private final Context mContext;
100 
101     private final PackageManager mPackageManager;
102 
103     private final UserManager mUserManager;
104     private final @NonNull UsbSettingsManager mSettingsManager;
105 
106     /** Maps DeviceFilter to user preferred application package */
107     @GuardedBy("mLock")
108     private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
109 
110     /** Maps DeviceFilter to set of UserPackages not to ask for launch preference anymore */
111     @GuardedBy("mLock")
112     private final ArrayMap<DeviceFilter, ArraySet<UserPackage>> mDevicePreferenceDeniedMap =
113             new ArrayMap<>();
114 
115     /** Maps AccessoryFilter to user preferred application package */
116     @GuardedBy("mLock")
117     private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
118 
119     /** Maps AccessoryFilter to set of UserPackages not to ask for launch preference anymore */
120     @GuardedBy("mLock")
121     private final ArrayMap<AccessoryFilter, ArraySet<UserPackage>> mAccessoryPreferenceDeniedMap =
122             new ArrayMap<>();
123 
124     private final Object mLock = new Object();
125 
126     /**
127      * If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently
128      * scheduled.
129      */
130     @GuardedBy("mLock")
131     private boolean mIsWriteSettingsScheduled;
132 
133     /**
134      * A package of a user.
135      */
136     @Immutable
137     private static class UserPackage {
138         /** User */
139         final @NonNull UserHandle user;
140 
141         /** Package name */
142         final @NonNull String packageName;
143 
144         /**
145          * Create a description of a per user package.
146          *
147          * @param packageName The name of the package
148          * @param user The user
149          */
UserPackage(@onNull String packageName, @NonNull UserHandle user)150         private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
151             this.packageName = packageName;
152             this.user = user;
153         }
154 
155         @Override
equals(Object obj)156         public boolean equals(Object obj) {
157             if (!(obj instanceof UserPackage)) {
158                 return false;
159             } else {
160                 UserPackage other = (UserPackage)obj;
161 
162                 return user.equals(other.user) && packageName.equals(other.packageName);
163             }
164         }
165 
166         @Override
hashCode()167         public int hashCode() {
168             int result = user.hashCode();
169             result = 31 * result + packageName.hashCode();
170             return result;
171         }
172 
173         @Override
toString()174         public String toString() {
175             return user.getIdentifier() + "/" + packageName;
176         }
177 
dump(DualDumpOutputStream dump, String idName, long id)178         public void dump(DualDumpOutputStream dump, String idName, long id) {
179             long token = dump.start(idName, id);
180 
181             dump.write("user_id", UserPackageProto.USER_ID, user.getIdentifier());
182             dump.write("package_name", UserPackageProto.PACKAGE_NAME, packageName);
183 
184             dump.end(token);
185         }
186     }
187 
188     private class MyPackageMonitor extends PackageMonitor {
189         @Override
onPackageAdded(String packageName, int uid)190         public void onPackageAdded(String packageName, int uid) {
191             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
192                     UserHandle.getUserId(uid))) {
193                 return;
194             }
195 
196             handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));
197         }
198 
199         @Override
onPackageRemoved(String packageName, int uid)200         public void onPackageRemoved(String packageName, int uid) {
201             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
202                     UserHandle.getUserId(uid))) {
203                 return;
204             }
205 
206             clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
207         }
208     }
209 
210     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
211 
212     private final UsbHandlerManager mUsbHandlerManager;
213 
214     private final MtpNotificationManager mMtpNotificationManager;
215 
216     /**
217      * Create new settings manager for a profile group.
218      *
219      * @param context The context of the service
220      * @param user The parent profile
221      * @param settingsManager The settings manager of the service
222      * @param usbResolveActivityManager The resovle activity manager of the service
223      */
UsbProfileGroupSettingsManager(@onNull Context context, @NonNull UserHandle user, @NonNull UsbSettingsManager settingsManager, @NonNull UsbHandlerManager usbResolveActivityManager)224     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
225             @NonNull UsbSettingsManager settingsManager,
226             @NonNull UsbHandlerManager usbResolveActivityManager) {
227         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
228 
229         Context parentUserContext;
230         try {
231             parentUserContext = context.createPackageContextAsUser("android", 0, user);
232         } catch (NameNotFoundException e) {
233             throw new RuntimeException("Missing android package");
234         }
235 
236         mContext = context;
237         mPackageManager = context.getPackageManager();
238         mSettingsManager = settingsManager;
239         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
240 
241         mParentUser = user;
242         mSettingsFile = new AtomicFile(new File(
243                 Environment.getUserSystemDirectory(user.getIdentifier()),
244                 "usb_device_manager.xml"), "usb-state");
245 
246         mDisablePermissionDialogs = context.getResources().getBoolean(
247                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
248 
249         synchronized (mLock) {
250             if (UserHandle.SYSTEM.equals(user)) {
251                 upgradeSingleUserLocked();
252             }
253             readSettingsLocked();
254         }
255 
256         mPackageMonitor.register(context, null, UserHandle.ALL, true);
257         mMtpNotificationManager = new MtpNotificationManager(
258                 parentUserContext,
259                 device -> resolveActivity(createDeviceAttachedIntent(device),
260                         device, false /* showMtpNotification */));
261 
262         mUsbHandlerManager = usbResolveActivityManager;
263     }
264 
265     /**
266      * Unregister all broadcast receivers. Must be called explicitly before
267      * object deletion.
268      */
unregisterReceivers()269     public void unregisterReceivers() {
270         mPackageMonitor.unregister();
271         mMtpNotificationManager.unregister();
272     }
273 
274     /**
275      * Remove all defaults and denied packages for a user.
276      *
277      * @param userToRemove The user
278      */
removeUser(@onNull UserHandle userToRemove)279     void removeUser(@NonNull UserHandle userToRemove) {
280         synchronized (mLock) {
281             boolean needToPersist = false;
282             Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
283                     .entrySet().iterator();
284             while (devicePreferenceIt.hasNext()) {
285                 Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next();
286 
287                 if (entry.getValue().user.equals(userToRemove)) {
288                     devicePreferenceIt.remove();
289                     needToPersist = true;
290                 }
291             }
292 
293             Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
294                     mAccessoryPreferenceMap.entrySet().iterator();
295             while (accessoryPreferenceIt.hasNext()) {
296                 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
297 
298                 if (entry.getValue().user.equals(userToRemove)) {
299                     accessoryPreferenceIt.remove();
300                     needToPersist = true;
301                 }
302             }
303 
304             int numEntries = mDevicePreferenceDeniedMap.size();
305             for (int i = 0; i < numEntries; i++) {
306                 ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.valueAt(i);
307                 for (int j = userPackages.size() - 1; j >= 0; j--) {
308                     if (userPackages.valueAt(j).user.equals(userToRemove)) {
309                         userPackages.removeAt(j);
310                         needToPersist = true;
311                     }
312                 }
313             }
314 
315             numEntries = mAccessoryPreferenceDeniedMap.size();
316             for (int i = 0; i < numEntries; i++) {
317                 ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.valueAt(i);
318                 for (int j = userPackages.size() - 1; j >= 0; j--) {
319                     if (userPackages.valueAt(j).user.equals(userToRemove)) {
320                         userPackages.removeAt(j);
321                         needToPersist = true;
322                     }
323                 }
324             }
325 
326             if (needToPersist) {
327                 scheduleWriteSettingsLocked();
328             }
329         }
330     }
331 
readPreference(XmlPullParser parser)332     private void readPreference(XmlPullParser parser)
333             throws IOException, XmlPullParserException {
334         String packageName = null;
335 
336         // If not set, assume it to be the parent profile
337         UserHandle user = mParentUser;
338 
339         int count = parser.getAttributeCount();
340         for (int i = 0; i < count; i++) {
341             if ("package".equals(parser.getAttributeName(i))) {
342                 packageName = parser.getAttributeValue(i);
343             }
344             if ("user".equals(parser.getAttributeName(i))) {
345                 // Might return null if user is not known anymore
346                 user = mUserManager
347                         .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
348             }
349         }
350 
351         XmlUtils.nextElement(parser);
352         if ("usb-device".equals(parser.getName())) {
353             DeviceFilter filter = DeviceFilter.read(parser);
354             if (user != null) {
355                 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
356             }
357         } else if ("usb-accessory".equals(parser.getName())) {
358             AccessoryFilter filter = AccessoryFilter.read(parser);
359             if (user != null) {
360                 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
361             }
362         }
363         XmlUtils.nextElement(parser);
364     }
365 
readPreferenceDeniedList(@onNull XmlPullParser parser)366     private void readPreferenceDeniedList(@NonNull XmlPullParser parser)
367             throws IOException, XmlPullParserException {
368         int outerDepth = parser.getDepth();
369         if (!XmlUtils.nextElementWithin(parser, outerDepth)) {
370             return;
371         }
372 
373         if ("usb-device".equals(parser.getName())) {
374             DeviceFilter filter = DeviceFilter.read(parser);
375             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
376                 if ("user-package".equals(parser.getName())) {
377                     try {
378                         int userId = XmlUtils.readIntAttribute(parser, "user");
379 
380                         String packageName = XmlUtils.readStringAttribute(parser, "package");
381                         if (packageName == null) {
382                             Slog.e(TAG, "Unable to parse package name");
383                         }
384 
385                         ArraySet<UserPackage> set = mDevicePreferenceDeniedMap.get(filter);
386                         if (set == null) {
387                             set = new ArraySet<>();
388                             mDevicePreferenceDeniedMap.put(filter, set);
389                         }
390                         set.add(new UserPackage(packageName, UserHandle.of(userId)));
391                     } catch (ProtocolException e) {
392                         Slog.e(TAG, "Unable to parse user id", e);
393                     }
394                 }
395             }
396         } else if ("usb-accessory".equals(parser.getName())) {
397             AccessoryFilter filter = AccessoryFilter.read(parser);
398 
399             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
400                 if ("user-package".equals(parser.getName())) {
401                     try {
402                         int userId = XmlUtils.readIntAttribute(parser, "user");
403 
404                         String packageName = XmlUtils.readStringAttribute(parser, "package");
405                         if (packageName == null) {
406                             Slog.e(TAG, "Unable to parse package name");
407                         }
408 
409                         ArraySet<UserPackage> set = mAccessoryPreferenceDeniedMap.get(filter);
410                         if (set == null) {
411                             set = new ArraySet<>();
412                             mAccessoryPreferenceDeniedMap.put(filter, set);
413                         }
414                         set.add(new UserPackage(packageName, UserHandle.of(userId)));
415                     } catch (ProtocolException e) {
416                         Slog.e(TAG, "Unable to parse user id", e);
417                     }
418                 }
419             }
420         }
421 
422         while (parser.getDepth() > outerDepth) {
423             parser.nextTag(); // ignore unknown tags
424         }
425     }
426 
427     /**
428      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
429      * Should only be called by owner.
430      */
431     @GuardedBy("mLock")
upgradeSingleUserLocked()432     private void upgradeSingleUserLocked() {
433         if (sSingleUserSettingsFile.exists()) {
434             mDevicePreferenceMap.clear();
435             mAccessoryPreferenceMap.clear();
436 
437             FileInputStream fis = null;
438             try {
439                 fis = new FileInputStream(sSingleUserSettingsFile);
440                 TypedXmlPullParser parser = Xml.resolvePullParser(fis);
441 
442                 XmlUtils.nextElement(parser);
443                 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
444                     final String tagName = parser.getName();
445                     if ("preference".equals(tagName)) {
446                         readPreference(parser);
447                     } else {
448                         XmlUtils.nextElement(parser);
449                     }
450                 }
451             } catch (IOException | XmlPullParserException e) {
452                 Log.wtf(TAG, "Failed to read single-user settings", e);
453             } finally {
454                 IoUtils.closeQuietly(fis);
455             }
456 
457             scheduleWriteSettingsLocked();
458 
459             // Success or failure, we delete single-user file
460             sSingleUserSettingsFile.delete();
461         }
462     }
463 
464     @GuardedBy("mLock")
readSettingsLocked()465     private void readSettingsLocked() {
466         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
467 
468         mDevicePreferenceMap.clear();
469         mAccessoryPreferenceMap.clear();
470 
471         FileInputStream stream = null;
472         try {
473             stream = mSettingsFile.openRead();
474             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
475 
476             XmlUtils.nextElement(parser);
477             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
478                 String tagName = parser.getName();
479                 if ("preference".equals(tagName)) {
480                     readPreference(parser);
481                 } else if ("preference-denied-list".equals(tagName)) {
482                     readPreferenceDeniedList(parser);
483                 } else {
484                     XmlUtils.nextElement(parser);
485                 }
486             }
487         } catch (FileNotFoundException e) {
488             if (DEBUG) Slog.d(TAG, "settings file not found");
489         } catch (Exception e) {
490             Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
491             mSettingsFile.delete();
492         } finally {
493             IoUtils.closeQuietly(stream);
494         }
495     }
496 
497     /**
498      * Schedule a async task to persist {@link #mDevicePreferenceMap} and
499      * {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do
500      * nothing as the currently scheduled one will do the work.
501      * <p>Called with {@link #mLock} held.</p>
502      * <p>In the uncommon case that the system crashes in between the scheduling and the write the
503      * update is lost.</p>
504      */
505     @GuardedBy("mLock")
scheduleWriteSettingsLocked()506     private void scheduleWriteSettingsLocked() {
507         if (mIsWriteSettingsScheduled) {
508             return;
509         } else {
510             mIsWriteSettingsScheduled = true;
511         }
512 
513         AsyncTask.execute(() -> {
514             synchronized (mLock) {
515                 FileOutputStream fos = null;
516                 try {
517                     fos = mSettingsFile.startWrite();
518 
519                     TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
520                     serializer.startDocument(null, true);
521                     serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
522                                     true);
523                     serializer.startTag(null, "settings");
524 
525                     for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
526                         serializer.startTag(null, "preference");
527                         serializer.attribute(null, "package",
528                                 mDevicePreferenceMap.get(filter).packageName);
529                         serializer.attribute(null, "user",
530                                 String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
531                         filter.write(serializer);
532                         serializer.endTag(null, "preference");
533                     }
534 
535                     for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
536                         serializer.startTag(null, "preference");
537                         serializer.attribute(null, "package",
538                                 mAccessoryPreferenceMap.get(filter).packageName);
539                         serializer.attribute(null, "user", String.valueOf(
540                                         getSerial(mAccessoryPreferenceMap.get(filter).user)));
541                         filter.write(serializer);
542                         serializer.endTag(null, "preference");
543                     }
544 
545                     int numEntries = mDevicePreferenceDeniedMap.size();
546                     for (int i = 0; i < numEntries; i++) {
547                         DeviceFilter filter = mDevicePreferenceDeniedMap.keyAt(i);
548                         ArraySet<UserPackage> userPackageSet = mDevicePreferenceDeniedMap
549                                 .valueAt(i);
550                         serializer.startTag(null, "preference-denied-list");
551                         filter.write(serializer);
552 
553                         int numUserPackages = userPackageSet.size();
554                         for (int j = 0; j < numUserPackages; j++) {
555                             UserPackage userPackage = userPackageSet.valueAt(j);
556                             serializer.startTag(null, "user-package");
557                             serializer.attribute(null, "user",
558                                     String.valueOf(getSerial(userPackage.user)));
559                             serializer.attribute(null, "package", userPackage.packageName);
560                             serializer.endTag(null, "user-package");
561                         }
562                         serializer.endTag(null, "preference-denied-list");
563                     }
564 
565                     numEntries = mAccessoryPreferenceDeniedMap.size();
566                     for (int i = 0; i < numEntries; i++) {
567                         AccessoryFilter filter = mAccessoryPreferenceDeniedMap.keyAt(i);
568                         ArraySet<UserPackage> userPackageSet =
569                                 mAccessoryPreferenceDeniedMap.valueAt(i);
570                         serializer.startTag(null, "preference-denied-list");
571                         filter.write(serializer);
572 
573                         int numUserPackages = userPackageSet.size();
574                         for (int j = 0; j < numUserPackages; j++) {
575                             UserPackage userPackage = userPackageSet.valueAt(j);
576                             serializer.startTag(null, "user-package");
577                             serializer.attribute(null, "user",
578                                     String.valueOf(getSerial(userPackage.user)));
579                             serializer.attribute(null, "package", userPackage.packageName);
580                             serializer.endTag(null, "user-package");
581                         }
582                         serializer.endTag(null, "preference-denied-list");
583                     }
584 
585                     serializer.endTag(null, "settings");
586                     serializer.endDocument();
587 
588                     mSettingsFile.finishWrite(fos);
589                 } catch (IOException e) {
590                     Slog.e(TAG, "Failed to write settings", e);
591                     if (fos != null) {
592                         mSettingsFile.failWrite(fos);
593                     }
594                 }
595 
596                 mIsWriteSettingsScheduled = false;
597             }
598         });
599     }
600 
601     /**
602      * Get {@link DeviceFilter} for all devices an activity should be launched for.
603      *
604      * @param pm The package manager used to get the device filter files
605      * @param info The {@link ResolveInfo} for the activity that can handle usb device attached
606      *             events
607      *
608      * @return The list of {@link DeviceFilter} the activity should be called for or {@code null} if
609      *         none
610      */
611     @Nullable
getDeviceFilters(@onNull PackageManager pm, @NonNull ResolveInfo info)612     static ArrayList<DeviceFilter> getDeviceFilters(@NonNull PackageManager pm,
613             @NonNull ResolveInfo info) {
614         ArrayList<DeviceFilter> filters = null;
615         ActivityInfo ai = info.activityInfo;
616 
617         XmlResourceParser parser = null;
618         try {
619             parser = ai.loadXmlMetaData(pm, UsbManager.ACTION_USB_DEVICE_ATTACHED);
620             if (parser == null) {
621                 Slog.w(TAG, "no meta-data for " + info);
622                 return null;
623             }
624 
625             XmlUtils.nextElement(parser);
626             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
627                 String tagName = parser.getName();
628                 if ("usb-device".equals(tagName)) {
629                     if (filters == null) {
630                         filters = new ArrayList<>(1);
631                     }
632                     filters.add(DeviceFilter.read(parser));
633                 }
634                 XmlUtils.nextElement(parser);
635             }
636         } catch (Exception e) {
637             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
638         } finally {
639             if (parser != null) parser.close();
640         }
641         return filters;
642     }
643 
644     /**
645      * Get {@link AccessoryFilter} for all accessories an activity should be launched for.
646      *
647      * @param pm The package manager used to get the accessory filter files
648      * @param info The {@link ResolveInfo} for the activity that can handle usb accessory attached
649      *             events
650      *
651      * @return The list of {@link AccessoryFilter} the activity should be called for or {@code null}
652      *         if none
653      */
getAccessoryFilters(@onNull PackageManager pm, @NonNull ResolveInfo info)654     static @Nullable ArrayList<AccessoryFilter> getAccessoryFilters(@NonNull PackageManager pm,
655             @NonNull ResolveInfo info) {
656         ArrayList<AccessoryFilter> filters = null;
657         ActivityInfo ai = info.activityInfo;
658 
659         XmlResourceParser parser = null;
660         try {
661             parser = ai.loadXmlMetaData(pm, UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
662             if (parser == null) {
663                 Slog.w(TAG, "no meta-data for " + info);
664                 return null;
665             }
666 
667             XmlUtils.nextElement(parser);
668             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
669                 String tagName = parser.getName();
670                 if ("usb-accessory".equals(tagName)) {
671                     if (filters == null) {
672                         filters = new ArrayList<>(1);
673                     }
674                     filters.add(AccessoryFilter.read(parser));
675                 }
676                 XmlUtils.nextElement(parser);
677             }
678         } catch (Exception e) {
679             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
680         } finally {
681             if (parser != null) parser.close();
682         }
683         return filters;
684     }
685 
686     // Checks to see if a package matches a device or accessory.
687     // Only one of device and accessory should be non-null.
packageMatchesLocked(ResolveInfo info, UsbDevice device, UsbAccessory accessory)688     private boolean packageMatchesLocked(ResolveInfo info, UsbDevice device,
689             UsbAccessory accessory) {
690         if (isForwardMatch(info)) {
691             return true;
692         }
693 
694         if (device != null) {
695             ArrayList<DeviceFilter> deviceFilters = getDeviceFilters(mPackageManager, info);
696             if (deviceFilters != null) {
697                 int numDeviceFilters = deviceFilters.size();
698                 for (int i = 0; i < numDeviceFilters; i++) {
699                     if (deviceFilters.get(i).matches(device)) {
700                         return true;
701                     }
702                 }
703             }
704         }
705 
706         if (accessory != null) {
707             ArrayList<AccessoryFilter> accessoryFilters = getAccessoryFilters(mPackageManager,
708                     info);
709             if (accessoryFilters != null) {
710                 int numAccessoryFilters = accessoryFilters.size();
711                 for (int i = 0; i < numAccessoryFilters; i++) {
712                     if (accessoryFilters.get(i).matches(accessory)) {
713                         return true;
714                     }
715                 }
716             }
717         }
718 
719         return false;
720     }
721 
722     /**
723      * Resolve all activities that match an intent for all profiles of this group.
724      *
725      * @param intent The intent to resolve
726      *
727      * @return The {@link ResolveInfo}s for all profiles of the group
728      */
queryIntentActivitiesForAllProfiles( @onNull Intent intent)729     private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
730             @NonNull Intent intent) {
731         List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
732 
733         ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
734         int numProfiles = profiles.size();
735         for (int i = 0; i < numProfiles; i++) {
736             resolveInfos.addAll(mSettingsManager.getSettingsForUser(profiles.get(i).id)
737                     .queryIntentActivities(intent));
738         }
739 
740         return resolveInfos;
741     }
742 
743     /**
744      * If this match used to forward the intent to another profile?
745      *
746      * @param match The match
747      *
748      * @return {@code true} iff this is such a forward match
749      */
isForwardMatch(@onNull ResolveInfo match)750     private boolean isForwardMatch(@NonNull ResolveInfo match) {
751         return match.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE);
752     }
753 
754     /**
755      * Only return those matches with the highest priority.
756      *
757      * @param matches All matches, some might have lower priority
758      *
759      * @return The matches with the highest priority
760      */
761     @NonNull
preferHighPriority(@onNull ArrayList<ResolveInfo> matches)762     private ArrayList<ResolveInfo> preferHighPriority(@NonNull ArrayList<ResolveInfo> matches) {
763         SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
764         SparseIntArray highestPriorityByUserId = new SparseIntArray();
765         ArrayList<ResolveInfo> forwardMatches = new ArrayList<>();
766 
767         // Create list of highest priority matches per user in highestPriorityMatchesByUserId
768         int numMatches = matches.size();
769         for (int matchNum = 0; matchNum < numMatches; matchNum++) {
770             ResolveInfo match = matches.get(matchNum);
771 
772             // Unnecessary forward matches are filtered out later, hence collect them all to add
773             // them below
774             if (isForwardMatch(match)) {
775                 forwardMatches.add(match);
776                 continue;
777             }
778 
779             // If this a previously unknown user?
780             if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) {
781                 highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE);
782                 highestPriorityMatchesByUserId.put(match.targetUserId, new ArrayList<>());
783             }
784 
785             // Find current highest priority matches for the current user
786             int highestPriority = highestPriorityByUserId.get(match.targetUserId);
787             ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get(
788                     match.targetUserId);
789 
790             if (match.priority == highestPriority) {
791                 highestPriorityMatches.add(match);
792             } else if (match.priority > highestPriority) {
793                 highestPriorityByUserId.put(match.targetUserId, match.priority);
794 
795                 highestPriorityMatches.clear();
796                 highestPriorityMatches.add(match);
797             }
798         }
799 
800         // Combine all users (+ forward matches) back together. This means that all non-forward
801         // matches have the same priority for a user. Matches for different users might have
802         // different priority.
803         ArrayList<ResolveInfo> combinedMatches = new ArrayList<>(forwardMatches);
804         int numMatchArrays = highestPriorityMatchesByUserId.size();
805         for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) {
806             combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum));
807         }
808 
809         return combinedMatches;
810     }
811 
812     /**
813      * If there are no matches for a profile, remove the forward intent to this profile.
814      *
815      * @param rawMatches The matches that contain all forward intents
816      *
817      * @return The matches with the unnecessary forward intents removed
818      */
removeForwardIntentIfNotNeeded( @onNull ArrayList<ResolveInfo> rawMatches)819     @NonNull private ArrayList<ResolveInfo> removeForwardIntentIfNotNeeded(
820             @NonNull ArrayList<ResolveInfo> rawMatches) {
821         final int numRawMatches = rawMatches.size();
822 
823         // The raw matches contain the activities that can be started but also the intents to
824         // forward the intent to the other profile
825         int numParentActivityMatches = 0;
826         int numNonParentActivityMatches = 0;
827         for (int i = 0; i < numRawMatches; i++) {
828             final ResolveInfo rawMatch = rawMatches.get(i);
829             if (!isForwardMatch(rawMatch)) {
830                 if (UserHandle.getUserHandleForUid(
831                         rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
832                     numParentActivityMatches++;
833                 } else {
834                     numNonParentActivityMatches++;
835                 }
836             }
837         }
838 
839         // If only one profile has activity matches, we need to remove all switch intents
840         if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
841             ArrayList<ResolveInfo> matches = new ArrayList<>(
842                     numParentActivityMatches + numNonParentActivityMatches);
843 
844             for (int i = 0; i < numRawMatches; i++) {
845                 ResolveInfo rawMatch = rawMatches.get(i);
846                 if (!isForwardMatch(rawMatch)) {
847                     matches.add(rawMatch);
848                 }
849             }
850             return matches;
851 
852         } else {
853             return rawMatches;
854         }
855     }
856 
getDeviceMatchesLocked(UsbDevice device, Intent intent)857     private ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
858         ArrayList<ResolveInfo> matches = new ArrayList<>();
859         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
860         int count = resolveInfos.size();
861         for (int i = 0; i < count; i++) {
862             ResolveInfo resolveInfo = resolveInfos.get(i);
863             if (packageMatchesLocked(resolveInfo, device, null)) {
864                 matches.add(resolveInfo);
865             }
866         }
867 
868         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
869     }
870 
getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)871     private ArrayList<ResolveInfo> getAccessoryMatchesLocked(
872             UsbAccessory accessory, Intent intent) {
873         ArrayList<ResolveInfo> matches = new ArrayList<>();
874         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
875         int count = resolveInfos.size();
876         for (int i = 0; i < count; i++) {
877             ResolveInfo resolveInfo = resolveInfos.get(i);
878             if (packageMatchesLocked(resolveInfo, null, accessory)) {
879                 matches.add(resolveInfo);
880             }
881         }
882 
883         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
884     }
885 
deviceAttached(UsbDevice device)886     public void deviceAttached(UsbDevice device) {
887         final Intent intent = createDeviceAttachedIntent(device);
888 
889         // Send broadcast to running activities with registered intent
890         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
891 
892         resolveActivity(intent, device, true /* showMtpNotification */);
893     }
894 
resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification)895     private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
896         final ArrayList<ResolveInfo> matches;
897         final ActivityInfo defaultActivity;
898         synchronized (mLock) {
899             matches = getDeviceMatchesLocked(device, intent);
900             defaultActivity = getDefaultActivityLocked(
901                     matches, mDevicePreferenceMap.get(new DeviceFilter(device)));
902         }
903 
904         if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
905                 mPackageManager, device) && defaultActivity == null) {
906             // Show notification if the device is MTP storage.
907             mMtpNotificationManager.showNotification(device);
908             return;
909         }
910 
911         // Start activity with registered intent
912         resolveActivity(intent, matches, defaultActivity, device, null);
913     }
914 
deviceAttachedForFixedHandler(UsbDevice device, ComponentName component)915     public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
916         final Intent intent = createDeviceAttachedIntent(device);
917 
918         // Send broadcast to running activity with registered intent
919         mContext.sendBroadcastAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
920 
921         ApplicationInfo appInfo;
922         try {
923             // Fixed handlers are always for parent user
924             appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0,
925                     mParentUser.getIdentifier());
926         } catch (NameNotFoundException e) {
927             Slog.e(TAG, "Default USB handling package (" + component.getPackageName()
928                     + ") not found  for user " + mParentUser);
929             return;
930         }
931 
932         mSettingsManager.mUsbService.getPermissionsForUser(UserHandle.getUserId(appInfo.uid))
933                 .grantDevicePermission(device, appInfo.uid);
934 
935         Intent activityIntent = new Intent(intent);
936         activityIntent.setComponent(component);
937         try {
938             mContext.startActivityAsUser(activityIntent, mParentUser);
939         } catch (ActivityNotFoundException e) {
940             Slog.e(TAG, "unable to start activity " + activityIntent);
941         }
942     }
943 
944     /**
945      * Remove notifications for a usb device.
946      *
947      * @param device The device the notifications are for.
948      */
usbDeviceRemoved(@onNull UsbDevice device)949     void usbDeviceRemoved(@NonNull UsbDevice device) {
950         mMtpNotificationManager.hideNotification(device.getDeviceId());
951     }
952 
accessoryAttached(UsbAccessory accessory)953     public void accessoryAttached(UsbAccessory accessory) {
954         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
955         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
956         intent.addFlags(
957                 Intent.FLAG_ACTIVITY_NEW_TASK |
958                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
959 
960         final ArrayList<ResolveInfo> matches;
961         final ActivityInfo defaultActivity;
962         synchronized (mLock) {
963             matches = getAccessoryMatchesLocked(accessory, intent);
964             defaultActivity = getDefaultActivityLocked(
965                     matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
966         }
967 
968         resolveActivity(intent, matches, defaultActivity, null, accessory);
969     }
970 
971     /**
972      * Start the appropriate package when an device/accessory got attached.
973      *
974      * @param intent The intent to start the package
975      * @param matches The available resolutions of the intent
976      * @param defaultActivity The default activity for the device (if set)
977      * @param device The device if a device was attached
978      * @param accessory The accessory if a device was attached
979      */
resolveActivity(@onNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory)980     private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
981             @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
982             @Nullable UsbAccessory accessory) {
983         // Remove all matches which are on the denied list
984         ArraySet deniedPackages = null;
985         if (device != null) {
986             deniedPackages = mDevicePreferenceDeniedMap.get(new DeviceFilter(device));
987         } else if (accessory != null) {
988             deniedPackages = mAccessoryPreferenceDeniedMap.get(new AccessoryFilter(accessory));
989         }
990         if (deniedPackages != null) {
991             for (int i = matches.size() - 1; i >= 0; i--) {
992                 ResolveInfo match = matches.get(i);
993                 String packageName = match.activityInfo.packageName;
994                 UserHandle user = UserHandle
995                         .getUserHandleForUid(match.activityInfo.applicationInfo.uid);
996                 if (deniedPackages.contains(new UserPackage(packageName, user))) {
997                     matches.remove(i);
998                 }
999             }
1000         }
1001 
1002         // don't show the resolver activity if there are no choices available
1003         if (matches.size() == 0) {
1004             if (accessory != null) {
1005                 mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser);
1006             }
1007             // do nothing
1008             return;
1009         }
1010 
1011         if (defaultActivity != null) {
1012             UsbUserPermissionManager defaultRIUserPermissions =
1013                     mSettingsManager.mUsbService.getPermissionsForUser(
1014                             UserHandle.getUserId(defaultActivity.applicationInfo.uid));
1015             // grant permission for default activity
1016             if (device != null) {
1017                 defaultRIUserPermissions
1018                         .grantDevicePermission(device, defaultActivity.applicationInfo.uid);
1019             } else if (accessory != null) {
1020                 defaultRIUserPermissions.grantAccessoryPermission(accessory,
1021                         defaultActivity.applicationInfo.uid);
1022             }
1023 
1024             // start default activity directly
1025             try {
1026                 intent.setComponent(
1027                         new ComponentName(defaultActivity.packageName, defaultActivity.name));
1028 
1029                 UserHandle user = UserHandle.getUserHandleForUid(
1030                         defaultActivity.applicationInfo.uid);
1031                 mContext.startActivityAsUser(intent, user);
1032             } catch (ActivityNotFoundException e) {
1033                 Slog.e(TAG, "startActivity failed", e);
1034             }
1035         } else {
1036             if (matches.size() == 1) {
1037                 mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory);
1038             } else {
1039                 mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent);
1040             }
1041         }
1042     }
1043 
1044     /**
1045      * Returns a default activity for matched ResolveInfo.
1046      * @param matches Resolved activities matched with connected device/accesary.
1047      * @param userPackage Default activity choosed by a user before. Should be null if no activity
1048      *     is choosed by a user.
1049      * @return Default activity
1050      */
getDefaultActivityLocked( @onNull ArrayList<ResolveInfo> matches, @Nullable UserPackage userPackage)1051     private @Nullable ActivityInfo getDefaultActivityLocked(
1052             @NonNull ArrayList<ResolveInfo> matches,
1053             @Nullable UserPackage userPackage) {
1054         if (userPackage != null) {
1055             // look for default activity
1056             for (final ResolveInfo info : matches) {
1057                 if (info.activityInfo != null && userPackage.equals(
1058                         new UserPackage(info.activityInfo.packageName,
1059                                 UserHandle.getUserHandleForUid(
1060                                         info.activityInfo.applicationInfo.uid)))) {
1061                     return info.activityInfo;
1062                 }
1063             }
1064         }
1065 
1066         if (matches.size() == 1) {
1067             final ActivityInfo activityInfo = matches.get(0).activityInfo;
1068             if (activityInfo != null) {
1069                 if (mDisablePermissionDialogs) {
1070                     return activityInfo;
1071                 }
1072                 // System apps are considered default unless there are other matches
1073                 if (activityInfo.applicationInfo != null
1074                         && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
1075                                 != 0) {
1076                     return activityInfo;
1077                 }
1078             }
1079         }
1080 
1081         return null;
1082     }
1083 
1084     @GuardedBy("mLock")
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull DeviceFilter filter)1085     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1086             @NonNull DeviceFilter filter) {
1087         ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
1088 
1089         // The keys in mDevicePreferenceMap are filters that match devices very narrowly
1090         for (DeviceFilter device : mDevicePreferenceMap.keySet()) {
1091             if (filter.contains(device)) {
1092                 UserPackage currentMatch = mDevicePreferenceMap.get(device);
1093                 if (!currentMatch.equals(userPackage)) {
1094                     keysToRemove.add(device);
1095                 }
1096             }
1097         }
1098 
1099         if (!keysToRemove.isEmpty()) {
1100             for (DeviceFilter keyToRemove : keysToRemove) {
1101                 mDevicePreferenceMap.remove(keyToRemove);
1102             }
1103         }
1104 
1105         return !keysToRemove.isEmpty();
1106     }
1107 
1108     @GuardedBy("mLock")
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull AccessoryFilter filter)1109     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
1110             @NonNull AccessoryFilter filter) {
1111         ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
1112 
1113         // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly
1114         for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) {
1115             if (filter.contains(accessory)) {
1116                 UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory);
1117                 if (!currentMatch.equals(userPackage)) {
1118                     keysToRemove.add(accessory);
1119                 }
1120             }
1121         }
1122 
1123         if (!keysToRemove.isEmpty()) {
1124             for (AccessoryFilter keyToRemove : keysToRemove) {
1125                 mAccessoryPreferenceMap.remove(keyToRemove);
1126             }
1127         }
1128 
1129         return !keysToRemove.isEmpty();
1130     }
1131 
1132     @GuardedBy("mLock")
handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo, String metaDataName)1133     private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
1134             String metaDataName) {
1135         XmlResourceParser parser = null;
1136         boolean changed = false;
1137 
1138         try {
1139             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
1140             if (parser == null) return false;
1141 
1142             XmlUtils.nextElement(parser);
1143             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
1144                 String tagName = parser.getName();
1145                 if ("usb-device".equals(tagName)) {
1146                     DeviceFilter filter = DeviceFilter.read(parser);
1147                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
1148                         changed = true;
1149                     }
1150                 }
1151                 else if ("usb-accessory".equals(tagName)) {
1152                     AccessoryFilter filter = AccessoryFilter.read(parser);
1153                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
1154                         changed = true;
1155                     }
1156                 }
1157                 XmlUtils.nextElement(parser);
1158             }
1159         } catch (Exception e) {
1160             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
1161         } finally {
1162             if (parser != null) parser.close();
1163         }
1164         return changed;
1165     }
1166 
1167     // Check to see if the package supports any USB devices or accessories.
1168     // If so, clear any preferences for matching devices/accessories.
handlePackageAdded(@onNull UserPackage userPackage)1169     private void handlePackageAdded(@NonNull UserPackage userPackage) {
1170         synchronized (mLock) {
1171             PackageInfo info;
1172             boolean changed = false;
1173 
1174             try {
1175                 info = mPackageManager.getPackageInfoAsUser(userPackage.packageName,
1176                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
1177                         userPackage.user.getIdentifier());
1178             } catch (NameNotFoundException e) {
1179                 Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e);
1180                 return;
1181             }
1182 
1183             ActivityInfo[] activities = info.activities;
1184             if (activities == null) return;
1185             for (int i = 0; i < activities.length; i++) {
1186                 // check for meta-data, both for devices and accessories
1187                 if (handlePackageAddedLocked(userPackage, activities[i],
1188                         UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
1189                     changed = true;
1190                 }
1191 
1192                 if (handlePackageAddedLocked(userPackage, activities[i],
1193                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
1194                     changed = true;
1195                 }
1196             }
1197 
1198             if (changed) {
1199                 scheduleWriteSettingsLocked();
1200             }
1201         }
1202     }
1203 
1204     /**
1205      * Get the serial number for a user handle.
1206      *
1207      * @param user The user handle
1208      *
1209      * @return The serial number
1210      */
getSerial(@onNull UserHandle user)1211     private int getSerial(@NonNull UserHandle user) {
1212         return mUserManager.getUserSerialNumber(user.getIdentifier());
1213     }
1214 
1215     /**
1216      * Set a package as default handler for a device.
1217      *
1218      * @param device The device that should be handled by default
1219      * @param packageName The default handler package
1220      * @param user The user the package belongs to
1221      */
setDevicePackage(@onNull UsbDevice device, @Nullable String packageName, @NonNull UserHandle user)1222     void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
1223             @NonNull UserHandle user) {
1224         DeviceFilter filter = new DeviceFilter(device);
1225         boolean changed;
1226         synchronized (mLock) {
1227             if (packageName == null) {
1228                 changed = (mDevicePreferenceMap.remove(filter) != null);
1229             } else {
1230                 UserPackage userPackage = new UserPackage(packageName, user);
1231 
1232                 changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
1233                 if (changed) {
1234                     mDevicePreferenceMap.put(filter, userPackage);
1235                 }
1236             }
1237             if (changed) {
1238                 scheduleWriteSettingsLocked();
1239             }
1240         }
1241     }
1242 
1243     /**
1244      * Add package to the denied for handling a device
1245      *
1246      * @param device the device to add to the denied
1247      * @param packageNames the packages to not become handler
1248      * @param user the user
1249      */
addDevicePackagesToDenied(@onNull UsbDevice device, @NonNull String[] packageNames, @NonNull UserHandle user)1250     void addDevicePackagesToDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
1251             @NonNull UserHandle user) {
1252         if (packageNames.length == 0) {
1253             return;
1254         }
1255         DeviceFilter filter = new DeviceFilter(device);
1256 
1257         synchronized (mLock) {
1258             ArraySet<UserPackage> userPackages;
1259             if (mDevicePreferenceDeniedMap.containsKey(filter)) {
1260                 userPackages = mDevicePreferenceDeniedMap.get(filter);
1261             } else {
1262                 userPackages = new ArraySet<>();
1263                 mDevicePreferenceDeniedMap.put(filter, userPackages);
1264             }
1265 
1266             boolean shouldWrite = false;
1267             for (String packageName : packageNames) {
1268                 UserPackage userPackage = new UserPackage(packageName, user);
1269                 if (!userPackages.contains(userPackage)) {
1270                     userPackages.add(userPackage);
1271                     shouldWrite = true;
1272                 }
1273             }
1274 
1275             if (shouldWrite) {
1276                 scheduleWriteSettingsLocked();
1277             }
1278         }
1279     }
1280 
1281     /**
1282      * Add package to the denied for handling a accessory
1283      *
1284      * @param accessory the accessory to add to the denied
1285      * @param packageNames the packages to not become handler
1286      * @param user the user
1287      */
addAccessoryPackagesToDenied(@onNull UsbAccessory accessory, @NonNull String[] packageNames, @NonNull UserHandle user)1288     void addAccessoryPackagesToDenied(@NonNull UsbAccessory accessory,
1289             @NonNull String[] packageNames, @NonNull UserHandle user) {
1290         if (packageNames.length == 0) {
1291             return;
1292         }
1293         AccessoryFilter filter = new AccessoryFilter(accessory);
1294 
1295         synchronized (mLock) {
1296             ArraySet<UserPackage> userPackages;
1297             if (mAccessoryPreferenceDeniedMap.containsKey(filter)) {
1298                 userPackages = mAccessoryPreferenceDeniedMap.get(filter);
1299             } else {
1300                 userPackages = new ArraySet<>();
1301                 mAccessoryPreferenceDeniedMap.put(filter, userPackages);
1302             }
1303 
1304             boolean shouldWrite = false;
1305             for (String packageName : packageNames) {
1306                 UserPackage userPackage = new UserPackage(packageName, user);
1307                 if (!userPackages.contains(userPackage)) {
1308                     userPackages.add(userPackage);
1309                     shouldWrite = true;
1310                 }
1311             }
1312 
1313             if (shouldWrite) {
1314                 scheduleWriteSettingsLocked();
1315             }
1316         }
1317     }
1318 
1319     /**
1320      * Remove UserPackage from the denied for handling a device
1321      *
1322      * @param device the device to remove denied packages from
1323      * @param packageName the packages to remove
1324      * @param user the user
1325      */
removeDevicePackagesFromDenied(@onNull UsbDevice device, @NonNull String[] packageNames, @NonNull UserHandle user)1326     void removeDevicePackagesFromDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
1327             @NonNull UserHandle user) {
1328         DeviceFilter filter = new DeviceFilter(device);
1329 
1330         synchronized (mLock) {
1331             ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.get(filter);
1332 
1333             if (userPackages != null) {
1334                 boolean shouldWrite = false;
1335                 for (String packageName : packageNames) {
1336                     UserPackage userPackage = new UserPackage(packageName, user);
1337 
1338                     if (userPackages.contains(userPackage)) {
1339                         userPackages.remove(userPackage);
1340                         shouldWrite = true;
1341 
1342                         if (userPackages.size() == 0) {
1343                             mDevicePreferenceDeniedMap.remove(filter);
1344                             break;
1345                         }
1346                     }
1347                 }
1348 
1349                 if (shouldWrite) {
1350                     scheduleWriteSettingsLocked();
1351                 }
1352             }
1353         }
1354     }
1355 
1356     /**
1357      * Remove UserPackage from the denied for handling a accessory
1358      *
1359      * @param accessory the accessory to remove denied packages from
1360      * @param packageName the packages to remove
1361      * @param user the user
1362      */
removeAccessoryPackagesFromDenied(@onNull UsbAccessory accessory, @NonNull String[] packageNames, @NonNull UserHandle user)1363     void removeAccessoryPackagesFromDenied(@NonNull UsbAccessory accessory,
1364             @NonNull String[] packageNames, @NonNull UserHandle user) {
1365         AccessoryFilter filter = new AccessoryFilter(accessory);
1366 
1367         synchronized (mLock) {
1368             ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.get(filter);
1369 
1370             if (userPackages != null) {
1371                 boolean shouldWrite = false;
1372                 for (String packageName : packageNames) {
1373                     UserPackage userPackage = new UserPackage(packageName, user);
1374 
1375                     if (userPackages.contains(userPackage)) {
1376                         userPackages.remove(userPackage);
1377                         shouldWrite = true;
1378 
1379                         if (userPackages.size() == 0) {
1380                             mAccessoryPreferenceDeniedMap.remove(filter);
1381                             break;
1382                         }
1383                     }
1384                 }
1385 
1386                 if (shouldWrite) {
1387                     scheduleWriteSettingsLocked();
1388                 }
1389             }
1390         }
1391     }
1392 
1393     /**
1394      * Set a package as default handler for a accessory.
1395      *
1396      * @param accessory The accessory that should be handled by default
1397      * @param packageName The default handler package
1398      * @param user The user the package belongs to
1399      */
setAccessoryPackage(@onNull UsbAccessory accessory, @Nullable String packageName, @NonNull UserHandle user)1400     void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName,
1401             @NonNull UserHandle user) {
1402         AccessoryFilter filter = new AccessoryFilter(accessory);
1403         boolean changed;
1404         synchronized (mLock) {
1405             if (packageName == null) {
1406                 changed = (mAccessoryPreferenceMap.remove(filter) != null);
1407             } else {
1408                 UserPackage userPackage = new UserPackage(packageName, user);
1409 
1410                 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));
1411                 if (changed) {
1412                     mAccessoryPreferenceMap.put(filter, userPackage);
1413                 }
1414             }
1415             if (changed) {
1416                 scheduleWriteSettingsLocked();
1417             }
1418         }
1419     }
1420 
1421     /**
1422      * Check if a package has is the default handler for any usb device or accessory.
1423      *
1424      * @param packageName The package name
1425      * @param user The user the package belongs to
1426      *
1427      * @return {@code true} iff the package is default for any usb device or accessory
1428      */
hasDefaults(@onNull String packageName, @NonNull UserHandle user)1429     boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1430         UserPackage userPackage = new UserPackage(packageName, user);
1431         synchronized (mLock) {
1432             if (mDevicePreferenceMap.values().contains(userPackage)) return true;
1433             return mAccessoryPreferenceMap.values().contains(userPackage);
1434         }
1435     }
1436 
1437     /**
1438      * Clear defaults for a package from any preference.
1439      *
1440      * @param packageName The package to remove
1441      * @param user The user the package belongs to
1442      */
clearDefaults(@onNull String packageName, @NonNull UserHandle user)1443     void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1444         UserPackage userPackage = new UserPackage(packageName, user);
1445 
1446         synchronized (mLock) {
1447             if (clearPackageDefaultsLocked(userPackage)) {
1448                 scheduleWriteSettingsLocked();
1449             }
1450         }
1451     }
1452 
1453     /**
1454      * Clear defaults for a package from any preference (does not persist).
1455      *
1456      * @param userPackage The package to remove
1457      *
1458      * @return {@code true} iff at least one preference was cleared
1459      */
clearPackageDefaultsLocked(@onNull UserPackage userPackage)1460     private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) {
1461         boolean cleared = false;
1462         synchronized (mLock) {
1463             if (mDevicePreferenceMap.containsValue(userPackage)) {
1464                 // make a copy of the key set to avoid ConcurrentModificationException
1465                 DeviceFilter[] keys = mDevicePreferenceMap.keySet().toArray(new DeviceFilter[0]);
1466                 for (int i = 0; i < keys.length; i++) {
1467                     DeviceFilter key = keys[i];
1468                     if (userPackage.equals(mDevicePreferenceMap.get(key))) {
1469                         mDevicePreferenceMap.remove(key);
1470                         cleared = true;
1471                     }
1472                 }
1473             }
1474             if (mAccessoryPreferenceMap.containsValue(userPackage)) {
1475                 // make a copy of the key set to avoid ConcurrentModificationException
1476                 AccessoryFilter[] keys =
1477                         mAccessoryPreferenceMap.keySet().toArray(new AccessoryFilter[0]);
1478                 for (int i = 0; i < keys.length; i++) {
1479                     AccessoryFilter key = keys[i];
1480                     if (userPackage.equals(mAccessoryPreferenceMap.get(key))) {
1481                         mAccessoryPreferenceMap.remove(key);
1482                         cleared = true;
1483                     }
1484                 }
1485             }
1486             return cleared;
1487         }
1488     }
1489 
dump(@onNull DualDumpOutputStream dump, @NonNull String idName, long id)1490     public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
1491         long token = dump.start(idName, id);
1492 
1493         synchronized (mLock) {
1494             dump.write("parent_user_id", UsbProfileGroupSettingsManagerProto.PARENT_USER_ID,
1495                     mParentUser.getIdentifier());
1496 
1497             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1498                 long devicePrefToken = dump.start("device_preferences",
1499                         UsbProfileGroupSettingsManagerProto.DEVICE_PREFERENCES);
1500 
1501                 filter.dump(dump, "filter", UsbSettingsDevicePreferenceProto.FILTER);
1502 
1503                 mDevicePreferenceMap.get(filter).dump(dump, "user_package",
1504                         UsbSettingsDevicePreferenceProto.USER_PACKAGE);
1505 
1506                 dump.end(devicePrefToken);
1507             }
1508             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1509                 long accessoryPrefToken = dump.start("accessory_preferences",
1510                         UsbProfileGroupSettingsManagerProto.ACCESSORY_PREFERENCES);
1511 
1512                 filter.dump(dump, "filter", UsbSettingsAccessoryPreferenceProto.FILTER);
1513 
1514                 mAccessoryPreferenceMap.get(filter).dump(dump, "user_package",
1515                         UsbSettingsAccessoryPreferenceProto.USER_PACKAGE);
1516 
1517                 dump.end(accessoryPrefToken);
1518             }
1519         }
1520 
1521         dump.end(token);
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