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 com.android.internal.annotations.GuardedBy; 30 31import java.io.FileDescriptor; 32import java.io.PrintWriter; 33import java.util.HashMap; 34 35/** 36 * UsbHostManager manages USB state in host mode. 37 */ 38public 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