blob: 175ae6f0677ab265ecf65b5afa4369627de59579 [file] [log] [blame]
Mike Lockwood46d0adf2011-05-26 10:27:39 -04001/*
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
Mike Lockwood46d0adf2011-05-26 10:27:39 -040019import android.content.Context;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040020import android.hardware.usb.UsbConstants;
21import android.hardware.usb.UsbDevice;
22import android.hardware.usb.UsbEndpoint;
23import android.hardware.usb.UsbInterface;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040024import android.os.Bundle;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040025import android.os.ParcelFileDescriptor;
Jeff Sharkeyfc3f24b2012-10-01 21:45:52 -070026import android.os.Parcelable;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040027import android.util.Slog;
28
Mike Lockwood46d0adf2011-05-26 10:27:39 -040029import java.io.FileDescriptor;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040030import java.io.PrintWriter;
31import java.util.HashMap;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040032
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
Jeff Sharkeyfc3f24b2012-10-01 21:45:52 -070041 private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
Mike Lockwood46d0adf2011-05-26 10:27:39 -040042
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 Lockwood46d0adf2011-05-26 10:27:39 -040048
Jeff Sharkeyfc3f24b2012-10-01 21:45:52 -070049 // @GuardedBy("mLock")
50 private UsbSettingsManager mCurrentSettings;
51
52 public UsbHostManager(Context context) {
Mike Lockwood46d0adf2011-05-26 10:27:39 -040053 mContext = context;
Mike Lockwood46d0adf2011-05-26 10:27:39 -040054 mHostBlacklist = context.getResources().getStringArray(
55 com.android.internal.R.array.config_usbHostBlacklist);
56 }
57
Jeff Sharkeyfc3f24b2012-10-01 21:45:52 -070058 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 Lockwood46d0adf2011-05-26 10:27:39 -040070 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 Lockwoodfdc0c292011-07-01 11:17:43 -0400111 Slog.w(TAG, "device already on mDevices list: " + deviceName);
Mike Lockwood46d0adf2011-05-26 10:27:39 -0400112 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 Lockwoodfdc0c292011-07-01 11:17:43 -0400147 Slog.e(TAG, "error parsing USB descriptors", e);
Mike Lockwood46d0adf2011-05-26 10:27:39 -0400148 return;
149 }
150
151 UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
152 deviceClass, deviceSubclass, deviceProtocol, interfaces);
153 mDevices.put(deviceName, device);
Jeff Sharkeyfc3f24b2012-10-01 21:45:52 -0700154 getCurrentSettings().deviceAttached(device);
Mike Lockwood46d0adf2011-05-26 10:27:39 -0400155 }
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 Sharkeyfc3f24b2012-10-01 21:45:52 -0700163 getCurrentSettings().deviceDetached(device);
Mike Lockwood46d0adf2011-05-26 10:27:39 -0400164 }
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 Sharkeyfc3f24b2012-10-01 21:45:52 -0700202 getCurrentSettings().checkPermission(device);
Mike Lockwood46d0adf2011-05-26 10:27:39 -0400203 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}