blob: 1bc203ef0a5b659322715fea2bdd4b3d61adc522 [file] [log] [blame]
Mike Lockwood24236072010-06-23 17:36:36 -04001/*
2 * Copyright (C) 2010 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 and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.Intent;
Mike Lockwoode7d511e2010-12-30 13:39:37 -050022import android.hardware.IUsbManager;
Mike Lockwood9182d3c2011-02-15 09:50:22 -050023import android.hardware.UsbAccessory;
Mike Lockwoode7d511e2010-12-30 13:39:37 -050024import android.hardware.UsbConstants;
25import android.hardware.UsbDevice;
26import android.hardware.UsbEndpoint;
27import android.hardware.UsbInterface;
Mike Lockwood770126a2010-12-09 22:30:37 -080028import android.hardware.UsbManager;
Mike Lockwood24236072010-06-23 17:36:36 -040029import android.net.Uri;
Mike Lockwoode7d511e2010-12-30 13:39:37 -050030import android.os.Bundle;
Mike Lockwood24236072010-06-23 17:36:36 -040031import android.os.Handler;
32import android.os.Message;
Mike Lockwoode7d511e2010-12-30 13:39:37 -050033import android.os.Parcelable;
34import android.os.ParcelFileDescriptor;
Mike Lockwood24236072010-06-23 17:36:36 -040035import android.os.UEventObserver;
36import android.provider.Settings;
37import android.util.Log;
38import android.util.Slog;
39
40import java.io.File;
41import java.io.FileNotFoundException;
42import java.io.FileReader;
43import java.util.ArrayList;
Mike Lockwoode7d511e2010-12-30 13:39:37 -050044import java.util.HashMap;
Mike Lockwood24236072010-06-23 17:36:36 -040045
46/**
Mike Lockwoodddc6cad2011-02-17 09:21:25 -050047 * UsbService monitors for changes to USB state.
48 * This includes code for both USB host support (where the android device is the host)
49 * as well as USB device support (android device is connected to a USB host).
50 * Accessory mode is a special case of USB device mode, where the android device is
51 * connected to a USB host that supports the android accessory protocol.
Mike Lockwood24236072010-06-23 17:36:36 -040052 */
Mike Lockwoode7d511e2010-12-30 13:39:37 -050053class UsbService extends IUsbManager.Stub {
Mike Lockwood770126a2010-12-09 22:30:37 -080054 private static final String TAG = UsbService.class.getSimpleName();
Mike Lockwood24236072010-06-23 17:36:36 -040055 private static final boolean LOG = false;
56
Mike Lockwoodb92df0f2010-12-10 16:19:32 -080057 private static final String USB_CONNECTED_MATCH =
58 "DEVPATH=/devices/virtual/switch/usb_connected";
59 private static final String USB_CONFIGURATION_MATCH =
60 "DEVPATH=/devices/virtual/switch/usb_configuration";
61 private static final String USB_FUNCTIONS_MATCH =
62 "DEVPATH=/devices/virtual/usb_composite/";
63 private static final String USB_CONNECTED_PATH =
64 "/sys/class/switch/usb_connected/state";
65 private static final String USB_CONFIGURATION_PATH =
66 "/sys/class/switch/usb_configuration/state";
67 private static final String USB_COMPOSITE_CLASS_PATH =
68 "/sys/class/usb_composite";
Mike Lockwood24236072010-06-23 17:36:36 -040069
Mike Lockwoodddc6cad2011-02-17 09:21:25 -050070 private static final int MSG_UPDATE_STATE = 0;
71 private static final int MSG_FUNCTION_ENABLED = 1;
72 private static final int MSG_FUNCTION_DISABLED = 2;
Mike Lockwood24236072010-06-23 17:36:36 -040073
Mike Lockwoodb92df0f2010-12-10 16:19:32 -080074 // Delay for debouncing USB disconnects.
75 // We often get rapid connect/disconnect events when enabling USB functions,
76 // which need debouncing.
77 private static final int UPDATE_DELAY = 1000;
78
79 // current connected and configuration state
80 private int mConnected;
81 private int mConfiguration;
82
83 // last broadcasted connected and configuration state
84 private int mLastConnected = -1;
85 private int mLastConfiguration = -1;
Mike Lockwood24236072010-06-23 17:36:36 -040086
Mike Lockwoode7d511e2010-12-30 13:39:37 -050087 // lists of enabled and disabled USB functions (for USB device mode)
Mike Lockwood24236072010-06-23 17:36:36 -040088 private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
89 private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
90
Mike Lockwood9182d3c2011-02-15 09:50:22 -050091 // contains all connected USB devices (for USB host mode)
Mike Lockwoode7d511e2010-12-30 13:39:37 -050092 private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
93
Mike Lockwooda8e3a892011-02-01 13:46:50 -050094 // USB busses to exclude from USB host support
95 private final String[] mHostBlacklist;
96
Mike Lockwood24236072010-06-23 17:36:36 -040097 private boolean mSystemReady;
Mike Lockwoodddc6cad2011-02-17 09:21:25 -050098
Mike Lockwood9182d3c2011-02-15 09:50:22 -050099 private UsbAccessory mCurrentAccessory;
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500100 // functions to restore after exiting accessory mode
101 private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>();
Mike Lockwood24236072010-06-23 17:36:36 -0400102
103 private final Context mContext;
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500104 private final Object mLock = new Object();
Mike Lockwood24236072010-06-23 17:36:36 -0400105
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500106 /*
107 * Handles USB function enable/disable events (device mode)
108 */
109 private final void functionEnabledLocked(String function, boolean enabled) {
110 boolean enteringAccessoryMode =
111 (enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function));
112
113 if (enteringAccessoryMode) {
114 // keep a list of functions to reenable after exiting accessory mode
115 mAccessoryRestoreFunctions.clear();
116 int count = mEnabledFunctions.size();
117 for (int i = 0; i < count; i++) {
118 String f = mEnabledFunctions.get(i);
119 // RNDIS should not be restored and adb is handled automatically
120 if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) &&
121 !UsbManager.USB_FUNCTION_ADB.equals(f) &&
122 !UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) {
123 mAccessoryRestoreFunctions.add(f);
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500124 }
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500125 }
126 }
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500127 if (enabled) {
128 if (!mEnabledFunctions.contains(function)) {
129 mEnabledFunctions.add(function);
130 }
131 mDisabledFunctions.remove(function);
132 } else {
133 if (!mDisabledFunctions.contains(function)) {
134 mDisabledFunctions.add(function);
135 }
136 mEnabledFunctions.remove(function);
137 }
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500138
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500139 if (enteringAccessoryMode) {
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500140 String[] strings = nativeGetAccessoryStrings();
141 if (strings != null) {
142 Log.d(TAG, "entering USB accessory mode");
143 mCurrentAccessory = new UsbAccessory(strings);
144 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
145 intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
146 // add strings as separate extras to allow filtering
147 if (strings[0] != null) {
148 intent.putExtra(UsbManager.EXTRA_ACCESSORY_MANUFACTURER, strings[0]);
149 }
150 if (strings[1] != null) {
151 intent.putExtra(UsbManager.EXTRA_ACCESSORY_PRODUCT, strings[1]);
152 }
153 if (strings[2] != null) {
154 intent.putExtra(UsbManager.EXTRA_ACCESSORY_TYPE, strings[2]);
155 }
156 if (strings[3] != null) {
157 intent.putExtra(UsbManager.EXTRA_ACCESSORY_VERSION, strings[3]);
158 }
159 mContext.sendBroadcast(intent);
160 } else {
161 Log.e(TAG, "nativeGetAccessoryStrings failed");
162 }
163 }
164 }
165
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500166 /*
167 * Listens for uevent messages from the kernel to monitor the USB state (device mode)
168 */
Mike Lockwood770126a2010-12-09 22:30:37 -0800169 private final UEventObserver mUEventObserver = new UEventObserver() {
170 @Override
171 public void onUEvent(UEventObserver.UEvent event) {
172 if (Log.isLoggable(TAG, Log.VERBOSE)) {
173 Slog.v(TAG, "USB UEVENT: " + event.toString());
174 }
Mike Lockwood24236072010-06-23 17:36:36 -0400175
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500176 synchronized (mLock) {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800177 String name = event.get("SWITCH_NAME");
178 String state = event.get("SWITCH_STATE");
179 if (name != null && state != null) {
Mike Lockwood770126a2010-12-09 22:30:37 -0800180 try {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800181 int intState = Integer.parseInt(state);
182 if ("usb_connected".equals(name)) {
183 mConnected = intState;
Mike Lockwood770126a2010-12-09 22:30:37 -0800184 // trigger an Intent broadcast
185 if (mSystemReady) {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800186 // debounce disconnects
187 update(mConnected == 0);
188 }
189 } else if ("usb_configuration".equals(name)) {
190 mConfiguration = intState;
191 // trigger an Intent broadcast
192 if (mSystemReady) {
193 update(mConnected == 0);
Mike Lockwood770126a2010-12-09 22:30:37 -0800194 }
Mike Lockwood24236072010-06-23 17:36:36 -0400195 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800196 } catch (NumberFormatException e) {
197 Slog.e(TAG, "Could not parse switch state from event " + event);
Mike Lockwood24236072010-06-23 17:36:36 -0400198 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800199 } else {
200 String function = event.get("FUNCTION");
201 String enabledStr = event.get("ENABLED");
202 if (function != null && enabledStr != null) {
203 // Note: we do not broadcast a change when a function is enabled or disabled.
204 // We just record the state change for the next broadcast.
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500205 int what = ("1".equals(enabledStr) ?
206 MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
207 Message msg = Message.obtain(mHandler, what);
208 msg.obj = function;
209 mHandler.sendMessage(msg);
Mike Lockwood24236072010-06-23 17:36:36 -0400210 }
211 }
212 }
213 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800214 };
215
216 public UsbService(Context context) {
217 mContext = context;
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500218 mHostBlacklist = context.getResources().getStringArray(
219 com.android.internal.R.array.config_usbHostBlacklist);
220
Mike Lockwood770126a2010-12-09 22:30:37 -0800221 init(); // set initial status
222
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100223 if (mConfiguration >= 0) {
224 mUEventObserver.startObserving(USB_CONNECTED_MATCH);
225 mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
226 mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
227 }
Mike Lockwood24236072010-06-23 17:36:36 -0400228 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800229
Mike Lockwood24236072010-06-23 17:36:36 -0400230 private final void init() {
231 char[] buffer = new char[1024];
232
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500233 // Read initial USB state (device mode)
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100234 mConfiguration = -1;
Mike Lockwood24236072010-06-23 17:36:36 -0400235 try {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800236 FileReader file = new FileReader(USB_CONNECTED_PATH);
Mike Lockwood24236072010-06-23 17:36:36 -0400237 int len = file.read(buffer, 0, 1024);
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700238 file.close();
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800239 mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
240
241 file = new FileReader(USB_CONFIGURATION_PATH);
242 len = file.read(buffer, 0, 1024);
243 file.close();
244 mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
Mike Lockwood24236072010-06-23 17:36:36 -0400245
246 } catch (FileNotFoundException e) {
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100247 Slog.i(TAG, "This kernel does not have USB configuration switch support");
Mike Lockwood24236072010-06-23 17:36:36 -0400248 } catch (Exception e) {
249 Slog.e(TAG, "" , e);
250 }
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100251 if (mConfiguration < 0)
252 return;
Mike Lockwood24236072010-06-23 17:36:36 -0400253
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500254 // Read initial list of enabled and disabled functions (device mode)
Mike Lockwood24236072010-06-23 17:36:36 -0400255 try {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500256 File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
257 for (int i = 0; i < files.length; i++) {
258 File file = new File(files[i], "enable");
259 FileReader reader = new FileReader(file);
260 int len = reader.read(buffer, 0, 1024);
261 reader.close();
262 int value = Integer.valueOf((new String(buffer, 0, len)).trim());
263 String functionName = files[i].getName();
264 if (value == 1) {
265 mEnabledFunctions.add(functionName);
266 } else {
267 mDisabledFunctions.add(functionName);
Mike Lockwood24236072010-06-23 17:36:36 -0400268 }
269 }
270 } catch (FileNotFoundException e) {
271 Slog.w(TAG, "This kernel does not have USB composite class support");
272 } catch (Exception e) {
273 Slog.e(TAG, "" , e);
274 }
275 }
276
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500277 private boolean isBlackListed(String deviceName) {
278 int count = mHostBlacklist.length;
279 for (int i = 0; i < count; i++) {
280 if (deviceName.startsWith(mHostBlacklist[i])) {
281 return true;
282 }
283 }
284 return false;
285 }
286
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500287 /* returns true if the USB device should not be accessible by applications (host mode) */
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500288 private boolean isBlackListed(int clazz, int subClass, int protocol) {
289 // blacklist hubs
290 if (clazz == UsbConstants.USB_CLASS_HUB) return true;
291
292 // blacklist HID boot devices (mouse and keyboard)
293 if (clazz == UsbConstants.USB_CLASS_HID &&
294 subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
295 return true;
296 }
297
298 return false;
299 }
300
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500301 /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500302 private void usbDeviceAdded(String deviceName, int vendorID, int productID,
303 int deviceClass, int deviceSubclass, int deviceProtocol,
304 /* array of quintuples containing id, class, subclass, protocol
305 and number of endpoints for each interface */
306 int[] interfaceValues,
307 /* array of quadruples containing address, attributes, max packet size
308 and interval for each endpoint */
309 int[] endpointValues) {
310
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500311 if (isBlackListed(deviceName) ||
312 isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500313 return;
314 }
315
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500316 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500317 if (mDevices.get(deviceName) != null) {
318 Log.w(TAG, "device already on mDevices list: " + deviceName);
319 return;
320 }
321
322 int numInterfaces = interfaceValues.length / 5;
323 Parcelable[] interfaces = new UsbInterface[numInterfaces];
324 try {
325 // repackage interfaceValues as an array of UsbInterface
326 int intf, endp, ival = 0, eval = 0;
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500327 for (intf = 0; intf < numInterfaces; intf++) {
328 int interfaceId = interfaceValues[ival++];
329 int interfaceClass = interfaceValues[ival++];
330 int interfaceSubclass = interfaceValues[ival++];
331 int interfaceProtocol = interfaceValues[ival++];
332 int numEndpoints = interfaceValues[ival++];
333
334 Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
335 for (endp = 0; endp < numEndpoints; endp++) {
336 int address = endpointValues[eval++];
337 int attributes = endpointValues[eval++];
338 int maxPacketSize = endpointValues[eval++];
339 int interval = endpointValues[eval++];
340 endpoints[endp] = new UsbEndpoint(address, attributes,
341 maxPacketSize, interval);
342 }
343
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500344 // don't allow if any interfaces are blacklisted
345 if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
346 return;
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500347 }
348 interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
349 interfaceSubclass, interfaceProtocol, endpoints);
350 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500351 } catch (Exception e) {
352 // beware of index out of bound exceptions, which might happen if
353 // a device does not set bNumEndpoints correctly
354 Log.e(TAG, "error parsing USB descriptors", e);
355 return;
356 }
357
358 UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
359 deviceClass, deviceSubclass, deviceProtocol, interfaces);
360 mDevices.put(deviceName, device);
361
362 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
363 intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
364 intent.putExtra(UsbManager.EXTRA_VENDOR_ID, vendorID);
365 intent.putExtra(UsbManager.EXTRA_PRODUCT_ID, productID);
366 intent.putExtra(UsbManager.EXTRA_DEVICE_CLASS, deviceClass);
367 intent.putExtra(UsbManager.EXTRA_DEVICE_SUBCLASS, deviceSubclass);
368 intent.putExtra(UsbManager.EXTRA_DEVICE_PROTOCOL, deviceProtocol);
369 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
370 Log.d(TAG, "usbDeviceAdded, sending " + intent);
371 mContext.sendBroadcast(intent);
372 }
373 }
374
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500375 /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500376 private void usbDeviceRemoved(String deviceName) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500377 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500378 UsbDevice device = mDevices.remove(deviceName);
379 if (device != null) {
380 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
381 intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
382 Log.d(TAG, "usbDeviceRemoved, sending " + intent);
383 mContext.sendBroadcast(intent);
384 }
385 }
386 }
387
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400388 private void initHostSupport() {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500389 // Create a thread to call into native code to wait for USB host events.
390 // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
391 Runnable runnable = new Runnable() {
392 public void run() {
393 monitorUsbHostBus();
394 }
395 };
396 new Thread(null, runnable, "UsbService host thread").start();
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400397 }
398
Mike Lockwood24236072010-06-23 17:36:36 -0400399 void systemReady() {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500400 synchronized (mLock) {
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400401 if (mContext.getResources().getBoolean(
402 com.android.internal.R.bool.config_hasUsbHostSupport)) {
403 // start monitoring for connected USB devices
404 initHostSupport();
405 }
406
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800407 update(false);
Mike Lockwood24236072010-06-23 17:36:36 -0400408 mSystemReady = true;
409 }
410 }
411
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500412 /*
413 * Sends a message to update the USB connected and configured state (device mode).
414 * If delayed is true, then we add a small delay in sending the message to debounce
415 * the USB connection when enabling USB tethering.
416 */
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800417 private final void update(boolean delayed) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500418 mHandler.removeMessages(MSG_UPDATE_STATE);
419 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
Mike Lockwood24236072010-06-23 17:36:36 -0400420 }
421
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500422 /* Returns a list of all currently attached USB devices (host mdoe) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500423 public void getDeviceList(Bundle devices) {
424 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500425 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500426 for (String name : mDevices.keySet()) {
427 devices.putParcelable(name, mDevices.get(name));
428 }
429 }
430 }
431
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500432 /* Opens the specified USB device (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500433 public ParcelFileDescriptor openDevice(String deviceName) {
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500434 if (isBlackListed(deviceName)) {
435 throw new SecurityException("USB device is on a restricted bus");
436 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500437 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500438 if (mDevices.get(deviceName) == null) {
439 // if it is not in mDevices, it either does not exist or is blacklisted
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500440 throw new IllegalArgumentException(
441 "device " + deviceName + " does not exist or is restricted");
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500442 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500443 return nativeOpenDevice(deviceName);
444 }
445
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500446 /* returns the currently attached USB accessory (device mode) */
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500447 public UsbAccessory getCurrentAccessory() {
448 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
449 return mCurrentAccessory;
450 }
451
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500452 /* opens the currently attached USB accessory (device mode) */
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500453 public ParcelFileDescriptor openAccessory() {
454 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
455 return nativeOpenAccessory();
456 }
457
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500458 /*
459 * This handler is for deferred handling of events related to device mode and accessories.
460 */
Mike Lockwood24236072010-06-23 17:36:36 -0400461 private final Handler mHandler = new Handler() {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500462 private void addEnabledFunctionsLocked(Intent intent) {
Mike Lockwood709981e2010-06-28 09:58:58 -0400463 // include state of all USB functions in our extras
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500464 for (int i = 0; i < mEnabledFunctions.size(); i++) {
465 intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
466 }
467 for (int i = 0; i < mDisabledFunctions.size(); i++) {
468 intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
Mike Lockwood709981e2010-06-28 09:58:58 -0400469 }
470 }
471
Mike Lockwood24236072010-06-23 17:36:36 -0400472 @Override
473 public void handleMessage(Message msg) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500474 synchronized (mLock) {
475 switch (msg.what) {
476 case MSG_UPDATE_STATE:
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800477 if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500478 if (mConnected == 0 && mCurrentAccessory != null) {
479 // turn off accessory mode when we are disconnected
480 if (UsbManager.setFunctionEnabled(
481 UsbManager.USB_FUNCTION_ACCESSORY, false)) {
482 Log.d(TAG, "exited USB accessory mode");
483
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500484 // restore previously enabled functions
485 for (String function : mAccessoryRestoreFunctions) {
486 if (UsbManager.setFunctionEnabled(function, true)) {
487 Log.e(TAG, "could not reenable function " + function);
488 }
489 }
490 mAccessoryRestoreFunctions.clear();
491
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500492 Intent intent = new Intent(
493 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
494 intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
495 mContext.sendBroadcast(intent);
496 mCurrentAccessory = null;
497
498 // this will cause an immediate reset of the USB bus,
499 // so there is no point in sending the
500 // function disabled broadcast.
501 return;
502 } else {
503 Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY");
504 }
505 }
Mike Lockwood24236072010-06-23 17:36:36 -0400506
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800507 final ContentResolver cr = mContext.getContentResolver();
508 if (Settings.Secure.getInt(cr,
509 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
510 Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
511 return;
512 }
513
514 mLastConnected = mConnected;
515 mLastConfiguration = mConfiguration;
516
517 // send a sticky broadcast containing current USB state
518 Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
519 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
520 intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
521 intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500522 addEnabledFunctionsLocked(intent);
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800523 mContext.sendStickyBroadcast(intent);
Mike Lockwood24236072010-06-23 17:36:36 -0400524 }
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500525 break;
526 case MSG_FUNCTION_ENABLED:
527 case MSG_FUNCTION_DISABLED:
528 functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
529 break;
530 }
Mike Lockwood24236072010-06-23 17:36:36 -0400531 }
532 }
533 };
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500534
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500535 // host support
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500536 private native void monitorUsbHostBus();
537 private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500538 // accessory support
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500539 private native String[] nativeGetAccessoryStrings();
540 private native ParcelFileDescriptor nativeOpenAccessory();
Mike Lockwood24236072010-06-23 17:36:36 -0400541}