blob: 2f8471352f28e215ff0452d1aec6411a9271e8d5 [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);
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500146 mContext.sendBroadcast(intent);
147 } else {
148 Log.e(TAG, "nativeGetAccessoryStrings failed");
149 }
150 }
151 }
152
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500153 /*
154 * Listens for uevent messages from the kernel to monitor the USB state (device mode)
155 */
Mike Lockwood770126a2010-12-09 22:30:37 -0800156 private final UEventObserver mUEventObserver = new UEventObserver() {
157 @Override
158 public void onUEvent(UEventObserver.UEvent event) {
159 if (Log.isLoggable(TAG, Log.VERBOSE)) {
160 Slog.v(TAG, "USB UEVENT: " + event.toString());
161 }
Mike Lockwood24236072010-06-23 17:36:36 -0400162
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500163 synchronized (mLock) {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800164 String name = event.get("SWITCH_NAME");
165 String state = event.get("SWITCH_STATE");
166 if (name != null && state != null) {
Mike Lockwood770126a2010-12-09 22:30:37 -0800167 try {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800168 int intState = Integer.parseInt(state);
169 if ("usb_connected".equals(name)) {
170 mConnected = intState;
Mike Lockwood770126a2010-12-09 22:30:37 -0800171 // trigger an Intent broadcast
172 if (mSystemReady) {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800173 // debounce disconnects
174 update(mConnected == 0);
175 }
176 } else if ("usb_configuration".equals(name)) {
177 mConfiguration = intState;
178 // trigger an Intent broadcast
179 if (mSystemReady) {
180 update(mConnected == 0);
Mike Lockwood770126a2010-12-09 22:30:37 -0800181 }
Mike Lockwood24236072010-06-23 17:36:36 -0400182 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800183 } catch (NumberFormatException e) {
184 Slog.e(TAG, "Could not parse switch state from event " + event);
Mike Lockwood24236072010-06-23 17:36:36 -0400185 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800186 } else {
187 String function = event.get("FUNCTION");
188 String enabledStr = event.get("ENABLED");
189 if (function != null && enabledStr != null) {
190 // Note: we do not broadcast a change when a function is enabled or disabled.
191 // We just record the state change for the next broadcast.
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500192 int what = ("1".equals(enabledStr) ?
193 MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
194 Message msg = Message.obtain(mHandler, what);
195 msg.obj = function;
196 mHandler.sendMessage(msg);
Mike Lockwood24236072010-06-23 17:36:36 -0400197 }
198 }
199 }
200 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800201 };
202
203 public UsbService(Context context) {
204 mContext = context;
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500205 mHostBlacklist = context.getResources().getStringArray(
206 com.android.internal.R.array.config_usbHostBlacklist);
207
Mike Lockwood770126a2010-12-09 22:30:37 -0800208 init(); // set initial status
209
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100210 if (mConfiguration >= 0) {
211 mUEventObserver.startObserving(USB_CONNECTED_MATCH);
212 mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
213 mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
214 }
Mike Lockwood24236072010-06-23 17:36:36 -0400215 }
Mike Lockwood770126a2010-12-09 22:30:37 -0800216
Mike Lockwood24236072010-06-23 17:36:36 -0400217 private final void init() {
218 char[] buffer = new char[1024];
219
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500220 // Read initial USB state (device mode)
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100221 mConfiguration = -1;
Mike Lockwood24236072010-06-23 17:36:36 -0400222 try {
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800223 FileReader file = new FileReader(USB_CONNECTED_PATH);
Mike Lockwood24236072010-06-23 17:36:36 -0400224 int len = file.read(buffer, 0, 1024);
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700225 file.close();
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800226 mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
227
228 file = new FileReader(USB_CONFIGURATION_PATH);
229 len = file.read(buffer, 0, 1024);
230 file.close();
231 mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
Mike Lockwood24236072010-06-23 17:36:36 -0400232
233 } catch (FileNotFoundException e) {
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100234 Slog.i(TAG, "This kernel does not have USB configuration switch support");
Mike Lockwood24236072010-06-23 17:36:36 -0400235 } catch (Exception e) {
236 Slog.e(TAG, "" , e);
237 }
David 'Digit' Turner49db8532011-01-17 00:19:37 +0100238 if (mConfiguration < 0)
239 return;
Mike Lockwood24236072010-06-23 17:36:36 -0400240
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500241 // Read initial list of enabled and disabled functions (device mode)
Mike Lockwood24236072010-06-23 17:36:36 -0400242 try {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500243 File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
244 for (int i = 0; i < files.length; i++) {
245 File file = new File(files[i], "enable");
246 FileReader reader = new FileReader(file);
247 int len = reader.read(buffer, 0, 1024);
248 reader.close();
249 int value = Integer.valueOf((new String(buffer, 0, len)).trim());
250 String functionName = files[i].getName();
251 if (value == 1) {
252 mEnabledFunctions.add(functionName);
253 } else {
254 mDisabledFunctions.add(functionName);
Mike Lockwood24236072010-06-23 17:36:36 -0400255 }
256 }
257 } catch (FileNotFoundException e) {
258 Slog.w(TAG, "This kernel does not have USB composite class support");
259 } catch (Exception e) {
260 Slog.e(TAG, "" , e);
261 }
262 }
263
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500264 private boolean isBlackListed(String deviceName) {
265 int count = mHostBlacklist.length;
266 for (int i = 0; i < count; i++) {
267 if (deviceName.startsWith(mHostBlacklist[i])) {
268 return true;
269 }
270 }
271 return false;
272 }
273
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500274 /* returns true if the USB device should not be accessible by applications (host mode) */
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500275 private boolean isBlackListed(int clazz, int subClass, int protocol) {
276 // blacklist hubs
277 if (clazz == UsbConstants.USB_CLASS_HUB) return true;
278
279 // blacklist HID boot devices (mouse and keyboard)
280 if (clazz == UsbConstants.USB_CLASS_HID &&
281 subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
282 return true;
283 }
284
285 return false;
286 }
287
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500288 /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500289 private void usbDeviceAdded(String deviceName, int vendorID, int productID,
290 int deviceClass, int deviceSubclass, int deviceProtocol,
291 /* array of quintuples containing id, class, subclass, protocol
292 and number of endpoints for each interface */
293 int[] interfaceValues,
294 /* array of quadruples containing address, attributes, max packet size
295 and interval for each endpoint */
296 int[] endpointValues) {
297
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500298 if (isBlackListed(deviceName) ||
299 isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500300 return;
301 }
302
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500303 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500304 if (mDevices.get(deviceName) != null) {
305 Log.w(TAG, "device already on mDevices list: " + deviceName);
306 return;
307 }
308
309 int numInterfaces = interfaceValues.length / 5;
310 Parcelable[] interfaces = new UsbInterface[numInterfaces];
311 try {
312 // repackage interfaceValues as an array of UsbInterface
313 int intf, endp, ival = 0, eval = 0;
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500314 for (intf = 0; intf < numInterfaces; intf++) {
315 int interfaceId = interfaceValues[ival++];
316 int interfaceClass = interfaceValues[ival++];
317 int interfaceSubclass = interfaceValues[ival++];
318 int interfaceProtocol = interfaceValues[ival++];
319 int numEndpoints = interfaceValues[ival++];
320
321 Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
322 for (endp = 0; endp < numEndpoints; endp++) {
323 int address = endpointValues[eval++];
324 int attributes = endpointValues[eval++];
325 int maxPacketSize = endpointValues[eval++];
326 int interval = endpointValues[eval++];
327 endpoints[endp] = new UsbEndpoint(address, attributes,
328 maxPacketSize, interval);
329 }
330
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500331 // don't allow if any interfaces are blacklisted
332 if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
333 return;
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500334 }
335 interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
336 interfaceSubclass, interfaceProtocol, endpoints);
337 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500338 } catch (Exception e) {
339 // beware of index out of bound exceptions, which might happen if
340 // a device does not set bNumEndpoints correctly
341 Log.e(TAG, "error parsing USB descriptors", e);
342 return;
343 }
344
345 UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
346 deviceClass, deviceSubclass, deviceProtocol, interfaces);
347 mDevices.put(deviceName, device);
348
349 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500350 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
351 Log.d(TAG, "usbDeviceAdded, sending " + intent);
352 mContext.sendBroadcast(intent);
353 }
354 }
355
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500356 /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500357 private void usbDeviceRemoved(String deviceName) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500358 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500359 UsbDevice device = mDevices.remove(deviceName);
360 if (device != null) {
361 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
Mike Lockwood188d00b2011-02-23 13:14:33 -0800362 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500363 Log.d(TAG, "usbDeviceRemoved, sending " + intent);
364 mContext.sendBroadcast(intent);
365 }
366 }
367 }
368
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400369 private void initHostSupport() {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500370 // Create a thread to call into native code to wait for USB host events.
371 // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
372 Runnable runnable = new Runnable() {
373 public void run() {
374 monitorUsbHostBus();
375 }
376 };
377 new Thread(null, runnable, "UsbService host thread").start();
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400378 }
379
Mike Lockwood24236072010-06-23 17:36:36 -0400380 void systemReady() {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500381 synchronized (mLock) {
Mike Lockwoodda39f0e2010-07-27 18:44:30 -0400382 if (mContext.getResources().getBoolean(
383 com.android.internal.R.bool.config_hasUsbHostSupport)) {
384 // start monitoring for connected USB devices
385 initHostSupport();
386 }
387
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800388 update(false);
Mike Lockwood24236072010-06-23 17:36:36 -0400389 mSystemReady = true;
390 }
391 }
392
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500393 /*
394 * Sends a message to update the USB connected and configured state (device mode).
395 * If delayed is true, then we add a small delay in sending the message to debounce
396 * the USB connection when enabling USB tethering.
397 */
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800398 private final void update(boolean delayed) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500399 mHandler.removeMessages(MSG_UPDATE_STATE);
400 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
Mike Lockwood24236072010-06-23 17:36:36 -0400401 }
402
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500403 /* Returns a list of all currently attached USB devices (host mdoe) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500404 public void getDeviceList(Bundle devices) {
405 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500406 synchronized (mLock) {
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500407 for (String name : mDevices.keySet()) {
408 devices.putParcelable(name, mDevices.get(name));
409 }
410 }
411 }
412
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500413 /* Opens the specified USB device (host mode) */
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500414 public ParcelFileDescriptor openDevice(String deviceName) {
Mike Lockwooda8e3a892011-02-01 13:46:50 -0500415 if (isBlackListed(deviceName)) {
416 throw new SecurityException("USB device is on a restricted bus");
417 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500418 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500419 if (mDevices.get(deviceName) == null) {
420 // if it is not in mDevices, it either does not exist or is blacklisted
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500421 throw new IllegalArgumentException(
422 "device " + deviceName + " does not exist or is restricted");
Mike Lockwood8b682ad2011-02-01 15:53:11 -0500423 }
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500424 return nativeOpenDevice(deviceName);
425 }
426
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500427 /* returns the currently attached USB accessory (device mode) */
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500428 public UsbAccessory getCurrentAccessory() {
429 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
430 return mCurrentAccessory;
431 }
432
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500433 /* opens the currently attached USB accessory (device mode) */
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500434 public ParcelFileDescriptor openAccessory() {
435 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
436 return nativeOpenAccessory();
437 }
438
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500439 /*
440 * This handler is for deferred handling of events related to device mode and accessories.
441 */
Mike Lockwood24236072010-06-23 17:36:36 -0400442 private final Handler mHandler = new Handler() {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500443 private void addEnabledFunctionsLocked(Intent intent) {
Mike Lockwood709981e2010-06-28 09:58:58 -0400444 // include state of all USB functions in our extras
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500445 for (int i = 0; i < mEnabledFunctions.size(); i++) {
446 intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
447 }
448 for (int i = 0; i < mDisabledFunctions.size(); i++) {
449 intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
Mike Lockwood709981e2010-06-28 09:58:58 -0400450 }
451 }
452
Mike Lockwood24236072010-06-23 17:36:36 -0400453 @Override
454 public void handleMessage(Message msg) {
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500455 synchronized (mLock) {
456 switch (msg.what) {
457 case MSG_UPDATE_STATE:
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800458 if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500459 if (mConnected == 0 && mCurrentAccessory != null) {
460 // turn off accessory mode when we are disconnected
461 if (UsbManager.setFunctionEnabled(
462 UsbManager.USB_FUNCTION_ACCESSORY, false)) {
463 Log.d(TAG, "exited USB accessory mode");
464
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500465 // restore previously enabled functions
466 for (String function : mAccessoryRestoreFunctions) {
467 if (UsbManager.setFunctionEnabled(function, true)) {
468 Log.e(TAG, "could not reenable function " + function);
469 }
470 }
471 mAccessoryRestoreFunctions.clear();
472
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500473 Intent intent = new Intent(
474 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
475 intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
476 mContext.sendBroadcast(intent);
477 mCurrentAccessory = null;
478
479 // this will cause an immediate reset of the USB bus,
480 // so there is no point in sending the
481 // function disabled broadcast.
482 return;
483 } else {
484 Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY");
485 }
486 }
Mike Lockwood24236072010-06-23 17:36:36 -0400487
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800488 final ContentResolver cr = mContext.getContentResolver();
489 if (Settings.Secure.getInt(cr,
490 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
491 Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
492 return;
493 }
494
495 mLastConnected = mConnected;
496 mLastConfiguration = mConfiguration;
497
498 // send a sticky broadcast containing current USB state
499 Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
500 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
501 intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
502 intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500503 addEnabledFunctionsLocked(intent);
Mike Lockwoodb92df0f2010-12-10 16:19:32 -0800504 mContext.sendStickyBroadcast(intent);
Mike Lockwood24236072010-06-23 17:36:36 -0400505 }
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500506 break;
507 case MSG_FUNCTION_ENABLED:
508 case MSG_FUNCTION_DISABLED:
509 functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
510 break;
511 }
Mike Lockwood24236072010-06-23 17:36:36 -0400512 }
513 }
514 };
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500515
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500516 // host support
Mike Lockwoode7d511e2010-12-30 13:39:37 -0500517 private native void monitorUsbHostBus();
518 private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
Mike Lockwoodddc6cad2011-02-17 09:21:25 -0500519 // accessory support
Mike Lockwood9182d3c2011-02-15 09:50:22 -0500520 private native String[] nativeGetAccessoryStrings();
521 private native ParcelFileDescriptor nativeOpenAccessory();
Mike Lockwood24236072010-06-23 17:36:36 -0400522}