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