Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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 | |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 19 | import android.content.Context; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 20 | import android.hardware.usb.UsbConstants; |
| 21 | import android.hardware.usb.UsbDevice; |
| 22 | import android.hardware.usb.UsbEndpoint; |
| 23 | import android.hardware.usb.UsbInterface; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 24 | import android.os.Bundle; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 25 | import android.os.ParcelFileDescriptor; |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 26 | import android.os.Parcelable; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 27 | import android.util.Slog; |
| 28 | |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 29 | import java.io.FileDescriptor; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 30 | import java.io.PrintWriter; |
| 31 | import java.util.HashMap; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 32 | |
| 33 | /** |
| 34 | * UsbHostManager manages USB state in host mode. |
| 35 | */ |
| 36 | public 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 |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 41 | private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>(); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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(); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 48 | |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 49 | // @GuardedBy("mLock") |
| 50 | private UsbSettingsManager mCurrentSettings; |
| 51 | |
| 52 | public UsbHostManager(Context context) { |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 53 | mContext = context; |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 54 | mHostBlacklist = context.getResources().getStringArray( |
| 55 | com.android.internal.R.array.config_usbHostBlacklist); |
| 56 | } |
| 57 | |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 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 | |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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) { |
Mike Lockwood | fdc0c29 | 2011-07-01 11:17:43 -0400 | [diff] [blame] | 111 | Slog.w(TAG, "device already on mDevices list: " + deviceName); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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 |
Mike Lockwood | fdc0c29 | 2011-07-01 11:17:43 -0400 | [diff] [blame] | 147 | Slog.e(TAG, "error parsing USB descriptors", e); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 148 | return; |
| 149 | } |
| 150 | |
| 151 | UsbDevice device = new UsbDevice(deviceName, vendorID, productID, |
| 152 | deviceClass, deviceSubclass, deviceProtocol, interfaces); |
| 153 | mDevices.put(deviceName, device); |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 154 | getCurrentSettings().deviceAttached(device); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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) { |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 163 | getCurrentSettings().deviceDetached(device); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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 | } |
Jeff Sharkey | fc3f24b | 2012-10-01 21:45:52 -0700 | [diff] [blame] | 202 | getCurrentSettings().checkPermission(device); |
Mike Lockwood | 46d0adf | 2011-05-26 10:27:39 -0400 | [diff] [blame] | 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 | } |