UsbService.java revision 1c0e543638fa940651b675645146fd71c2ebd9b9
1/*
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.usb;
18
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.hardware.usb.IUsbManager;
27import android.hardware.usb.UsbAccessory;
28import android.hardware.usb.UsbConstants;
29import android.hardware.usb.UsbDevice;
30import android.hardware.usb.UsbEndpoint;
31import android.hardware.usb.UsbInterface;
32import android.hardware.usb.UsbManager;
33import android.net.Uri;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.Message;
38import android.os.Parcelable;
39import android.os.ParcelFileDescriptor;
40import android.os.UEventObserver;
41import android.provider.Settings;
42import android.util.Log;
43import android.util.Slog;
44
45import java.io.File;
46import java.io.FileDescriptor;
47import java.io.FileNotFoundException;
48import java.io.FileReader;
49import java.io.PrintWriter;
50import java.util.ArrayList;
51import java.util.HashMap;
52import java.util.List;
53
54/**
55 * UsbService monitors for changes to USB state.
56 * This includes code for both USB host support (where the android device is the host)
57 * as well as USB device support (android device is connected to a USB host).
58 * Accessory mode is a special case of USB device mode, where the android device is
59 * connected to a USB host that supports the android accessory protocol.
60 */
61public class UsbService extends IUsbManager.Stub {
62    private static final String TAG = UsbService.class.getSimpleName();
63    private static final boolean LOG = false;
64
65    private static final String USB_CONNECTED_MATCH =
66            "DEVPATH=/devices/virtual/switch/usb_connected";
67    private static final String USB_CONFIGURATION_MATCH =
68            "DEVPATH=/devices/virtual/switch/usb_configuration";
69    private static final String USB_FUNCTIONS_MATCH =
70            "DEVPATH=/devices/virtual/usb_composite/";
71    private static final String USB_CONNECTED_PATH =
72            "/sys/class/switch/usb_connected/state";
73    private static final String USB_CONFIGURATION_PATH =
74            "/sys/class/switch/usb_configuration/state";
75    private static final String USB_COMPOSITE_CLASS_PATH =
76            "/sys/class/usb_composite";
77
78    private static final int MSG_UPDATE_STATE = 0;
79    private static final int MSG_FUNCTION_ENABLED = 1;
80    private static final int MSG_FUNCTION_DISABLED = 2;
81
82    // Delay for debouncing USB disconnects.
83    // We often get rapid connect/disconnect events when enabling USB functions,
84    // which need debouncing.
85    private static final int UPDATE_DELAY = 1000;
86
87    // current connected and configuration state
88    private int mConnected;
89    private int mConfiguration;
90
91    // last broadcasted connected and configuration state
92    private int mLastConnected = -1;
93    private int mLastConfiguration = -1;
94
95    // lists of enabled and disabled USB functions (for USB device mode)
96    private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
97    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
98
99    // contains all connected USB devices (for USB host mode)
100    private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
101
102    // USB busses to exclude from USB host support
103    private final String[] mHostBlacklist;
104
105    private boolean mSystemReady;
106
107    private UsbAccessory mCurrentAccessory;
108    // USB functions that are enabled by default, to restore after exiting accessory mode
109    private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
110
111    private final Context mContext;
112    private final Object mLock = new Object();
113    private final UsbDeviceSettingsManager mDeviceManager;
114    private final boolean mHasUsbHost;
115    private final boolean mHasUsbAccessory;
116
117    private final void readCurrentAccessoryLocked() {
118        if (mHasUsbAccessory) {
119            String[] strings = nativeGetAccessoryStrings();
120            if (strings != null) {
121                mCurrentAccessory = new UsbAccessory(strings);
122                Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
123                if (mSystemReady) {
124                    mDeviceManager.accessoryAttached(mCurrentAccessory);
125                }
126            } else {
127                Log.e(TAG, "nativeGetAccessoryStrings failed");
128            }
129        }
130    }
131
132    /*
133     * Handles USB function enable/disable events (device mode)
134     */
135    private final void functionEnabledLocked(String function, boolean enabled) {
136        if (enabled) {
137            if (!mEnabledFunctions.contains(function)) {
138                mEnabledFunctions.add(function);
139            }
140            mDisabledFunctions.remove(function);
141
142            if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
143                readCurrentAccessoryLocked();
144            }
145        } else {
146            if (!mDisabledFunctions.contains(function)) {
147                mDisabledFunctions.add(function);
148            }
149            mEnabledFunctions.remove(function);
150        }
151    }
152
153    /*
154     * Listens for uevent messages from the kernel to monitor the USB state (device mode)
155     */
156    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            }
162
163            synchronized (mLock) {
164                String name = event.get("SWITCH_NAME");
165                String state = event.get("SWITCH_STATE");
166                if (name != null && state != null) {
167                    try {
168                        int intState = Integer.parseInt(state);
169                        if ("usb_connected".equals(name)) {
170                            mConnected = intState;
171                            // trigger an Intent broadcast
172                            if (mSystemReady) {
173                                // debounce disconnects to avoid problems bringing up USB tethering
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);
181                            }
182                        }
183                    } catch (NumberFormatException e) {
184                        Slog.e(TAG, "Could not parse switch state from event " + event);
185                    }
186                } 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.
192                        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);
197                    }
198                }
199            }
200        }
201    };
202
203   private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
204        public void onReceive(Context context, Intent intent) {
205            // handle accessories attached at boot time
206            synchronized (mLock) {
207                if (mCurrentAccessory != null) {
208                    mDeviceManager.accessoryAttached(mCurrentAccessory);
209                }
210            }
211        }
212    };
213
214    public UsbService(Context context) {
215        mContext = context;
216        mDeviceManager = new UsbDeviceSettingsManager(context);
217        PackageManager pm = mContext.getPackageManager();
218        mHasUsbHost = pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST);
219        mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
220
221        mHostBlacklist = context.getResources().getStringArray(
222                com.android.internal.R.array.config_usbHostBlacklist);
223
224        synchronized (mLock) {
225            init();  // set initial status
226
227            // Watch for USB configuration changes
228            if (mConfiguration >= 0) {
229                mUEventObserver.startObserving(USB_CONNECTED_MATCH);
230                mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
231                mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
232            }
233        }
234    }
235
236    private final void init() {
237        char[] buffer = new char[1024];
238        boolean inAccessoryMode = false;
239
240        // Read initial USB state (device mode)
241        mConfiguration = -1;
242        try {
243            FileReader file = new FileReader(USB_CONNECTED_PATH);
244            int len = file.read(buffer, 0, 1024);
245            file.close();
246            mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
247
248            file = new FileReader(USB_CONFIGURATION_PATH);
249            len = file.read(buffer, 0, 1024);
250            file.close();
251            mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
252
253        } catch (FileNotFoundException e) {
254            Slog.i(TAG, "This kernel does not have USB configuration switch support");
255        } catch (Exception e) {
256            Slog.e(TAG, "" , e);
257        }
258        if (mConfiguration < 0) {
259            // This may happen in the emulator or devices without USB device mode support
260            return;
261        }
262
263        // Read initial list of enabled and disabled functions (device mode)
264        try {
265            File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
266            for (int i = 0; i < files.length; i++) {
267                File file = new File(files[i], "enable");
268                FileReader reader = new FileReader(file);
269                int len = reader.read(buffer, 0, 1024);
270                reader.close();
271                int value = Integer.valueOf((new String(buffer, 0, len)).trim());
272                String functionName = files[i].getName();
273                if (value == 1) {
274                    mEnabledFunctions.add(functionName);
275                if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
276                        // The USB accessory driver is on by default, but it might have been
277                        // enabled before the USB service has initialized.
278                        inAccessoryMode = true;
279                    } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
280                        // adb is enabled/disabled automatically by the adbd daemon,
281                        // so don't treat it as a default function.
282                        mDefaultFunctions.add(functionName);
283                    }
284                } else {
285                    mDisabledFunctions.add(functionName);
286                }
287            }
288        } catch (FileNotFoundException e) {
289            Slog.w(TAG, "This kernel does not have USB composite class support");
290        } catch (Exception e) {
291            Slog.e(TAG, "" , e);
292        }
293
294        // handle the case where an accessory switched the driver to accessory mode
295        // before the framework finished booting
296        if (inAccessoryMode) {
297            readCurrentAccessoryLocked();
298
299            // FIXME - if we booted in accessory mode, then we have no way to figure out
300            // which functions are enabled by default.
301            // For now, assume that MTP or mass storage are the only possibilities
302            if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
303                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
304            } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
305                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
306            }
307        }
308    }
309
310    private boolean isBlackListed(String deviceName) {
311        int count = mHostBlacklist.length;
312        for (int i = 0; i < count; i++) {
313            if (deviceName.startsWith(mHostBlacklist[i])) {
314                return true;
315            }
316        }
317        return false;
318    }
319
320    /* returns true if the USB device should not be accessible by applications (host mode) */
321    private boolean isBlackListed(int clazz, int subClass, int protocol) {
322        // blacklist hubs
323        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
324
325        // blacklist HID boot devices (mouse and keyboard)
326        if (clazz == UsbConstants.USB_CLASS_HID &&
327                subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
328            return true;
329        }
330
331        return false;
332    }
333
334    /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
335    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
336            int deviceClass, int deviceSubclass, int deviceProtocol,
337            /* array of quintuples containing id, class, subclass, protocol
338               and number of endpoints for each interface */
339            int[] interfaceValues,
340           /* array of quadruples containing address, attributes, max packet size
341              and interval for each endpoint */
342            int[] endpointValues) {
343
344        if (isBlackListed(deviceName) ||
345                isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
346            return;
347        }
348
349        synchronized (mLock) {
350            if (mDevices.get(deviceName) != null) {
351                Log.w(TAG, "device already on mDevices list: " + deviceName);
352                return;
353            }
354
355            int numInterfaces = interfaceValues.length / 5;
356            Parcelable[] interfaces = new UsbInterface[numInterfaces];
357            try {
358                // repackage interfaceValues as an array of UsbInterface
359                int intf, endp, ival = 0, eval = 0;
360                for (intf = 0; intf < numInterfaces; intf++) {
361                    int interfaceId = interfaceValues[ival++];
362                    int interfaceClass = interfaceValues[ival++];
363                    int interfaceSubclass = interfaceValues[ival++];
364                    int interfaceProtocol = interfaceValues[ival++];
365                    int numEndpoints = interfaceValues[ival++];
366
367                    Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
368                    for (endp = 0; endp < numEndpoints; endp++) {
369                        int address = endpointValues[eval++];
370                        int attributes = endpointValues[eval++];
371                        int maxPacketSize = endpointValues[eval++];
372                        int interval = endpointValues[eval++];
373                        endpoints[endp] = new UsbEndpoint(address, attributes,
374                                maxPacketSize, interval);
375                    }
376
377                    // don't allow if any interfaces are blacklisted
378                    if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
379                        return;
380                    }
381                    interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
382                            interfaceSubclass, interfaceProtocol, endpoints);
383                }
384            } catch (Exception e) {
385                // beware of index out of bound exceptions, which might happen if
386                // a device does not set bNumEndpoints correctly
387                Log.e(TAG, "error parsing USB descriptors", e);
388                return;
389            }
390
391            UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
392                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
393            mDevices.put(deviceName, device);
394            mDeviceManager.deviceAttached(device);
395        }
396    }
397
398    /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
399    private void usbDeviceRemoved(String deviceName) {
400        synchronized (mLock) {
401            UsbDevice device = mDevices.remove(deviceName);
402            if (device != null) {
403                mDeviceManager.deviceDetached(device);
404            }
405        }
406    }
407
408    private void initHostSupport() {
409        // Create a thread to call into native code to wait for USB host events.
410        // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
411        Runnable runnable = new Runnable() {
412            public void run() {
413                monitorUsbHostBus();
414            }
415        };
416        new Thread(null, runnable, "UsbService host thread").start();
417    }
418
419    public void systemReady() {
420        synchronized (mLock) {
421            if (mHasUsbHost) {
422                // start monitoring for connected USB devices
423                initHostSupport();
424            }
425
426            update(false);
427            if (mCurrentAccessory != null) {
428                Log.d(TAG, "accessoryAttached at systemReady");
429                // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
430                // to handle this later.
431                mContext.registerReceiver(mBootCompletedReceiver,
432                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
433            }
434            mSystemReady = true;
435        }
436    }
437
438    /*
439     * Sends a message to update the USB connected and configured state (device mode).
440     * If delayed is true, then we add a small delay in sending the message to debounce
441     * the USB connection when enabling USB tethering.
442     */
443    private final void update(boolean delayed) {
444        mHandler.removeMessages(MSG_UPDATE_STATE);
445        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
446    }
447
448    /* Returns a list of all currently attached USB devices (host mdoe) */
449    public void getDeviceList(Bundle devices) {
450        synchronized (mLock) {
451            for (String name : mDevices.keySet()) {
452                devices.putParcelable(name, mDevices.get(name));
453            }
454        }
455    }
456
457    /* Opens the specified USB device (host mode) */
458    public ParcelFileDescriptor openDevice(String deviceName) {
459        synchronized (mLock) {
460            if (isBlackListed(deviceName)) {
461                throw new SecurityException("USB device is on a restricted bus");
462            }
463            UsbDevice device = mDevices.get(deviceName);
464            if (device == null) {
465                // if it is not in mDevices, it either does not exist or is blacklisted
466                throw new IllegalArgumentException(
467                        "device " + deviceName + " does not exist or is restricted");
468            }
469            mDeviceManager.checkPermission(device);
470            return nativeOpenDevice(deviceName);
471        }
472    }
473
474    /* returns the currently attached USB accessory (device mode) */
475    public UsbAccessory getCurrentAccessory() {
476        return mCurrentAccessory;
477    }
478
479    /* opens the currently attached USB accessory (device mode) */
480    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
481        synchronized (mLock) {
482            if (mCurrentAccessory == null) {
483                throw new IllegalArgumentException("no accessory attached");
484            }
485            if (!mCurrentAccessory.equals(accessory)) {
486                Log.e(TAG, accessory.toString() + " does not match current accessory "
487                        + mCurrentAccessory);
488                throw new IllegalArgumentException("accessory not attached");
489            }
490            mDeviceManager.checkPermission(mCurrentAccessory);
491            return nativeOpenAccessory();
492        }
493    }
494
495    public void setDevicePackage(UsbDevice device, String packageName) {
496        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
497        mDeviceManager.setDevicePackage(device, packageName);
498    }
499
500    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
501        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
502        mDeviceManager.setAccessoryPackage(accessory, packageName);
503    }
504
505    public boolean hasDevicePermission(UsbDevice device) {
506        return mDeviceManager.hasPermission(device);
507    }
508
509    public boolean hasAccessoryPermission(UsbAccessory accessory) {
510        return mDeviceManager.hasPermission(accessory);
511    }
512
513    public void requestDevicePermission(UsbDevice device, String packageName,
514            PendingIntent pi) {
515        mDeviceManager.requestPermission(device, packageName, pi);
516    }
517
518    public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
519            PendingIntent pi) {
520        mDeviceManager.requestPermission(accessory, packageName, pi);
521    }
522
523    public void grantDevicePermission(UsbDevice device, int uid) {
524        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
525        mDeviceManager.grantDevicePermission(device, uid);
526    }
527
528    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
529        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
530        mDeviceManager.grantAccessoryPermission(accessory, uid);
531    }
532
533    public boolean hasDefaults(String packageName) {
534        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
535        return mDeviceManager.hasDefaults(packageName);
536    }
537
538    public void clearDefaults(String packageName) {
539        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
540        mDeviceManager.clearDefaults(packageName);
541    }
542
543    /*
544     * This handler is for deferred handling of events related to device mode and accessories.
545     */
546    private final Handler mHandler = new Handler() {
547        private void addEnabledFunctionsLocked(Intent intent) {
548            // include state of all USB functions in our extras
549            for (int i = 0; i < mEnabledFunctions.size(); i++) {
550                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
551            }
552            for (int i = 0; i < mDisabledFunctions.size(); i++) {
553                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
554            }
555        }
556
557        @Override
558        public void handleMessage(Message msg) {
559            synchronized (mLock) {
560                switch (msg.what) {
561                    case MSG_UPDATE_STATE:
562                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
563                            if (mConnected == 0) {
564                                // make sure accessory mode is off, and restore default functions
565                                if (mCurrentAccessory != null && UsbManager.setFunctionEnabled(
566                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
567                                    Log.d(TAG, "exited USB accessory mode");
568
569                                    int count = mDefaultFunctions.size();
570                                    for (int i = 0; i < count; i++) {
571                                        String function = mDefaultFunctions.get(i);
572                                        if (!UsbManager.setFunctionEnabled(function, true)) {
573                                            Log.e(TAG, "could not reenable function " + function);
574                                        }
575                                    }
576
577                                    mDeviceManager.accessoryDetached(mCurrentAccessory);
578                                    mCurrentAccessory = null;
579                                }
580                            }
581
582                            final ContentResolver cr = mContext.getContentResolver();
583                            if (Settings.Secure.getInt(cr,
584                                    Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
585                                Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
586                                return;
587                            }
588
589                            mLastConnected = mConnected;
590                            mLastConfiguration = mConfiguration;
591
592                            // send a sticky broadcast containing current USB state
593                            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
594                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
595                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
596                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
597                            addEnabledFunctionsLocked(intent);
598                            mContext.sendStickyBroadcast(intent);
599                        }
600                        break;
601                    case MSG_FUNCTION_ENABLED:
602                    case MSG_FUNCTION_DISABLED:
603                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
604                        break;
605                }
606            }
607        }
608    };
609
610    @Override
611    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
612        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
613                != PackageManager.PERMISSION_GRANTED) {
614            pw.println("Permission Denial: can't dump UsbManager from from pid="
615                    + Binder.getCallingPid()
616                    + ", uid=" + Binder.getCallingUid());
617            return;
618        }
619
620        synchronized (mLock) {
621            pw.println("USB Manager State:");
622
623            pw.println("  USB Device State:");
624            pw.print("    Enabled Functions: ");
625            for (int i = 0; i < mEnabledFunctions.size(); i++) {
626                pw.print(mEnabledFunctions.get(i) + " ");
627            }
628            pw.println("");
629            pw.print("    Disabled Functions: ");
630            for (int i = 0; i < mDisabledFunctions.size(); i++) {
631                pw.print(mDisabledFunctions.get(i) + " ");
632            }
633            pw.println("");
634            pw.print("    Default Functions: ");
635            for (int i = 0; i < mDefaultFunctions.size(); i++) {
636                pw.print(mDefaultFunctions.get(i) + " ");
637            }
638            pw.println("");
639            pw.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
640            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
641
642            pw.println("  USB Host State:");
643            for (String name : mDevices.keySet()) {
644                pw.println("    " + name + ": " + mDevices.get(name));
645            }
646            mDeviceManager.dump(fd, pw);
647        }
648    }
649
650    // host support
651    private native void monitorUsbHostBus();
652    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
653    // accessory support
654    private native String[] nativeGetAccessoryStrings();
655    private native ParcelFileDescriptor nativeOpenAccessory();
656}
657