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.UsbConfiguration;
     21 import android.hardware.usb.UsbConstants;
     22 import android.hardware.usb.UsbDevice;
     23 import android.hardware.usb.UsbEndpoint;
     24 import android.hardware.usb.UsbInterface;
     25 import android.os.Bundle;
     26 import android.os.ParcelFileDescriptor;
     27 import android.util.Slog;
     28 
     29 import com.android.internal.annotations.GuardedBy;
     30 import com.android.internal.util.IndentingPrintWriter;
     31 
     32 import java.io.FileDescriptor;
     33 import java.io.PrintWriter;
     34 import java.util.ArrayList;
     35 import java.util.HashMap;
     36 
     37 /**
     38  * UsbHostManager manages USB state in host mode.
     39  */
     40 public class UsbHostManager {
     41     private static final String TAG = UsbHostManager.class.getSimpleName();
     42     private static final boolean DEBUG = false;
     43 
     44     // contains all connected USB devices
     45     private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
     46 
     47 
     48     // USB busses to exclude from USB host support
     49     private final String[] mHostBlacklist;
     50 
     51     private final Context mContext;
     52     private final Object mLock = new Object();
     53 
     54     private UsbDevice mNewDevice;
     55     private UsbConfiguration mNewConfiguration;
     56     private UsbInterface mNewInterface;
     57     private ArrayList<UsbConfiguration> mNewConfigurations;
     58     private ArrayList<UsbInterface> mNewInterfaces;
     59     private ArrayList<UsbEndpoint> mNewEndpoints;
     60 
     61     private final UsbAlsaManager mUsbAlsaManager;
     62 
     63     @GuardedBy("mLock")
     64     private UsbSettingsManager mCurrentSettings;
     65 
     66     public UsbHostManager(Context context, UsbAlsaManager alsaManager) {
     67         mContext = context;
     68         mHostBlacklist = context.getResources().getStringArray(
     69                 com.android.internal.R.array.config_usbHostBlacklist);
     70         mUsbAlsaManager = alsaManager;
     71     }
     72 
     73     public void setCurrentSettings(UsbSettingsManager settings) {
     74         synchronized (mLock) {
     75             mCurrentSettings = settings;
     76         }
     77     }
     78 
     79     private UsbSettingsManager getCurrentSettings() {
     80         synchronized (mLock) {
     81             return mCurrentSettings;
     82         }
     83     }
     84 
     85     private boolean isBlackListed(String deviceName) {
     86         int count = mHostBlacklist.length;
     87         for (int i = 0; i < count; i++) {
     88             if (deviceName.startsWith(mHostBlacklist[i])) {
     89                 return true;
     90             }
     91         }
     92         return false;
     93     }
     94 
     95     /* returns true if the USB device should not be accessible by applications */
     96     private boolean isBlackListed(int clazz, int subClass, int protocol) {
     97         // blacklist hubs
     98         if (clazz == UsbConstants.USB_CLASS_HUB) return true;
     99 
    100         // blacklist HID boot devices (mouse and keyboard)
    101         if (clazz == UsbConstants.USB_CLASS_HID &&
    102                 subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
    103             return true;
    104         }
    105 
    106         return false;
    107     }
    108 
    109     /* Called from JNI in monitorUsbHostBus() to report new USB devices
    110        Returns true if successful, in which case the JNI code will continue adding configurations,
    111        interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
    112        have been processed
    113      */
    114     private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
    115             int deviceClass, int deviceSubclass, int deviceProtocol,
    116             String manufacturerName, String productName, int version, String serialNumber) {
    117 
    118         if (DEBUG) {
    119             Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
    120             // Audio Class Codes:
    121             // Audio: 0x01
    122             // Audio Subclass Codes:
    123             // undefined: 0x00
    124             // audio control: 0x01
    125             // audio streaming: 0x02
    126             // midi streaming: 0x03
    127 
    128             // some useful debugging info
    129             Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
    130                     + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
    131         }
    132 
    133         // OK this is non-obvious, but true. One can't tell if the device being attached is even
    134         // potentially an audio device without parsing the interface descriptors, so punt on any
    135         // such test until endUsbDeviceAdded() when we have that info.
    136 
    137         if (isBlackListed(deviceName) ||
    138                 isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
    139             return false;
    140         }
    141 
    142         synchronized (mLock) {
    143             if (mDevices.get(deviceName) != null) {
    144                 Slog.w(TAG, "device already on mDevices list: " + deviceName);
    145                 return false;
    146             }
    147 
    148             if (mNewDevice != null) {
    149                 Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
    150                 return false;
    151             }
    152 
    153             // Create version string in "%.%" format
    154             String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);
    155 
    156             mNewDevice = new UsbDevice(deviceName, vendorID, productID,
    157                     deviceClass, deviceSubclass, deviceProtocol,
    158                     manufacturerName, productName, versionString, serialNumber);
    159 
    160             mNewConfigurations = new ArrayList<UsbConfiguration>();
    161             mNewInterfaces = new ArrayList<UsbInterface>();
    162             mNewEndpoints = new ArrayList<UsbEndpoint>();
    163         }
    164 
    165         return true;
    166     }
    167 
    168     /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
    169        currently being added.  Returns true if successful, false in case of error.
    170      */
    171     private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
    172         if (mNewConfiguration != null) {
    173             mNewConfiguration.setInterfaces(
    174                     mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
    175             mNewInterfaces.clear();
    176         }
    177 
    178         mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
    179         mNewConfigurations.add(mNewConfiguration);
    180     }
    181 
    182     /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
    183        currently being added.  Returns true if successful, false in case of error.
    184      */
    185     private void addUsbInterface(int id, String name, int altSetting,
    186             int Class, int subClass, int protocol) {
    187         if (mNewInterface != null) {
    188             mNewInterface.setEndpoints(
    189                     mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
    190             mNewEndpoints.clear();
    191         }
    192 
    193         mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
    194         mNewInterfaces.add(mNewInterface);
    195     }
    196 
    197     /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
    198        currently being added.  Returns true if successful, false in case of error.
    199      */
    200     private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
    201         mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
    202     }
    203 
    204     /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
    205     private void endUsbDeviceAdded() {
    206         if (DEBUG) {
    207             Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
    208         }
    209         if (mNewInterface != null) {
    210             mNewInterface.setEndpoints(
    211                     mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
    212         }
    213         if (mNewConfiguration != null) {
    214             mNewConfiguration.setInterfaces(
    215                     mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
    216         }
    217 
    218 
    219         synchronized (mLock) {
    220             if (mNewDevice != null) {
    221                 mNewDevice.setConfigurations(
    222                         mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));
    223                 mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
    224                 Slog.d(TAG, "Added device " + mNewDevice);
    225                 getCurrentSettings().deviceAttached(mNewDevice);
    226                 mUsbAlsaManager.usbDeviceAdded(mNewDevice);
    227             } else {
    228                 Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
    229             }
    230             mNewDevice = null;
    231             mNewConfigurations = null;
    232             mNewInterfaces = null;
    233             mNewEndpoints = null;
    234             mNewConfiguration = null;
    235             mNewInterface = null;
    236         }
    237     }
    238 
    239     /* Called from JNI in monitorUsbHostBus to report USB device removal */
    240     private void usbDeviceRemoved(String deviceName) {
    241         synchronized (mLock) {
    242             UsbDevice device = mDevices.remove(deviceName);
    243             if (device != null) {
    244                 mUsbAlsaManager.usbDeviceRemoved(device);
    245                 getCurrentSettings().deviceDetached(device);
    246             }
    247         }
    248     }
    249 
    250     public void systemReady() {
    251         synchronized (mLock) {
    252             // Create a thread to call into native code to wait for USB host events.
    253             // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
    254             Runnable runnable = new Runnable() {
    255                 public void run() {
    256                     monitorUsbHostBus();
    257                 }
    258             };
    259             new Thread(null, runnable, "UsbService host thread").start();
    260         }
    261     }
    262 
    263     /* Returns a list of all currently attached USB devices */
    264     public void getDeviceList(Bundle devices) {
    265         synchronized (mLock) {
    266             for (String name : mDevices.keySet()) {
    267                 devices.putParcelable(name, mDevices.get(name));
    268             }
    269         }
    270     }
    271 
    272     /* Opens the specified USB device */
    273     public ParcelFileDescriptor openDevice(String deviceName) {
    274         synchronized (mLock) {
    275             if (isBlackListed(deviceName)) {
    276                 throw new SecurityException("USB device is on a restricted bus");
    277             }
    278             UsbDevice device = mDevices.get(deviceName);
    279             if (device == null) {
    280                 // if it is not in mDevices, it either does not exist or is blacklisted
    281                 throw new IllegalArgumentException(
    282                         "device " + deviceName + " does not exist or is restricted");
    283             }
    284             getCurrentSettings().checkPermission(device);
    285             return nativeOpenDevice(deviceName);
    286         }
    287     }
    288 
    289     public void dump(IndentingPrintWriter pw) {
    290         synchronized (mLock) {
    291             pw.println("USB Host State:");
    292             for (String name : mDevices.keySet()) {
    293                 pw.println("  " + name + ": " + mDevices.get(name));
    294             }
    295         }
    296     }
    297 
    298     private native void monitorUsbHostBus();
    299     private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
    300 }
    301