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