Home | History | Annotate | Download | only in usb
      1 /*
      2  * Copyright (C) 2011 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 android.app.PendingIntent;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.ActivityInfo;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.pm.PackageInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.content.pm.ResolveInfo;
     30 import android.content.res.XmlResourceParser;
     31 import android.hardware.usb.UsbAccessory;
     32 import android.hardware.usb.UsbDevice;
     33 import android.hardware.usb.UsbInterface;
     34 import android.hardware.usb.UsbManager;
     35 import android.os.Binder;
     36 import android.os.Environment;
     37 import android.os.Process;
     38 import android.os.UserHandle;
     39 import android.util.AtomicFile;
     40 import android.util.Log;
     41 import android.util.Slog;
     42 import android.util.SparseBooleanArray;
     43 import android.util.Xml;
     44 
     45 import com.android.internal.content.PackageMonitor;
     46 import com.android.internal.util.FastXmlSerializer;
     47 import com.android.internal.util.XmlUtils;
     48 
     49 import org.xmlpull.v1.XmlPullParser;
     50 import org.xmlpull.v1.XmlPullParserException;
     51 import org.xmlpull.v1.XmlSerializer;
     52 
     53 import java.io.File;
     54 import java.io.FileDescriptor;
     55 import java.io.FileInputStream;
     56 import java.io.FileNotFoundException;
     57 import java.io.FileOutputStream;
     58 import java.io.IOException;
     59 import java.io.PrintWriter;
     60 import java.util.ArrayList;
     61 import java.util.HashMap;
     62 import java.util.List;
     63 
     64 import libcore.io.IoUtils;
     65 
     66 class UsbSettingsManager {
     67     private static final String TAG = "UsbSettingsManager";
     68     private static final boolean DEBUG = false;
     69 
     70     /** Legacy settings file, before multi-user */
     71     private static final File sSingleUserSettingsFile = new File(
     72             "/data/system/usb_device_manager.xml");
     73 
     74     private final UserHandle mUser;
     75     private final AtomicFile mSettingsFile;
     76 
     77     private final Context mContext;
     78     private final Context mUserContext;
     79     private final PackageManager mPackageManager;
     80 
     81     // Temporary mapping USB device name to list of UIDs with permissions for the device
     82     private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
     83             new HashMap<String, SparseBooleanArray>();
     84     // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
     85     private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
     86             new HashMap<UsbAccessory, SparseBooleanArray>();
     87     // Maps DeviceFilter to user preferred application package
     88     private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
     89             new HashMap<DeviceFilter, String>();
     90     // Maps AccessoryFilter to user preferred application package
     91     private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
     92             new HashMap<AccessoryFilter, String>();
     93 
     94     private final Object mLock = new Object();
     95 
     96     // This class is used to describe a USB device.
     97     // When used in HashMaps all values must be specified,
     98     // but wildcards can be used for any of the fields in
     99     // the package meta-data.
    100     private static class DeviceFilter {
    101         // USB Vendor ID (or -1 for unspecified)
    102         public final int mVendorId;
    103         // USB Product ID (or -1 for unspecified)
    104         public final int mProductId;
    105         // USB device or interface class (or -1 for unspecified)
    106         public final int mClass;
    107         // USB device subclass (or -1 for unspecified)
    108         public final int mSubclass;
    109         // USB device protocol (or -1 for unspecified)
    110         public final int mProtocol;
    111 
    112         public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
    113             mVendorId = vid;
    114             mProductId = pid;
    115             mClass = clasz;
    116             mSubclass = subclass;
    117             mProtocol = protocol;
    118         }
    119 
    120         public DeviceFilter(UsbDevice device) {
    121             mVendorId = device.getVendorId();
    122             mProductId = device.getProductId();
    123             mClass = device.getDeviceClass();
    124             mSubclass = device.getDeviceSubclass();
    125             mProtocol = device.getDeviceProtocol();
    126         }
    127 
    128         public static DeviceFilter read(XmlPullParser parser)
    129                 throws XmlPullParserException, IOException {
    130             int vendorId = -1;
    131             int productId = -1;
    132             int deviceClass = -1;
    133             int deviceSubclass = -1;
    134             int deviceProtocol = -1;
    135 
    136             int count = parser.getAttributeCount();
    137             for (int i = 0; i < count; i++) {
    138                 String name = parser.getAttributeName(i);
    139                 // All attribute values are ints
    140                 int value = Integer.parseInt(parser.getAttributeValue(i));
    141 
    142                 if ("vendor-id".equals(name)) {
    143                     vendorId = value;
    144                 } else if ("product-id".equals(name)) {
    145                     productId = value;
    146                 } else if ("class".equals(name)) {
    147                     deviceClass = value;
    148                 } else if ("subclass".equals(name)) {
    149                     deviceSubclass = value;
    150                 } else if ("protocol".equals(name)) {
    151                     deviceProtocol = value;
    152                 }
    153             }
    154             return new DeviceFilter(vendorId, productId,
    155                     deviceClass, deviceSubclass, deviceProtocol);
    156         }
    157 
    158         public void write(XmlSerializer serializer) throws IOException {
    159             serializer.startTag(null, "usb-device");
    160             if (mVendorId != -1) {
    161                 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
    162             }
    163             if (mProductId != -1) {
    164                 serializer.attribute(null, "product-id", Integer.toString(mProductId));
    165             }
    166             if (mClass != -1) {
    167                 serializer.attribute(null, "class", Integer.toString(mClass));
    168             }
    169             if (mSubclass != -1) {
    170                 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
    171             }
    172             if (mProtocol != -1) {
    173                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
    174             }
    175             serializer.endTag(null, "usb-device");
    176         }
    177 
    178         private boolean matches(int clasz, int subclass, int protocol) {
    179             return ((mClass == -1 || clasz == mClass) &&
    180                     (mSubclass == -1 || subclass == mSubclass) &&
    181                     (mProtocol == -1 || protocol == mProtocol));
    182         }
    183 
    184         public boolean matches(UsbDevice device) {
    185             if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
    186             if (mProductId != -1 && device.getProductId() != mProductId) return false;
    187 
    188             // check device class/subclass/protocol
    189             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
    190                     device.getDeviceProtocol())) return true;
    191 
    192             // if device doesn't match, check the interfaces
    193             int count = device.getInterfaceCount();
    194             for (int i = 0; i < count; i++) {
    195                 UsbInterface intf = device.getInterface(i);
    196                  if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
    197                         intf.getInterfaceProtocol())) return true;
    198             }
    199 
    200             return false;
    201         }
    202 
    203         public boolean matches(DeviceFilter f) {
    204             if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
    205             if (mProductId != -1 && f.mProductId != mProductId) return false;
    206 
    207             // check device class/subclass/protocol
    208             return matches(f.mClass, f.mSubclass, f.mProtocol);
    209         }
    210 
    211         @Override
    212         public boolean equals(Object obj) {
    213             // can't compare if we have wildcard strings
    214             if (mVendorId == -1 || mProductId == -1 ||
    215                     mClass == -1 || mSubclass == -1 || mProtocol == -1) {
    216                 return false;
    217             }
    218             if (obj instanceof DeviceFilter) {
    219                 DeviceFilter filter = (DeviceFilter)obj;
    220                 return (filter.mVendorId == mVendorId &&
    221                         filter.mProductId == mProductId &&
    222                         filter.mClass == mClass &&
    223                         filter.mSubclass == mSubclass &&
    224                         filter.mProtocol == mProtocol);
    225             }
    226             if (obj instanceof UsbDevice) {
    227                 UsbDevice device = (UsbDevice)obj;
    228                 return (device.getVendorId() == mVendorId &&
    229                         device.getProductId() == mProductId &&
    230                         device.getDeviceClass() == mClass &&
    231                         device.getDeviceSubclass() == mSubclass &&
    232                         device.getDeviceProtocol() == mProtocol);
    233             }
    234             return false;
    235         }
    236 
    237         @Override
    238         public int hashCode() {
    239             return (((mVendorId << 16) | mProductId) ^
    240                     ((mClass << 16) | (mSubclass << 8) | mProtocol));
    241         }
    242 
    243         @Override
    244         public String toString() {
    245             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
    246                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
    247                     ",mProtocol=" + mProtocol + "]";
    248         }
    249     }
    250 
    251     // This class is used to describe a USB accessory.
    252     // When used in HashMaps all values must be specified,
    253     // but wildcards can be used for any of the fields in
    254     // the package meta-data.
    255     private static class AccessoryFilter {
    256         // USB accessory manufacturer (or null for unspecified)
    257         public final String mManufacturer;
    258         // USB accessory model (or null for unspecified)
    259         public final String mModel;
    260         // USB accessory version (or null for unspecified)
    261         public final String mVersion;
    262 
    263         public AccessoryFilter(String manufacturer, String model, String version) {
    264             mManufacturer = manufacturer;
    265             mModel = model;
    266             mVersion = version;
    267         }
    268 
    269         public AccessoryFilter(UsbAccessory accessory) {
    270             mManufacturer = accessory.getManufacturer();
    271             mModel = accessory.getModel();
    272             mVersion = accessory.getVersion();
    273         }
    274 
    275         public static AccessoryFilter read(XmlPullParser parser)
    276                 throws XmlPullParserException, IOException {
    277             String manufacturer = null;
    278             String model = null;
    279             String version = null;
    280 
    281             int count = parser.getAttributeCount();
    282             for (int i = 0; i < count; i++) {
    283                 String name = parser.getAttributeName(i);
    284                 String value = parser.getAttributeValue(i);
    285 
    286                 if ("manufacturer".equals(name)) {
    287                     manufacturer = value;
    288                 } else if ("model".equals(name)) {
    289                     model = value;
    290                 } else if ("version".equals(name)) {
    291                     version = value;
    292                 }
    293              }
    294              return new AccessoryFilter(manufacturer, model, version);
    295         }
    296 
    297         public void write(XmlSerializer serializer)throws IOException {
    298             serializer.startTag(null, "usb-accessory");
    299             if (mManufacturer != null) {
    300                 serializer.attribute(null, "manufacturer", mManufacturer);
    301             }
    302             if (mModel != null) {
    303                 serializer.attribute(null, "model", mModel);
    304             }
    305             if (mVersion != null) {
    306                 serializer.attribute(null, "version", mVersion);
    307             }
    308             serializer.endTag(null, "usb-accessory");
    309         }
    310 
    311         public boolean matches(UsbAccessory acc) {
    312             if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
    313             if (mModel != null && !acc.getModel().equals(mModel)) return false;
    314             if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
    315             return true;
    316         }
    317 
    318         public boolean matches(AccessoryFilter f) {
    319             if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
    320             if (mModel != null && !f.mModel.equals(mModel)) return false;
    321             if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
    322             return true;
    323         }
    324 
    325         @Override
    326         public boolean equals(Object obj) {
    327             // can't compare if we have wildcard strings
    328             if (mManufacturer == null || mModel == null || mVersion == null) {
    329                 return false;
    330             }
    331             if (obj instanceof AccessoryFilter) {
    332                 AccessoryFilter filter = (AccessoryFilter)obj;
    333                 return (mManufacturer.equals(filter.mManufacturer) &&
    334                         mModel.equals(filter.mModel) &&
    335                         mVersion.equals(filter.mVersion));
    336             }
    337             if (obj instanceof UsbAccessory) {
    338                 UsbAccessory accessory = (UsbAccessory)obj;
    339                 return (mManufacturer.equals(accessory.getManufacturer()) &&
    340                         mModel.equals(accessory.getModel()) &&
    341                         mVersion.equals(accessory.getVersion()));
    342             }
    343             return false;
    344         }
    345 
    346         @Override
    347         public int hashCode() {
    348             return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
    349                     (mModel == null ? 0 : mModel.hashCode()) ^
    350                     (mVersion == null ? 0 : mVersion.hashCode()));
    351         }
    352 
    353         @Override
    354         public String toString() {
    355             return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
    356                                 "\", mModel=\"" + mModel +
    357                                 "\", mVersion=\"" + mVersion + "\"]";
    358         }
    359     }
    360 
    361     private class MyPackageMonitor extends PackageMonitor {
    362         @Override
    363         public void onPackageAdded(String packageName, int uid) {
    364             handlePackageUpdate(packageName);
    365         }
    366 
    367         @Override
    368         public boolean onPackageChanged(String packageName, int uid, String[] components) {
    369             handlePackageUpdate(packageName);
    370             return false;
    371         }
    372 
    373         @Override
    374         public void onPackageRemoved(String packageName, int uid) {
    375             clearDefaults(packageName);
    376         }
    377     }
    378 
    379     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
    380 
    381     public UsbSettingsManager(Context context, UserHandle user) {
    382         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
    383 
    384         try {
    385             mUserContext = context.createPackageContextAsUser("android", 0, user);
    386         } catch (NameNotFoundException e) {
    387             throw new RuntimeException("Missing android package");
    388         }
    389 
    390         mContext = context;
    391         mPackageManager = mUserContext.getPackageManager();
    392 
    393         mUser = user;
    394         mSettingsFile = new AtomicFile(new File(
    395                 Environment.getUserSystemDirectory(user.getIdentifier()),
    396                 "usb_device_manager.xml"));
    397 
    398         synchronized (mLock) {
    399             if (UserHandle.OWNER.equals(user)) {
    400                 upgradeSingleUserLocked();
    401             }
    402             readSettingsLocked();
    403         }
    404 
    405         mPackageMonitor.register(mUserContext, null, true);
    406     }
    407 
    408     private void readPreference(XmlPullParser parser)
    409             throws XmlPullParserException, IOException {
    410         String packageName = null;
    411         int count = parser.getAttributeCount();
    412         for (int i = 0; i < count; i++) {
    413             if ("package".equals(parser.getAttributeName(i))) {
    414                 packageName = parser.getAttributeValue(i);
    415                 break;
    416             }
    417         }
    418         XmlUtils.nextElement(parser);
    419         if ("usb-device".equals(parser.getName())) {
    420             DeviceFilter filter = DeviceFilter.read(parser);
    421             mDevicePreferenceMap.put(filter, packageName);
    422         } else if ("usb-accessory".equals(parser.getName())) {
    423             AccessoryFilter filter = AccessoryFilter.read(parser);
    424             mAccessoryPreferenceMap.put(filter, packageName);
    425         }
    426         XmlUtils.nextElement(parser);
    427     }
    428 
    429     /**
    430      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
    431      * Should only by called by owner.
    432      */
    433     private void upgradeSingleUserLocked() {
    434         if (sSingleUserSettingsFile.exists()) {
    435             mDevicePreferenceMap.clear();
    436             mAccessoryPreferenceMap.clear();
    437 
    438             FileInputStream fis = null;
    439             try {
    440                 fis = new FileInputStream(sSingleUserSettingsFile);
    441                 XmlPullParser parser = Xml.newPullParser();
    442                 parser.setInput(fis, null);
    443 
    444                 XmlUtils.nextElement(parser);
    445                 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
    446                     final String tagName = parser.getName();
    447                     if ("preference".equals(tagName)) {
    448                         readPreference(parser);
    449                     } else {
    450                         XmlUtils.nextElement(parser);
    451                     }
    452                 }
    453             } catch (IOException e) {
    454                 Log.wtf(TAG, "Failed to read single-user settings", e);
    455             } catch (XmlPullParserException e) {
    456                 Log.wtf(TAG, "Failed to read single-user settings", e);
    457             } finally {
    458                 IoUtils.closeQuietly(fis);
    459             }
    460 
    461             writeSettingsLocked();
    462 
    463             // Success or failure, we delete single-user file
    464             sSingleUserSettingsFile.delete();
    465         }
    466     }
    467 
    468     private void readSettingsLocked() {
    469         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
    470 
    471         mDevicePreferenceMap.clear();
    472         mAccessoryPreferenceMap.clear();
    473 
    474         FileInputStream stream = null;
    475         try {
    476             stream = mSettingsFile.openRead();
    477             XmlPullParser parser = Xml.newPullParser();
    478             parser.setInput(stream, null);
    479 
    480             XmlUtils.nextElement(parser);
    481             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
    482                 String tagName = parser.getName();
    483                 if ("preference".equals(tagName)) {
    484                     readPreference(parser);
    485                 } else {
    486                     XmlUtils.nextElement(parser);
    487                 }
    488             }
    489         } catch (FileNotFoundException e) {
    490             if (DEBUG) Slog.d(TAG, "settings file not found");
    491         } catch (Exception e) {
    492             Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
    493             mSettingsFile.delete();
    494         } finally {
    495             IoUtils.closeQuietly(stream);
    496         }
    497     }
    498 
    499     private void writeSettingsLocked() {
    500         if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
    501 
    502         FileOutputStream fos = null;
    503         try {
    504             fos = mSettingsFile.startWrite();
    505 
    506             FastXmlSerializer serializer = new FastXmlSerializer();
    507             serializer.setOutput(fos, "utf-8");
    508             serializer.startDocument(null, true);
    509             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    510             serializer.startTag(null, "settings");
    511 
    512             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
    513                 serializer.startTag(null, "preference");
    514                 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
    515                 filter.write(serializer);
    516                 serializer.endTag(null, "preference");
    517             }
    518 
    519             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
    520                 serializer.startTag(null, "preference");
    521                 serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
    522                 filter.write(serializer);
    523                 serializer.endTag(null, "preference");
    524             }
    525 
    526             serializer.endTag(null, "settings");
    527             serializer.endDocument();
    528 
    529             mSettingsFile.finishWrite(fos);
    530         } catch (IOException e) {
    531             Slog.e(TAG, "Failed to write settings", e);
    532             if (fos != null) {
    533                 mSettingsFile.failWrite(fos);
    534             }
    535         }
    536     }
    537 
    538     // Checks to see if a package matches a device or accessory.
    539     // Only one of device and accessory should be non-null.
    540     private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
    541             UsbDevice device, UsbAccessory accessory) {
    542         ActivityInfo ai = info.activityInfo;
    543 
    544         XmlResourceParser parser = null;
    545         try {
    546             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
    547             if (parser == null) {
    548                 Slog.w(TAG, "no meta-data for " + info);
    549                 return false;
    550             }
    551 
    552             XmlUtils.nextElement(parser);
    553             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
    554                 String tagName = parser.getName();
    555                 if (device != null && "usb-device".equals(tagName)) {
    556                     DeviceFilter filter = DeviceFilter.read(parser);
    557                     if (filter.matches(device)) {
    558                         return true;
    559                     }
    560                 }
    561                 else if (accessory != null && "usb-accessory".equals(tagName)) {
    562                     AccessoryFilter filter = AccessoryFilter.read(parser);
    563                     if (filter.matches(accessory)) {
    564                         return true;
    565                     }
    566                 }
    567                 XmlUtils.nextElement(parser);
    568             }
    569         } catch (Exception e) {
    570             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
    571         } finally {
    572             if (parser != null) parser.close();
    573         }
    574         return false;
    575     }
    576 
    577     private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
    578         ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
    579         List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
    580                 PackageManager.GET_META_DATA);
    581         int count = resolveInfos.size();
    582         for (int i = 0; i < count; i++) {
    583             ResolveInfo resolveInfo = resolveInfos.get(i);
    584             if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
    585                 matches.add(resolveInfo);
    586             }
    587         }
    588         return matches;
    589     }
    590 
    591     private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
    592             UsbAccessory accessory, Intent intent) {
    593         ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
    594         List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
    595                 PackageManager.GET_META_DATA);
    596         int count = resolveInfos.size();
    597         for (int i = 0; i < count; i++) {
    598             ResolveInfo resolveInfo = resolveInfos.get(i);
    599             if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
    600                 matches.add(resolveInfo);
    601             }
    602         }
    603         return matches;
    604     }
    605 
    606     public void deviceAttached(UsbDevice device) {
    607         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    608         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    609         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    610 
    611         ArrayList<ResolveInfo> matches;
    612         String defaultPackage;
    613         synchronized (mLock) {
    614             matches = getDeviceMatchesLocked(device, intent);
    615             // Launch our default activity directly, if we have one.
    616             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
    617             defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
    618         }
    619 
    620         // Send broadcast to running activity with registered intent
    621         mUserContext.sendBroadcast(intent);
    622 
    623         // Start activity with registered intent
    624         resolveActivity(intent, matches, defaultPackage, device, null);
    625     }
    626 
    627     public void deviceDetached(UsbDevice device) {
    628         // clear temporary permissions for the device
    629         mDevicePermissionMap.remove(device.getDeviceName());
    630 
    631         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
    632         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    633         if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
    634         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    635     }
    636 
    637     public void accessoryAttached(UsbAccessory accessory) {
    638         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
    639         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    640         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    641 
    642         ArrayList<ResolveInfo> matches;
    643         String defaultPackage;
    644         synchronized (mLock) {
    645             matches = getAccessoryMatchesLocked(accessory, intent);
    646             // Launch our default activity directly, if we have one.
    647             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
    648             defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
    649         }
    650 
    651         resolveActivity(intent, matches, defaultPackage, null, accessory);
    652     }
    653 
    654     public void accessoryDetached(UsbAccessory accessory) {
    655         // clear temporary permissions for the accessory
    656         mAccessoryPermissionMap.remove(accessory);
    657 
    658         Intent intent = new Intent(
    659                 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
    660         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    661         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    662     }
    663 
    664     private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
    665             String defaultPackage, UsbDevice device, UsbAccessory accessory) {
    666         int count = matches.size();
    667 
    668         // don't show the resolver activity if there are no choices available
    669         if (count == 0) {
    670             if (accessory != null) {
    671                 String uri = accessory.getUri();
    672                 if (uri != null && uri.length() > 0) {
    673                     // display URI to user
    674                     // start UsbResolverActivity so user can choose an activity
    675                     Intent dialogIntent = new Intent();
    676                     dialogIntent.setClassName("com.android.systemui",
    677                             "com.android.systemui.usb.UsbAccessoryUriActivity");
    678                     dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    679                     dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    680                     dialogIntent.putExtra("uri", uri);
    681                     try {
    682                         mUserContext.startActivityAsUser(dialogIntent, mUser);
    683                     } catch (ActivityNotFoundException e) {
    684                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
    685                     }
    686                 }
    687             }
    688 
    689             // do nothing
    690             return;
    691         }
    692 
    693         ResolveInfo defaultRI = null;
    694         if (count == 1 && defaultPackage == null) {
    695             // Check to see if our single choice is on the system partition.
    696             // If so, treat it as our default without calling UsbResolverActivity
    697             ResolveInfo rInfo = matches.get(0);
    698             if (rInfo.activityInfo != null &&
    699                     rInfo.activityInfo.applicationInfo != null &&
    700                     (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    701                 defaultRI = rInfo;
    702             }
    703         }
    704 
    705         if (defaultRI == null && defaultPackage != null) {
    706             // look for default activity
    707             for (int i = 0; i < count; i++) {
    708                 ResolveInfo rInfo = matches.get(i);
    709                 if (rInfo.activityInfo != null &&
    710                         defaultPackage.equals(rInfo.activityInfo.packageName)) {
    711                     defaultRI = rInfo;
    712                     break;
    713                 }
    714             }
    715         }
    716 
    717         if (defaultRI != null) {
    718             // grant permission for default activity
    719             if (device != null) {
    720                 grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
    721             } else if (accessory != null) {
    722                 grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
    723             }
    724 
    725             // start default activity directly
    726             try {
    727                 intent.setComponent(
    728                         new ComponentName(defaultRI.activityInfo.packageName,
    729                                 defaultRI.activityInfo.name));
    730                 mUserContext.startActivityAsUser(intent, mUser);
    731             } catch (ActivityNotFoundException e) {
    732                 Slog.e(TAG, "startActivity failed", e);
    733             }
    734         } else {
    735             Intent resolverIntent = new Intent();
    736             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    737 
    738             if (count == 1) {
    739                 // start UsbConfirmActivity if there is only one choice
    740                 resolverIntent.setClassName("com.android.systemui",
    741                         "com.android.systemui.usb.UsbConfirmActivity");
    742                 resolverIntent.putExtra("rinfo", matches.get(0));
    743 
    744                 if (device != null) {
    745                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
    746                 } else {
    747                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    748                 }
    749             } else {
    750                 // start UsbResolverActivity so user can choose an activity
    751                 resolverIntent.setClassName("com.android.systemui",
    752                         "com.android.systemui.usb.UsbResolverActivity");
    753                 resolverIntent.putParcelableArrayListExtra("rlist", matches);
    754                 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
    755             }
    756             try {
    757                 mUserContext.startActivityAsUser(resolverIntent, mUser);
    758             } catch (ActivityNotFoundException e) {
    759                 Slog.e(TAG, "unable to start activity " + resolverIntent);
    760             }
    761         }
    762     }
    763 
    764     private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
    765         boolean changed = false;
    766         for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
    767             if (filter.matches(test)) {
    768                 mDevicePreferenceMap.remove(test);
    769                 changed = true;
    770             }
    771         }
    772         return changed;
    773     }
    774 
    775     private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
    776         boolean changed = false;
    777         for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
    778             if (filter.matches(test)) {
    779                 mAccessoryPreferenceMap.remove(test);
    780                 changed = true;
    781             }
    782         }
    783         return changed;
    784     }
    785 
    786     private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
    787             String metaDataName) {
    788         XmlResourceParser parser = null;
    789         boolean changed = false;
    790 
    791         try {
    792             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
    793             if (parser == null) return false;
    794 
    795             XmlUtils.nextElement(parser);
    796             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
    797                 String tagName = parser.getName();
    798                 if ("usb-device".equals(tagName)) {
    799                     DeviceFilter filter = DeviceFilter.read(parser);
    800                     if (clearCompatibleMatchesLocked(packageName, filter)) {
    801                         changed = true;
    802                     }
    803                 }
    804                 else if ("usb-accessory".equals(tagName)) {
    805                     AccessoryFilter filter = AccessoryFilter.read(parser);
    806                     if (clearCompatibleMatchesLocked(packageName, filter)) {
    807                         changed = true;
    808                     }
    809                 }
    810                 XmlUtils.nextElement(parser);
    811             }
    812         } catch (Exception e) {
    813             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
    814         } finally {
    815             if (parser != null) parser.close();
    816         }
    817         return changed;
    818     }
    819 
    820     // Check to see if the package supports any USB devices or accessories.
    821     // If so, clear any non-matching preferences for matching devices/accessories.
    822     private void handlePackageUpdate(String packageName) {
    823         synchronized (mLock) {
    824             PackageInfo info;
    825             boolean changed = false;
    826 
    827             try {
    828                 info = mPackageManager.getPackageInfo(packageName,
    829                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
    830             } catch (NameNotFoundException e) {
    831                 Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
    832                 return;
    833             }
    834 
    835             ActivityInfo[] activities = info.activities;
    836             if (activities == null) return;
    837             for (int i = 0; i < activities.length; i++) {
    838                 // check for meta-data, both for devices and accessories
    839                 if (handlePackageUpdateLocked(packageName, activities[i],
    840                         UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
    841                     changed = true;
    842                 }
    843                 if (handlePackageUpdateLocked(packageName, activities[i],
    844                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
    845                     changed = true;
    846                 }
    847             }
    848 
    849             if (changed) {
    850                 writeSettingsLocked();
    851             }
    852         }
    853     }
    854 
    855     public boolean hasPermission(UsbDevice device) {
    856         synchronized (mLock) {
    857             int uid = Binder.getCallingUid();
    858             if (uid == Process.SYSTEM_UID) {
    859                 return true;
    860             }
    861             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
    862             if (uidList == null) {
    863                 return false;
    864             }
    865             return uidList.get(uid);
    866         }
    867     }
    868 
    869     public boolean hasPermission(UsbAccessory accessory) {
    870         synchronized (mLock) {
    871             int uid = Binder.getCallingUid();
    872             if (uid == Process.SYSTEM_UID) {
    873                 return true;
    874             }
    875             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
    876             if (uidList == null) {
    877                 return false;
    878             }
    879             return uidList.get(uid);
    880         }
    881     }
    882 
    883     public void checkPermission(UsbDevice device) {
    884         if (!hasPermission(device)) {
    885             throw new SecurityException("User has not given permission to device " + device);
    886         }
    887     }
    888 
    889     public void checkPermission(UsbAccessory accessory) {
    890         if (!hasPermission(accessory)) {
    891             throw new SecurityException("User has not given permission to accessory " + accessory);
    892         }
    893     }
    894 
    895     private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
    896         final int uid = Binder.getCallingUid();
    897 
    898         // compare uid with packageName to foil apps pretending to be someone else
    899         try {
    900             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
    901             if (aInfo.uid != uid) {
    902                 throw new IllegalArgumentException("package " + packageName +
    903                         " does not match caller's uid " + uid);
    904             }
    905         } catch (PackageManager.NameNotFoundException e) {
    906             throw new IllegalArgumentException("package " + packageName + " not found");
    907         }
    908 
    909         long identity = Binder.clearCallingIdentity();
    910         intent.setClassName("com.android.systemui",
    911                 "com.android.systemui.usb.UsbPermissionActivity");
    912         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    913         intent.putExtra(Intent.EXTRA_INTENT, pi);
    914         intent.putExtra("package", packageName);
    915         intent.putExtra(Intent.EXTRA_UID, uid);
    916         try {
    917             mUserContext.startActivityAsUser(intent, mUser);
    918         } catch (ActivityNotFoundException e) {
    919             Slog.e(TAG, "unable to start UsbPermissionActivity");
    920         } finally {
    921             Binder.restoreCallingIdentity(identity);
    922         }
    923     }
    924 
    925     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
    926       Intent intent = new Intent();
    927 
    928         // respond immediately if permission has already been granted
    929       if (hasPermission(device)) {
    930             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    931             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
    932             try {
    933                 pi.send(mUserContext, 0, intent);
    934             } catch (PendingIntent.CanceledException e) {
    935                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
    936             }
    937             return;
    938         }
    939 
    940         // start UsbPermissionActivity so user can choose an activity
    941         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    942         requestPermissionDialog(intent, packageName, pi);
    943     }
    944 
    945     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
    946         Intent intent = new Intent();
    947 
    948         // respond immediately if permission has already been granted
    949         if (hasPermission(accessory)) {
    950             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    951             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
    952             try {
    953                 pi.send(mUserContext, 0, intent);
    954             } catch (PendingIntent.CanceledException e) {
    955                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
    956             }
    957             return;
    958         }
    959 
    960         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
    961         requestPermissionDialog(intent, packageName, pi);
    962     }
    963 
    964     public void setDevicePackage(UsbDevice device, String packageName) {
    965         DeviceFilter filter = new DeviceFilter(device);
    966         boolean changed = false;
    967         synchronized (mLock) {
    968             if (packageName == null) {
    969                 changed = (mDevicePreferenceMap.remove(filter) != null);
    970             } else {
    971                 changed = !packageName.equals(mDevicePreferenceMap.get(filter));
    972                 if (changed) {
    973                     mDevicePreferenceMap.put(filter, packageName);
    974                 }
    975             }
    976             if (changed) {
    977                 writeSettingsLocked();
    978             }
    979         }
    980     }
    981 
    982     public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
    983         AccessoryFilter filter = new AccessoryFilter(accessory);
    984         boolean changed = false;
    985         synchronized (mLock) {
    986             if (packageName == null) {
    987                 changed = (mAccessoryPreferenceMap.remove(filter) != null);
    988             } else {
    989                 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
    990                 if (changed) {
    991                     mAccessoryPreferenceMap.put(filter, packageName);
    992                 }
    993             }
    994             if (changed) {
    995                 writeSettingsLocked();
    996             }
    997         }
    998     }
    999 
   1000     public void grantDevicePermission(UsbDevice device, int uid) {
   1001         synchronized (mLock) {
   1002             String deviceName = device.getDeviceName();
   1003             SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
   1004             if (uidList == null) {
   1005                 uidList = new SparseBooleanArray(1);
   1006                 mDevicePermissionMap.put(deviceName, uidList);
   1007             }
   1008             uidList.put(uid, true);
   1009         }
   1010     }
   1011 
   1012     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
   1013         synchronized (mLock) {
   1014             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
   1015             if (uidList == null) {
   1016                 uidList = new SparseBooleanArray(1);
   1017                 mAccessoryPermissionMap.put(accessory, uidList);
   1018             }
   1019             uidList.put(uid, true);
   1020         }
   1021     }
   1022 
   1023     public boolean hasDefaults(String packageName) {
   1024         synchronized (mLock) {
   1025             if (mDevicePreferenceMap.values().contains(packageName)) return true;
   1026             if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
   1027             return false;
   1028         }
   1029     }
   1030 
   1031     public void clearDefaults(String packageName) {
   1032         synchronized (mLock) {
   1033             if (clearPackageDefaultsLocked(packageName)) {
   1034                 writeSettingsLocked();
   1035             }
   1036         }
   1037     }
   1038 
   1039     private boolean clearPackageDefaultsLocked(String packageName) {
   1040         boolean cleared = false;
   1041         synchronized (mLock) {
   1042             if (mDevicePreferenceMap.containsValue(packageName)) {
   1043                 // make a copy of the key set to avoid ConcurrentModificationException
   1044                 Object[] keys = mDevicePreferenceMap.keySet().toArray();
   1045                 for (int i = 0; i < keys.length; i++) {
   1046                     Object key = keys[i];
   1047                     if (packageName.equals(mDevicePreferenceMap.get(key))) {
   1048                         mDevicePreferenceMap.remove(key);
   1049                         cleared = true;
   1050                     }
   1051                 }
   1052             }
   1053             if (mAccessoryPreferenceMap.containsValue(packageName)) {
   1054                 // make a copy of the key set to avoid ConcurrentModificationException
   1055                 Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
   1056                 for (int i = 0; i < keys.length; i++) {
   1057                     Object key = keys[i];
   1058                     if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
   1059                         mAccessoryPreferenceMap.remove(key);
   1060                         cleared = true;
   1061                     }
   1062                 }
   1063             }
   1064             return cleared;
   1065         }
   1066     }
   1067 
   1068     public void dump(FileDescriptor fd, PrintWriter pw) {
   1069         synchronized (mLock) {
   1070             pw.println("  Device permissions:");
   1071             for (String deviceName : mDevicePermissionMap.keySet()) {
   1072                 pw.print("    " + deviceName + ": ");
   1073                 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
   1074                 int count = uidList.size();
   1075                 for (int i = 0; i < count; i++) {
   1076                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
   1077                 }
   1078                 pw.println("");
   1079             }
   1080             pw.println("  Accessory permissions:");
   1081             for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
   1082                 pw.print("    " + accessory + ": ");
   1083                 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
   1084                 int count = uidList.size();
   1085                 for (int i = 0; i < count; i++) {
   1086                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
   1087                 }
   1088                 pw.println("");
   1089             }
   1090             pw.println("  Device preferences:");
   1091             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
   1092                 pw.println("    " + filter + ": " + mDevicePreferenceMap.get(filter));
   1093             }
   1094             pw.println("  Accessory preferences:");
   1095             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
   1096                 pw.println("    " + filter + ": " + mAccessoryPreferenceMap.get(filter));
   1097             }
   1098         }
   1099     }
   1100 }
   1101