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