1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.usb;
18 
19 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ActivityNotFoundException;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.content.pm.ResolveInfo;
33 import android.content.pm.UserInfo;
34 import android.content.res.XmlResourceParser;
35 import android.hardware.usb.AccessoryFilter;
36 import android.hardware.usb.DeviceFilter;
37 import android.hardware.usb.UsbAccessory;
38 import android.hardware.usb.UsbDevice;
39 import android.hardware.usb.UsbManager;
40 import android.os.AsyncTask;
41 import android.os.Environment;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 import android.service.usb.UsbProfileGroupSettingsManagerProto;
45 import android.service.usb.UsbSettingsAccessoryPreferenceProto;
46 import android.service.usb.UsbSettingsDevicePreferenceProto;
47 import android.service.usb.UserPackageProto;
48 import android.util.AtomicFile;
49 import android.util.Log;
50 import android.util.Slog;
51 import android.util.SparseArray;
52 import android.util.SparseIntArray;
53 import android.util.Xml;
54 
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.annotations.Immutable;
57 import com.android.internal.content.PackageMonitor;
58 import com.android.internal.util.FastXmlSerializer;
59 import com.android.internal.util.XmlUtils;
60 import com.android.internal.util.dump.DualDumpOutputStream;
61 
62 import libcore.io.IoUtils;
63 
64 import org.xmlpull.v1.XmlPullParser;
65 import org.xmlpull.v1.XmlPullParserException;
66 
67 import java.io.File;
68 import java.io.FileInputStream;
69 import java.io.FileNotFoundException;
70 import java.io.FileOutputStream;
71 import java.io.IOException;
72 import java.nio.charset.StandardCharsets;
73 import java.util.ArrayList;
74 import java.util.HashMap;
75 import java.util.Iterator;
76 import java.util.List;
77 import java.util.Map;
78 
79 class UsbProfileGroupSettingsManager {
80     private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
81     private static final boolean DEBUG = false;
82 
83     /** Legacy settings file, before multi-user */
84     private static final File sSingleUserSettingsFile = new File(
85             "/data/system/usb_device_manager.xml");
86 
87     /** The parent user (main user of the profile group) */
88     private final UserHandle mParentUser;
89 
90     private final AtomicFile mSettingsFile;
91     private final boolean mDisablePermissionDialogs;
92 
93     private final Context mContext;
94 
95     private final PackageManager mPackageManager;
96 
97     private final UserManager mUserManager;
98     private final @NonNull UsbSettingsManager mSettingsManager;
99 
100     /** Maps DeviceFilter to user preferred application package */
101     @GuardedBy("mLock")
102     private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
103 
104     /** Maps AccessoryFilter to user preferred application package */
105     @GuardedBy("mLock")
106     private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
107 
108     private final Object mLock = new Object();
109 
110     /**
111      * If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently
112      * scheduled.
113      */
114     @GuardedBy("mLock")
115     private boolean mIsWriteSettingsScheduled;
116 
117     /**
118      * A package of a user.
119      */
120     @Immutable
121     private static class UserPackage {
122         /** User */
123         final @NonNull UserHandle user;
124 
125         /** Package name */
126         final @NonNull String packageName;
127 
128         /**
129          * Create a description of a per user package.
130          *
131          * @param packageName The name of the package
132          * @param user The user
133          */
UserPackage(@onNull String packageName, @NonNull UserHandle user)134         private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
135             this.packageName = packageName;
136             this.user = user;
137         }
138 
139         @Override
equals(Object obj)140         public boolean equals(Object obj) {
141             if (!(obj instanceof UserPackage)) {
142                 return false;
143             } else {
144                 UserPackage other = (UserPackage)obj;
145 
146                 return user.equals(other.user) && packageName.equals(other.packageName);
147             }
148         }
149 
150         @Override
hashCode()151         public int hashCode() {
152             int result = user.hashCode();
153             result = 31 * result + packageName.hashCode();
154             return result;
155         }
156 
157         @Override
toString()158         public String toString() {
159             return user.getIdentifier() + "/" + packageName;
160         }
161 
dump(DualDumpOutputStream dump, String idName, long id)162         public void dump(DualDumpOutputStream dump, String idName, long id) {
163             long token = dump.start(idName, id);
164 
165             dump.write("user_id", UserPackageProto.USER_ID, user.getIdentifier());
166             dump.write("package_name", UserPackageProto.PACKAGE_NAME, packageName);
167 
168             dump.end(token);
169         }
170     }
171 
172     private class MyPackageMonitor extends PackageMonitor {
173         @Override
onPackageAdded(String packageName, int uid)174         public void onPackageAdded(String packageName, int uid) {
175             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
176                     UserHandle.getUserId(uid))) {
177                 return;
178             }
179 
180             handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));
181         }
182 
183         @Override
onPackageRemoved(String packageName, int uid)184         public void onPackageRemoved(String packageName, int uid) {
185             if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(),
186                     UserHandle.getUserId(uid))) {
187                 return;
188             }
189 
190             clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
191         }
192     }
193 
194     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
195 
196     private final MtpNotificationManager mMtpNotificationManager;
197 
198     /**
199      * Create new settings manager for a profile group.
200      *
201      * @param context The context of the service
202      * @param user The parent profile
203      * @param settingsManager The settings manager of the service
204      */
UsbProfileGroupSettingsManager(@onNull Context context, @NonNull UserHandle user, @NonNull UsbSettingsManager settingsManager)205     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
206             @NonNull UsbSettingsManager settingsManager) {
207         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
208 
209         Context parentUserContext;
210         try {
211             parentUserContext = context.createPackageContextAsUser("android", 0, user);
212         } catch (NameNotFoundException e) {
213             throw new RuntimeException("Missing android package");
214         }
215 
216         mContext = context;
217         mPackageManager = context.getPackageManager();
218         mSettingsManager = settingsManager;
219         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
220 
221         mParentUser = user;
222         mSettingsFile = new AtomicFile(new File(
223                 Environment.getUserSystemDirectory(user.getIdentifier()),
224                 "usb_device_manager.xml"), "usb-state");
225 
226         mDisablePermissionDialogs = context.getResources().getBoolean(
227                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
228 
229         synchronized (mLock) {
230             if (UserHandle.SYSTEM.equals(user)) {
231                 upgradeSingleUserLocked();
232             }
233             readSettingsLocked();
234         }
235 
236         mPackageMonitor.register(context, null, UserHandle.ALL, true);
237         mMtpNotificationManager = new MtpNotificationManager(
238                 parentUserContext,
239                 device -> resolveActivity(createDeviceAttachedIntent(device),
240                         device, false /* showMtpNotification */));
241     }
242 
243     /**
244      * Remove all defaults for a user.
245      *
246      * @param userToRemove The user the defaults belong to.
247      */
removeAllDefaultsForUser(@onNull UserHandle userToRemove)248     void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
249         synchronized (mLock) {
250             boolean needToPersist = false;
251             Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
252                     .entrySet().iterator();
253             while (devicePreferenceIt.hasNext()) {
254                 Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next();
255 
256                 if (entry.getValue().user.equals(userToRemove)) {
257                     devicePreferenceIt.remove();
258                     needToPersist = true;
259                 }
260             }
261 
262             Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt =
263                     mAccessoryPreferenceMap.entrySet().iterator();
264             while (accessoryPreferenceIt.hasNext()) {
265                 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next();
266 
267                 if (entry.getValue().user.equals(userToRemove)) {
268                     accessoryPreferenceIt.remove();
269                     needToPersist = true;
270                 }
271             }
272 
273             if (needToPersist) {
274                 scheduleWriteSettingsLocked();
275             }
276         }
277     }
278 
readPreference(XmlPullParser parser)279     private void readPreference(XmlPullParser parser)
280             throws XmlPullParserException, IOException {
281         String packageName = null;
282 
283         // If not set, assume it to be the parent profile
284         UserHandle user = mParentUser;
285 
286         int count = parser.getAttributeCount();
287         for (int i = 0; i < count; i++) {
288             if ("package".equals(parser.getAttributeName(i))) {
289                 packageName = parser.getAttributeValue(i);
290             }
291             if ("user".equals(parser.getAttributeName(i))) {
292                 // Might return null if user is not known anymore
293                 user = mUserManager
294                         .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
295             }
296         }
297 
298         XmlUtils.nextElement(parser);
299         if ("usb-device".equals(parser.getName())) {
300             DeviceFilter filter = DeviceFilter.read(parser);
301             if (user != null) {
302                 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
303             }
304         } else if ("usb-accessory".equals(parser.getName())) {
305             AccessoryFilter filter = AccessoryFilter.read(parser);
306             if (user != null) {
307                 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
308             }
309         }
310         XmlUtils.nextElement(parser);
311     }
312 
313     /**
314      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
315      * Should only by called by owner.
316      */
317     @GuardedBy("mLock")
upgradeSingleUserLocked()318     private void upgradeSingleUserLocked() {
319         if (sSingleUserSettingsFile.exists()) {
320             mDevicePreferenceMap.clear();
321             mAccessoryPreferenceMap.clear();
322 
323             FileInputStream fis = null;
324             try {
325                 fis = new FileInputStream(sSingleUserSettingsFile);
326                 XmlPullParser parser = Xml.newPullParser();
327                 parser.setInput(fis, StandardCharsets.UTF_8.name());
328 
329                 XmlUtils.nextElement(parser);
330                 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
331                     final String tagName = parser.getName();
332                     if ("preference".equals(tagName)) {
333                         readPreference(parser);
334                     } else {
335                         XmlUtils.nextElement(parser);
336                     }
337                 }
338             } catch (IOException | XmlPullParserException e) {
339                 Log.wtf(TAG, "Failed to read single-user settings", e);
340             } finally {
341                 IoUtils.closeQuietly(fis);
342             }
343 
344             scheduleWriteSettingsLocked();
345 
346             // Success or failure, we delete single-user file
347             sSingleUserSettingsFile.delete();
348         }
349     }
350 
351     @GuardedBy("mLock")
readSettingsLocked()352     private void readSettingsLocked() {
353         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
354 
355         mDevicePreferenceMap.clear();
356         mAccessoryPreferenceMap.clear();
357 
358         FileInputStream stream = null;
359         try {
360             stream = mSettingsFile.openRead();
361             XmlPullParser parser = Xml.newPullParser();
362             parser.setInput(stream, StandardCharsets.UTF_8.name());
363 
364             XmlUtils.nextElement(parser);
365             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
366                 String tagName = parser.getName();
367                 if ("preference".equals(tagName)) {
368                     readPreference(parser);
369                 } else {
370                     XmlUtils.nextElement(parser);
371                 }
372             }
373         } catch (FileNotFoundException e) {
374             if (DEBUG) Slog.d(TAG, "settings file not found");
375         } catch (Exception e) {
376             Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
377             mSettingsFile.delete();
378         } finally {
379             IoUtils.closeQuietly(stream);
380         }
381     }
382 
383     /**
384      * Schedule a async task to persist {@link #mDevicePreferenceMap} and
385      * {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do
386      * nothing as the currently scheduled one will do the work.
387      * <p>Called with {@link #mLock} held.</p>
388      * <p>In the uncommon case that the system crashes in between the scheduling and the write the
389      * update is lost.</p>
390      */
391     @GuardedBy("mLock")
scheduleWriteSettingsLocked()392     private void scheduleWriteSettingsLocked() {
393         if (mIsWriteSettingsScheduled) {
394             return;
395         } else {
396             mIsWriteSettingsScheduled = true;
397         }
398 
399         AsyncTask.execute(() -> {
400             synchronized (mLock) {
401                 FileOutputStream fos = null;
402                 try {
403                     fos = mSettingsFile.startWrite();
404 
405                     FastXmlSerializer serializer = new FastXmlSerializer();
406                     serializer.setOutput(fos, StandardCharsets.UTF_8.name());
407                     serializer.startDocument(null, true);
408                     serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
409                                     true);
410                     serializer.startTag(null, "settings");
411 
412                     for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
413                         serializer.startTag(null, "preference");
414                         serializer.attribute(null, "package",
415                                 mDevicePreferenceMap.get(filter).packageName);
416                         serializer.attribute(null, "user",
417                                 String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
418                         filter.write(serializer);
419                         serializer.endTag(null, "preference");
420                     }
421 
422                     for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
423                         serializer.startTag(null, "preference");
424                         serializer.attribute(null, "package",
425                                 mAccessoryPreferenceMap.get(filter).packageName);
426                         serializer.attribute(null, "user", String.valueOf(
427                                         getSerial(mAccessoryPreferenceMap.get(filter).user)));
428                         filter.write(serializer);
429                         serializer.endTag(null, "preference");
430                     }
431 
432                     serializer.endTag(null, "settings");
433                     serializer.endDocument();
434 
435                     mSettingsFile.finishWrite(fos);
436                 } catch (IOException e) {
437                     Slog.e(TAG, "Failed to write settings", e);
438                     if (fos != null) {
439                         mSettingsFile.failWrite(fos);
440                     }
441                 }
442 
443                 mIsWriteSettingsScheduled = false;
444             }
445         });
446     }
447 
448     // Checks to see if a package matches a device or accessory.
449     // Only one of device and accessory should be non-null.
packageMatchesLocked(ResolveInfo info, String metaDataName, UsbDevice device, UsbAccessory accessory)450     private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
451             UsbDevice device, UsbAccessory accessory) {
452         if (isForwardMatch(info)) {
453             return true;
454         }
455 
456         ActivityInfo ai = info.activityInfo;
457 
458         XmlResourceParser parser = null;
459         try {
460             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
461             if (parser == null) {
462                 Slog.w(TAG, "no meta-data for " + info);
463                 return false;
464             }
465 
466             XmlUtils.nextElement(parser);
467             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
468                 String tagName = parser.getName();
469                 if (device != null && "usb-device".equals(tagName)) {
470                     DeviceFilter filter = DeviceFilter.read(parser);
471                     if (filter.matches(device)) {
472                         return true;
473                     }
474                 }
475                 else if (accessory != null && "usb-accessory".equals(tagName)) {
476                     AccessoryFilter filter = AccessoryFilter.read(parser);
477                     if (filter.matches(accessory)) {
478                         return true;
479                     }
480                 }
481                 XmlUtils.nextElement(parser);
482             }
483         } catch (Exception e) {
484             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
485         } finally {
486             if (parser != null) parser.close();
487         }
488         return false;
489     }
490 
491     /**
492      * Resolve all activities that match an intent for all profiles of this group.
493      *
494      * @param intent The intent to resolve
495      *
496      * @return The {@link ResolveInfo}s for all profiles of the group
497      */
queryIntentActivitiesForAllProfiles( @onNull Intent intent)498     private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
499             @NonNull Intent intent) {
500         List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
501 
502         ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
503         int numProfiles = profiles.size();
504         for (int i = 0; i < numProfiles; i++) {
505             resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent,
506                     PackageManager.GET_META_DATA, profiles.get(i).id));
507         }
508 
509         return resolveInfos;
510     }
511 
512     /**
513      * If this match used to forward the intent to another profile?
514      *
515      * @param match The match
516      *
517      * @return {@code true} iff this is such a forward match
518      */
isForwardMatch(@onNull ResolveInfo match)519     private boolean isForwardMatch(@NonNull ResolveInfo match) {
520         return match.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE);
521     }
522 
523     /**
524      * Only return those matches with the highest priority.
525      *
526      * @param matches All matches, some might have lower priority
527      *
528      * @return The matches with the highest priority
529      */
530     @NonNull
preferHighPriority(@onNull ArrayList<ResolveInfo> matches)531     private ArrayList<ResolveInfo> preferHighPriority(@NonNull ArrayList<ResolveInfo> matches) {
532         SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
533         SparseIntArray highestPriorityByUserId = new SparseIntArray();
534         ArrayList<ResolveInfo> forwardMatches = new ArrayList<>();
535 
536         // Create list of highest priority matches per user in highestPriorityMatchesByUserId
537         int numMatches = matches.size();
538         for (int matchNum = 0; matchNum < numMatches; matchNum++) {
539             ResolveInfo match = matches.get(matchNum);
540 
541             // Unnecessary forward matches are filtered out later, hence collect them all to add
542             // them below
543             if (isForwardMatch(match)) {
544                 forwardMatches.add(match);
545                 continue;
546             }
547 
548             // If this a previously unknown user?
549             if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) {
550                 highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE);
551                 highestPriorityMatchesByUserId.put(match.targetUserId, new ArrayList<>());
552             }
553 
554             // Find current highest priority matches for the current user
555             int highestPriority = highestPriorityByUserId.get(match.targetUserId);
556             ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get(
557                     match.targetUserId);
558 
559             if (match.priority == highestPriority) {
560                 highestPriorityMatches.add(match);
561             } else if (match.priority > highestPriority) {
562                 highestPriorityByUserId.put(match.targetUserId, match.priority);
563 
564                 highestPriorityMatches.clear();
565                 highestPriorityMatches.add(match);
566             }
567         }
568 
569         // Combine all users (+ forward matches) back together. This means that all non-forward
570         // matches have the same priority for a user. Matches for different users might have
571         // different priority.
572         ArrayList<ResolveInfo> combinedMatches = new ArrayList<>(forwardMatches);
573         int numMatchArrays = highestPriorityMatchesByUserId.size();
574         for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) {
575             combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum));
576         }
577 
578         return combinedMatches;
579     }
580 
581     /**
582      * If there are no matches for a profile, remove the forward intent to this profile.
583      *
584      * @param rawMatches The matches that contain all forward intents
585      *
586      * @return The matches with the unnecessary forward intents removed
587      */
removeForwardIntentIfNotNeeded( @onNull ArrayList<ResolveInfo> rawMatches)588     @NonNull private ArrayList<ResolveInfo> removeForwardIntentIfNotNeeded(
589             @NonNull ArrayList<ResolveInfo> rawMatches) {
590         final int numRawMatches = rawMatches.size();
591 
592         // The raw matches contain the activities that can be started but also the intents to
593         // forward the intent to the other profile
594         int numParentActivityMatches = 0;
595         int numNonParentActivityMatches = 0;
596         for (int i = 0; i < numRawMatches; i++) {
597             final ResolveInfo rawMatch = rawMatches.get(i);
598             if (!isForwardMatch(rawMatch)) {
599                 if (UserHandle.getUserHandleForUid(
600                         rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
601                     numParentActivityMatches++;
602                 } else {
603                     numNonParentActivityMatches++;
604                 }
605             }
606         }
607 
608         // If only one profile has activity matches, we need to remove all switch intents
609         if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
610             ArrayList<ResolveInfo> matches = new ArrayList<>(
611                     numParentActivityMatches + numNonParentActivityMatches);
612 
613             for (int i = 0; i < numRawMatches; i++) {
614                 ResolveInfo rawMatch = rawMatches.get(i);
615                 if (!isForwardMatch(rawMatch)) {
616                     matches.add(rawMatch);
617                 }
618             }
619             return matches;
620 
621         } else {
622             return rawMatches;
623         }
624     }
625 
getDeviceMatchesLocked(UsbDevice device, Intent intent)626     private ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
627         ArrayList<ResolveInfo> matches = new ArrayList<>();
628         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
629         int count = resolveInfos.size();
630         for (int i = 0; i < count; i++) {
631             ResolveInfo resolveInfo = resolveInfos.get(i);
632             if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
633                 matches.add(resolveInfo);
634             }
635         }
636 
637         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
638     }
639 
getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)640     private ArrayList<ResolveInfo> getAccessoryMatchesLocked(
641             UsbAccessory accessory, Intent intent) {
642         ArrayList<ResolveInfo> matches = new ArrayList<>();
643         List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
644         int count = resolveInfos.size();
645         for (int i = 0; i < count; i++) {
646             ResolveInfo resolveInfo = resolveInfos.get(i);
647             if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
648                 matches.add(resolveInfo);
649             }
650         }
651 
652         return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
653     }
654 
deviceAttached(UsbDevice device)655     public void deviceAttached(UsbDevice device) {
656         final Intent intent = createDeviceAttachedIntent(device);
657 
658         // Send broadcast to running activities with registered intent
659         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
660 
661         resolveActivity(intent, device, true /* showMtpNotification */);
662     }
663 
resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification)664     private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
665         final ArrayList<ResolveInfo> matches;
666         final ActivityInfo defaultActivity;
667         synchronized (mLock) {
668             matches = getDeviceMatchesLocked(device, intent);
669             defaultActivity = getDefaultActivityLocked(
670                     matches, mDevicePreferenceMap.get(new DeviceFilter(device)));
671         }
672 
673         if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
674                 mPackageManager, device) && defaultActivity == null) {
675             // Show notification if the device is MTP storage.
676             mMtpNotificationManager.showNotification(device);
677             return;
678         }
679 
680         // Start activity with registered intent
681         resolveActivity(intent, matches, defaultActivity, device, null);
682     }
683 
deviceAttachedForFixedHandler(UsbDevice device, ComponentName component)684     public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
685         final Intent intent = createDeviceAttachedIntent(device);
686 
687         // Send broadcast to running activity with registered intent
688         mContext.sendBroadcast(intent);
689 
690         ApplicationInfo appInfo;
691         try {
692             // Fixed handlers are always for parent user
693             appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0,
694                     mParentUser.getIdentifier());
695         } catch (NameNotFoundException e) {
696             Slog.e(TAG, "Default USB handling package (" + component.getPackageName()
697                     + ") not found  for user " + mParentUser);
698             return;
699         }
700 
701         mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
702                 .grantDevicePermission(device, appInfo.uid);
703 
704         Intent activityIntent = new Intent(intent);
705         activityIntent.setComponent(component);
706         try {
707             mContext.startActivityAsUser(activityIntent, mParentUser);
708         } catch (ActivityNotFoundException e) {
709             Slog.e(TAG, "unable to start activity " + activityIntent);
710         }
711     }
712 
713     /**
714      * Remove notifications for a usb device.
715      *
716      * @param device The device the notifications are for.
717      */
usbDeviceRemoved(@onNull UsbDevice device)718     void usbDeviceRemoved(@NonNull UsbDevice device) {
719         mMtpNotificationManager.hideNotification(device.getDeviceId());
720     }
721 
accessoryAttached(UsbAccessory accessory)722     public void accessoryAttached(UsbAccessory accessory) {
723         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
724         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
725         intent.addFlags(
726                 Intent.FLAG_ACTIVITY_NEW_TASK |
727                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
728 
729         final ArrayList<ResolveInfo> matches;
730         final ActivityInfo defaultActivity;
731         synchronized (mLock) {
732             matches = getAccessoryMatchesLocked(accessory, intent);
733             defaultActivity = getDefaultActivityLocked(
734                     matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
735         }
736 
737         resolveActivity(intent, matches, defaultActivity, null, accessory);
738     }
739 
740     /**
741      * Start the appropriate package when an device/accessory got attached.
742      *
743      * @param intent The intent to start the package
744      * @param matches The available resolutions of the intent
745      * @param defaultActivity The default activity for the device (if set)
746      * @param device The device if a device was attached
747      * @param accessory The accessory if a device was attached
748      */
resolveActivity(@onNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory)749     private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
750             @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
751             @Nullable UsbAccessory accessory) {
752         // don't show the resolver activity if there are no choices available
753         if (matches.size() == 0) {
754             if (accessory != null) {
755                 String uri = accessory.getUri();
756                 if (uri != null && uri.length() > 0) {
757                     // display URI to user
758                     Intent dialogIntent = new Intent();
759                     dialogIntent.setClassName("com.android.systemui",
760                             "com.android.systemui.usb.UsbAccessoryUriActivity");
761                     dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
762                     dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
763                     dialogIntent.putExtra("uri", uri);
764                     try {
765                         mContext.startActivityAsUser(dialogIntent, mParentUser);
766                     } catch (ActivityNotFoundException e) {
767                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
768                     }
769                 }
770             }
771 
772             // do nothing
773             return;
774         }
775 
776         if (defaultActivity != null) {
777             UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
778                     UserHandle.getUserId(defaultActivity.applicationInfo.uid));
779             // grant permission for default activity
780             if (device != null) {
781                 defaultRIUserSettings.
782                         grantDevicePermission(device, defaultActivity.applicationInfo.uid);
783             } else if (accessory != null) {
784                 defaultRIUserSettings.grantAccessoryPermission(accessory,
785                         defaultActivity.applicationInfo.uid);
786             }
787 
788             // start default activity directly
789             try {
790                 intent.setComponent(
791                         new ComponentName(defaultActivity.packageName, defaultActivity.name));
792 
793                 UserHandle user = UserHandle.getUserHandleForUid(
794                         defaultActivity.applicationInfo.uid);
795                 mContext.startActivityAsUser(intent, user);
796             } catch (ActivityNotFoundException e) {
797                 Slog.e(TAG, "startActivity failed", e);
798             }
799         } else {
800             Intent resolverIntent = new Intent();
801             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
802             UserHandle user;
803 
804             if (matches.size() == 1) {
805                 ResolveInfo rInfo = matches.get(0);
806 
807                 // start UsbConfirmActivity if there is only one choice
808                 resolverIntent.setClassName("com.android.systemui",
809                         "com.android.systemui.usb.UsbConfirmActivity");
810                 resolverIntent.putExtra("rinfo", rInfo);
811                 user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
812 
813                 if (device != null) {
814                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
815                 } else {
816                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
817                 }
818             } else {
819                 user = mParentUser;
820 
821                 // start UsbResolverActivity so user can choose an activity
822                 resolverIntent.setClassName("com.android.systemui",
823                         "com.android.systemui.usb.UsbResolverActivity");
824                 resolverIntent.putParcelableArrayListExtra("rlist", matches);
825                 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
826             }
827             try {
828                 mContext.startActivityAsUser(resolverIntent, user);
829             } catch (ActivityNotFoundException e) {
830                 Slog.e(TAG, "unable to start activity " + resolverIntent, e);
831             }
832         }
833     }
834 
835     /**
836      * Returns a default activity for matched ResolveInfo.
837      * @param matches Resolved activities matched with connected device/accesary.
838      * @param userPackage Default activity choosed by a user before. Should be null if no activity
839      *     is choosed by a user.
840      * @return Default activity
841      */
getDefaultActivityLocked( @onNull ArrayList<ResolveInfo> matches, @Nullable UserPackage userPackage)842     private @Nullable ActivityInfo getDefaultActivityLocked(
843             @NonNull ArrayList<ResolveInfo> matches,
844             @Nullable UserPackage userPackage) {
845         if (userPackage != null) {
846             // look for default activity
847             for (final ResolveInfo info : matches) {
848                 if (info.activityInfo != null && userPackage.equals(
849                         new UserPackage(info.activityInfo.packageName,
850                                 UserHandle.getUserHandleForUid(
851                                         info.activityInfo.applicationInfo.uid)))) {
852                     return info.activityInfo;
853                 }
854             }
855         }
856 
857         if (matches.size() == 1) {
858             final ActivityInfo activityInfo = matches.get(0).activityInfo;
859             if (activityInfo != null) {
860                 if (mDisablePermissionDialogs) {
861                     return activityInfo;
862                 }
863                 // System apps are considered default unless there are other matches
864                 if (activityInfo.applicationInfo != null
865                         && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
866                                 != 0) {
867                     return activityInfo;
868                 }
869             }
870         }
871 
872         return null;
873     }
874 
875     @GuardedBy("mLock")
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull DeviceFilter filter)876     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
877             @NonNull DeviceFilter filter) {
878         ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
879 
880         // The keys in mDevicePreferenceMap are filters that match devices very narrowly
881         for (DeviceFilter device : mDevicePreferenceMap.keySet()) {
882             if (filter.contains(device)) {
883                 UserPackage currentMatch = mDevicePreferenceMap.get(device);
884                 if (!currentMatch.equals(userPackage)) {
885                     keysToRemove.add(device);
886                 }
887             }
888         }
889 
890         if (!keysToRemove.isEmpty()) {
891             for (DeviceFilter keyToRemove : keysToRemove) {
892                 mDevicePreferenceMap.remove(keyToRemove);
893             }
894         }
895 
896         return !keysToRemove.isEmpty();
897     }
898 
899     @GuardedBy("mLock")
clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull AccessoryFilter filter)900     private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
901             @NonNull AccessoryFilter filter) {
902         ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
903 
904         // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly
905         for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) {
906             if (filter.contains(accessory)) {
907                 UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory);
908                 if (!currentMatch.equals(userPackage)) {
909                     keysToRemove.add(accessory);
910                 }
911             }
912         }
913 
914         if (!keysToRemove.isEmpty()) {
915             for (AccessoryFilter keyToRemove : keysToRemove) {
916                 mAccessoryPreferenceMap.remove(keyToRemove);
917             }
918         }
919 
920         return !keysToRemove.isEmpty();
921     }
922 
923     @GuardedBy("mLock")
handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo, String metaDataName)924     private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
925             String metaDataName) {
926         XmlResourceParser parser = null;
927         boolean changed = false;
928 
929         try {
930             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
931             if (parser == null) return false;
932 
933             XmlUtils.nextElement(parser);
934             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
935                 String tagName = parser.getName();
936                 if ("usb-device".equals(tagName)) {
937                     DeviceFilter filter = DeviceFilter.read(parser);
938                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
939                         changed = true;
940                     }
941                 }
942                 else if ("usb-accessory".equals(tagName)) {
943                     AccessoryFilter filter = AccessoryFilter.read(parser);
944                     if (clearCompatibleMatchesLocked(userPackage, filter)) {
945                         changed = true;
946                     }
947                 }
948                 XmlUtils.nextElement(parser);
949             }
950         } catch (Exception e) {
951             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
952         } finally {
953             if (parser != null) parser.close();
954         }
955         return changed;
956     }
957 
958     // Check to see if the package supports any USB devices or accessories.
959     // If so, clear any preferences for matching devices/accessories.
handlePackageAdded(@onNull UserPackage userPackage)960     private void handlePackageAdded(@NonNull UserPackage userPackage) {
961         synchronized (mLock) {
962             PackageInfo info;
963             boolean changed = false;
964 
965             try {
966                 info = mPackageManager.getPackageInfoAsUser(userPackage.packageName,
967                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
968                         userPackage.user.getIdentifier());
969             } catch (NameNotFoundException e) {
970                 Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e);
971                 return;
972             }
973 
974             ActivityInfo[] activities = info.activities;
975             if (activities == null) return;
976             for (int i = 0; i < activities.length; i++) {
977                 // check for meta-data, both for devices and accessories
978                 if (handlePackageAddedLocked(userPackage, activities[i],
979                         UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
980                     changed = true;
981                 }
982 
983                 if (handlePackageAddedLocked(userPackage, activities[i],
984                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
985                     changed = true;
986                 }
987             }
988 
989             if (changed) {
990                 scheduleWriteSettingsLocked();
991             }
992         }
993     }
994 
995     /**
996      * Get the serial number for a user handle.
997      *
998      * @param user The user handle
999      *
1000      * @return The serial number
1001      */
getSerial(@onNull UserHandle user)1002     private int getSerial(@NonNull UserHandle user) {
1003         return mUserManager.getUserSerialNumber(user.getIdentifier());
1004     }
1005 
1006     /**
1007      * Set a package as default handler for a device.
1008      *
1009      * @param device The device that should be handled by default
1010      * @param packageName The default handler package
1011      * @param user The user the package belongs to
1012      */
setDevicePackage(@onNull UsbDevice device, @Nullable String packageName, @NonNull UserHandle user)1013     void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
1014             @NonNull UserHandle user) {
1015         DeviceFilter filter = new DeviceFilter(device);
1016         boolean changed;
1017         synchronized (mLock) {
1018             if (packageName == null) {
1019                 changed = (mDevicePreferenceMap.remove(filter) != null);
1020             } else {
1021                 UserPackage userPackage = new UserPackage(packageName, user);
1022 
1023                 changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
1024                 if (changed) {
1025                     mDevicePreferenceMap.put(filter, userPackage);
1026                 }
1027             }
1028             if (changed) {
1029                 scheduleWriteSettingsLocked();
1030             }
1031         }
1032     }
1033 
1034     /**
1035      * Set a package as default handler for a accessory.
1036      *
1037      * @param accessory The accessory that should be handled by default
1038      * @param packageName The default handler package
1039      * @param user The user the package belongs to
1040      */
setAccessoryPackage(@onNull UsbAccessory accessory, @Nullable String packageName, @NonNull UserHandle user)1041     void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName,
1042             @NonNull UserHandle user) {
1043         AccessoryFilter filter = new AccessoryFilter(accessory);
1044         boolean changed;
1045         synchronized (mLock) {
1046             if (packageName == null) {
1047                 changed = (mAccessoryPreferenceMap.remove(filter) != null);
1048             } else {
1049                 UserPackage userPackage = new UserPackage(packageName, user);
1050 
1051                 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));
1052                 if (changed) {
1053                     mAccessoryPreferenceMap.put(filter, userPackage);
1054                 }
1055             }
1056             if (changed) {
1057                 scheduleWriteSettingsLocked();
1058             }
1059         }
1060     }
1061 
1062     /**
1063      * Check if a package has is the default handler for any usb device or accessory.
1064      *
1065      * @param packageName The package name
1066      * @param user The user the package belongs to
1067      *
1068      * @return {@code true} iff the package is default for any usb device or accessory
1069      */
hasDefaults(@onNull String packageName, @NonNull UserHandle user)1070     boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1071         UserPackage userPackage = new UserPackage(packageName, user);
1072         synchronized (mLock) {
1073             if (mDevicePreferenceMap.values().contains(userPackage)) return true;
1074             return mAccessoryPreferenceMap.values().contains(userPackage);
1075         }
1076     }
1077 
1078     /**
1079      * Clear defaults for a package from any preference.
1080      *
1081      * @param packageName The package to remove
1082      * @param user The user the package belongs to
1083      */
clearDefaults(@onNull String packageName, @NonNull UserHandle user)1084     void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
1085         UserPackage userPackage = new UserPackage(packageName, user);
1086 
1087         synchronized (mLock) {
1088             if (clearPackageDefaultsLocked(userPackage)) {
1089                 scheduleWriteSettingsLocked();
1090             }
1091         }
1092     }
1093 
1094     /**
1095      * Clear defaults for a package from any preference (does not persist).
1096      *
1097      * @param userPackage The package to remove
1098      *
1099      * @return {@code true} iff at least one preference was cleared
1100      */
clearPackageDefaultsLocked(@onNull UserPackage userPackage)1101     private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) {
1102         boolean cleared = false;
1103         synchronized (mLock) {
1104             if (mDevicePreferenceMap.containsValue(userPackage)) {
1105                 // make a copy of the key set to avoid ConcurrentModificationException
1106                 DeviceFilter[] keys = mDevicePreferenceMap.keySet().toArray(new DeviceFilter[0]);
1107                 for (int i = 0; i < keys.length; i++) {
1108                     DeviceFilter key = keys[i];
1109                     if (userPackage.equals(mDevicePreferenceMap.get(key))) {
1110                         mDevicePreferenceMap.remove(key);
1111                         cleared = true;
1112                     }
1113                 }
1114             }
1115             if (mAccessoryPreferenceMap.containsValue(userPackage)) {
1116                 // make a copy of the key set to avoid ConcurrentModificationException
1117                 AccessoryFilter[] keys =
1118                         mAccessoryPreferenceMap.keySet().toArray(new AccessoryFilter[0]);
1119                 for (int i = 0; i < keys.length; i++) {
1120                     AccessoryFilter key = keys[i];
1121                     if (userPackage.equals(mAccessoryPreferenceMap.get(key))) {
1122                         mAccessoryPreferenceMap.remove(key);
1123                         cleared = true;
1124                     }
1125                 }
1126             }
1127             return cleared;
1128         }
1129     }
1130 
dump(@onNull DualDumpOutputStream dump, @NonNull String idName, long id)1131     public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
1132         long token = dump.start(idName, id);
1133 
1134         synchronized (mLock) {
1135             dump.write("parent_user_id", UsbProfileGroupSettingsManagerProto.PARENT_USER_ID,
1136                     mParentUser.getIdentifier());
1137 
1138             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1139                 long devicePrefToken = dump.start("device_preferences",
1140                         UsbProfileGroupSettingsManagerProto.DEVICE_PREFERENCES);
1141 
1142                 filter.dump(dump, "filter", UsbSettingsDevicePreferenceProto.FILTER);
1143 
1144                 mDevicePreferenceMap.get(filter).dump(dump, "user_package",
1145                         UsbSettingsDevicePreferenceProto.USER_PACKAGE);
1146 
1147                 dump.end(devicePrefToken);
1148             }
1149             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1150                 long accessoryPrefToken = dump.start("accessory_preferences",
1151                         UsbProfileGroupSettingsManagerProto.ACCESSORY_PREFERENCES);
1152 
1153                 filter.dump(dump, "filter", UsbSettingsAccessoryPreferenceProto.FILTER);
1154 
1155                 mAccessoryPreferenceMap.get(filter).dump(dump, "user_package",
1156                         UsbSettingsAccessoryPreferenceProto.USER_PACKAGE);
1157 
1158                 dump.end(accessoryPrefToken);
1159             }
1160         }
1161 
1162         dump.end(token);
1163     }
1164 
createDeviceAttachedIntent(UsbDevice device)1165     private static Intent createDeviceAttachedIntent(UsbDevice device) {
1166         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1167         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
1168         intent.addFlags(
1169                 Intent.FLAG_ACTIVITY_NEW_TASK |
1170                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1171         return intent;
1172     }
1173 }
1174