Home | History | Annotate | Download | only in usb
      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          */
    134         private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
    135             this.packageName = packageName;
    136             this.user = user;
    137         }
    138 
    139         @Override
    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
    151         public int hashCode() {
    152             int result = user.hashCode();
    153             result = 31 * result + packageName.hashCode();
    154             return result;
    155         }
    156 
    157         @Override
    158         public String toString() {
    159             return user.getIdentifier() + "/" + packageName;
    160         }
    161 
    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
    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
    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      */
    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      */
    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 
    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")
    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")
    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")
    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.
    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      */
    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      */
    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
    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      */
    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 
    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 
    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 
    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 
    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 
    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      */
    718     void usbDeviceRemoved(@NonNull UsbDevice device) {
    719         mMtpNotificationManager.hideNotification(device.getDeviceId());
    720     }
    721 
    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      */
    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      */
    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")
    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")
    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")
    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.
    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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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 
   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 
   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