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 an
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.usb;
     18 
     19 import android.content.Context;
     20 import android.hardware.usb.UsbConstants;
     21 import android.hardware.usb.UsbDevice;
     22 import android.hardware.usb.UsbEndpoint;
     23 import android.hardware.usb.UsbInterface;
     24 import android.os.Bundle;
     25 import android.os.ParcelFileDescriptor;
     26 import android.os.Parcelable;
     27 import android.util.Slog;
     28 
     29 import com.android.internal.annotations.GuardedBy;
     30 
     31 import java.io.FileDescriptor;
     32 import java.io.PrintWriter;
     33 import java.util.HashMap;
     34 
     35 /**
     36  * UsbHostManager manages USB state in host mode.
     37  */
     38 public class UsbHostManager {
     39     private static final String TAG = UsbHostManager.class.getSimpleName();
     40     private static final boolean LOG = false;
     41 
     42     // contains all connected USB devices
     43     private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
     44 
     45     // USB busses to exclude from USB host support
     46     private final String[] mHostBlacklist;
     47 
     48     private final Context mContext;
     49     private final Object mLock = new Object();
     50 
     51     @GuardedBy("mLock")
     52     private UsbSettingsManager mCurrentSettings;
     53 
     54     public UsbHostManager(Context context) {
     55         mContext = context;
     56         mHostBlacklist = context.getResources().getStringArray(
     57                 com.android.internal.R.array.config_usbHostBlacklist);
     58     }
     59 
     60     public void setCurrentSettings(UsbSettingsManager settings) {
     61         synchronized (mLock) {
     62             mCurrentSettings = settings;
     63         }
     64     }
     65 
     66     private UsbSettingsManager getCurrentSettings() {
     67         synchronized (mLock) {
     68             return mCurrentSettings;
     69         }
     70     }
     71 
     72     private boolean isBlackListed(String deviceName) {
     73         int count = mHostBlacklist.length;
     74         for (int i = 0; i < count; i++) {
     75             if (deviceName.startsWith(mHostBlacklist[i])) {
     76                 return true;
     77             }
     78         }
     79         return false;
     80     }
     81 
     82     /* returns true if the USB device should not be accessible by applications */
     83     private boolean isBlackListed(int clazz, int subClass, int protocol) {
     84         // blacklist hubs
     85         if (clazz == UsbConstants.USB_CLASS_HUB) return true;
     86 
     87         // blacklist HID boot devices (mouse and keyboard)
     88         if (clazz == UsbConstants.USB_CLASS_HID &&
     89                 subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
     90             return true;
     91         }
     92 
     93         return false;
     94     }
     95 
     96     /* Called from JNI in monitorUsbHostBus() to report new USB devices */
     97     private void usbDeviceAdded(String deviceName, int vendorID, int productID,
     98             int deviceClass, int deviceSubclass, int deviceProtocol,
     99             /* array of quintuples containing id, class, subclass, protocol
    100                and number of endpoints for each interface */
    101             int[] interfaceValues,
    102            /* array of quadruples containing address, attributes, max packet size
    103               and interval for each endpoint */
    104             int[] endpointValues) {
    105 
    106         if (isBlackListed(deviceName) ||
    107                 isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
    108             return;
    109         }
    110 
    111         synchronized (mLock) {
    112             if (mDevices.get(deviceName) != null) {
    113                 Slog.w(TAG, "device already on mDevices list: " + deviceName);
    114                 return;
    115             }
    116 
    117             int numInterfaces = interfaceValues.length / 5;
    118             Parcelable[] interfaces = new UsbInterface[numInterfaces];
    119             try {
    120                 // repackage interfaceValues as an array of UsbInterface
    121                 int intf, endp, ival = 0, eval = 0;
    122                 for (intf = 0; intf < numInterfaces; intf++) {
    123                     int interfaceId = interfaceValues[ival++];
    124                     int interfaceClass = interfaceValues[ival++];
    125                     int interfaceSubclass = interfaceValues[ival++];
    126                     int interfaceProtocol = interfaceValues[ival++];
    127                     int numEndpoints = interfaceValues[ival++];
    128 
    129                     Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
    130                     for (endp = 0; endp < numEndpoints; endp++) {
    131                         int address = endpointValues[eval++];
    132                         int attributes = endpointValues[eval++];
    133                         int maxPacketSize = endpointValues[eval++];
    134                         int interval = endpointValues[eval++];
    135                         endpoints[endp] = new UsbEndpoint(address, attributes,
    136                                 maxPacketSize, interval);
    137                     }
    138 
    139                     // don't allow if any interfaces are blacklisted
    140                     if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
    141                         return;
    142                     }
    143                     interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
    144                             interfaceSubclass, interfaceProtocol, endpoints);
    145                 }
    146             } catch (Exception e) {
    147                 // beware of index out of bound exceptions, which might happen if
    148                 // a device does not set bNumEndpoints correctly
    149                 Slog.e(TAG, "error parsing USB descriptors", e);
    150                 return;
    151             }
    152 
    153             UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
    154                     deviceClass, deviceSubclass, deviceProtocol, interfaces);
    155             mDevices.put(deviceName, device);
    156             getCurrentSettings().deviceAttached(device);
    157         }
    158     }
    159 
    160     /* Called from JNI in monitorUsbHostBus to report USB device removal */
    161     private void usbDeviceRemoved(String deviceName) {
    162         synchronized (mLock) {
    163             UsbDevice device = mDevices.remove(deviceName);
    164             if (device != null) {
    165                 getCurrentSettings().deviceDetached(device);
    166             }
    167         }
    168     }
    169 
    170     public void systemReady() {
    171         synchronized (mLock) {
    172             // Create a thread to call into native code to wait for USB host events.
    173             // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
    174             Runnable runnable = new Runnable() {
    175                 public void run() {
    176                     monitorUsbHostBus();
    177                 }
    178             };
    179             new Thread(null, runnable, "UsbService host thread").start();
    180         }
    181     }
    182 
    183     /* Returns a list of all currently attached USB devices */
    184     public void getDeviceList(Bundle devices) {
    185         synchronized (mLock) {
    186             for (String name : mDevices.keySet()) {
    187                 devices.putParcelable(name, mDevices.get(name));
    188             }
    189         }
    190     }
    191 
    192     /* Opens the specified USB device */
    193     public ParcelFileDescriptor openDevice(String deviceName) {
    194         synchronized (mLock) {
    195             if (isBlackListed(deviceName)) {
    196                 throw new SecurityException("USB device is on a restricted bus");
    197             }
    198             UsbDevice device = mDevices.get(deviceName);
    199             if (device == null) {
    200                 // if it is not in mDevices, it either does not exist or is blacklisted
    201                 throw new IllegalArgumentException(
    202                         "device " + deviceName + " does not exist or is restricted");
    203             }
    204             getCurrentSettings().checkPermission(device);
    205             return nativeOpenDevice(deviceName);
    206         }
    207     }
    208 
    209     public void dump(FileDescriptor fd, PrintWriter pw) {
    210         synchronized (mLock) {
    211             pw.println("  USB Host State:");
    212             for (String name : mDevices.keySet()) {
    213                 pw.println("    " + name + ": " + mDevices.get(name));
    214             }
    215         }
    216     }
    217 
    218     private native void monitorUsbHostBus();
    219     private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
    220 }
    221