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 UsbHostManager(Context context, UsbAlsaManager alsaManager)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 setCurrentSettings(UsbSettingsManager settings)73 public void setCurrentSettings(UsbSettingsManager settings) { 74 synchronized (mLock) { 75 mCurrentSettings = settings; 76 } 77 } 78 getCurrentSettings()79 private UsbSettingsManager getCurrentSettings() { 80 synchronized (mLock) { 81 return mCurrentSettings; 82 } 83 } 84 isBlackListed(String deviceName)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 */ isBlackListed(int clazz, int subClass, int protocol)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 */ beginUsbDeviceAdded(String deviceName, int vendorID, int productID, int deviceClass, int deviceSubclass, int deviceProtocol, String manufacturerName, String productName, int version, String serialNumber)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 */ addUsbConfiguration(int id, String name, int attributes, int maxPower)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 */ addUsbInterface(int id, String name, int altSetting, int Class, int subClass, int protocol)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 */ addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval)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 */ endUsbDeviceAdded()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 */ usbDeviceRemoved(String deviceName)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 systemReady()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 */ getDeviceList(Bundle 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 */ openDevice(String deviceName)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 dump(IndentingPrintWriter pw)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 monitorUsbHostBus()298 private native void monitorUsbHostBus(); nativeOpenDevice(String deviceName)299 private native ParcelFileDescriptor nativeOpenDevice(String deviceName); 300 } 301