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