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