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