UsbService: Refactor USB host and device support into two separate classes

Host support is in UsbHostManager, device support is in UsbDeviceManager

Renamed UsbDeviceSettingsManager to UsbSettingsManager

Change-Id: Ib76e72957c233fa7f08f454d4d9a2a1da6368cc7
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
new file mode 100644
index 0000000..3791cc4
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UsbDeviceManager manages USB state in device mode.
+ */
+public class UsbDeviceManager {
+    private static final String TAG = UsbDeviceManager.class.getSimpleName();
+    private static final boolean LOG = false;
+
+    private static final String USB_CONNECTED_MATCH =
+            "DEVPATH=/devices/virtual/switch/usb_connected";
+    private static final String USB_CONFIGURATION_MATCH =
+            "DEVPATH=/devices/virtual/switch/usb_configuration";
+    private static final String USB_FUNCTIONS_MATCH =
+            "DEVPATH=/devices/virtual/usb_composite/";
+    private static final String USB_CONNECTED_PATH =
+            "/sys/class/switch/usb_connected/state";
+    private static final String USB_CONFIGURATION_PATH =
+            "/sys/class/switch/usb_configuration/state";
+    private static final String USB_COMPOSITE_CLASS_PATH =
+            "/sys/class/usb_composite";
+
+    private static final int MSG_UPDATE_STATE = 0;
+    private static final int MSG_FUNCTION_ENABLED = 1;
+    private static final int MSG_FUNCTION_DISABLED = 2;
+
+    // Delay for debouncing USB disconnects.
+    // We often get rapid connect/disconnect events when enabling USB functions,
+    // which need debouncing.
+    private static final int UPDATE_DELAY = 1000;
+
+    // current connected and configuration state
+    private int mConnected;
+    private int mConfiguration;
+
+    // last broadcasted connected and configuration state
+    private int mLastConnected = -1;
+    private int mLastConfiguration = -1;
+
+    // lists of enabled and disabled USB functions
+    private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
+    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
+
+    private boolean mSystemReady;
+
+    private UsbAccessory mCurrentAccessory;
+    // USB functions that are enabled by default, to restore after exiting accessory mode
+    private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private final UsbSettingsManager mSettingsManager;
+    private final boolean mHasUsbAccessory;
+
+    private final void readCurrentAccessoryLocked() {
+        if (mHasUsbAccessory) {
+            String[] strings = nativeGetAccessoryStrings();
+            if (strings != null) {
+                mCurrentAccessory = new UsbAccessory(strings);
+                Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+                if (mSystemReady) {
+                    mSettingsManager.accessoryAttached(mCurrentAccessory);
+                }
+            } else {
+                Log.e(TAG, "nativeGetAccessoryStrings failed");
+            }
+        }
+    }
+
+    /*
+     * Handles USB function enable/disable events
+     */
+    private final void functionEnabledLocked(String function, boolean enabled) {
+        if (enabled) {
+            if (!mEnabledFunctions.contains(function)) {
+                mEnabledFunctions.add(function);
+            }
+            mDisabledFunctions.remove(function);
+
+            if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
+                readCurrentAccessoryLocked();
+            }
+        } else {
+            if (!mDisabledFunctions.contains(function)) {
+                mDisabledFunctions.add(function);
+            }
+            mEnabledFunctions.remove(function);
+        }
+    }
+
+    /*
+     * Listens for uevent messages from the kernel to monitor the USB state
+     */
+    private final UEventObserver mUEventObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEventObserver.UEvent event) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Slog.v(TAG, "USB UEVENT: " + event.toString());
+            }
+
+            synchronized (mLock) {
+                String name = event.get("SWITCH_NAME");
+                String state = event.get("SWITCH_STATE");
+                if (name != null && state != null) {
+                    try {
+                        int intState = Integer.parseInt(state);
+                        if ("usb_connected".equals(name)) {
+                            mConnected = intState;
+                            // trigger an Intent broadcast
+                            if (mSystemReady) {
+                                // debounce disconnects to avoid problems bringing up USB tethering
+                                update(mConnected == 0);
+                            }
+                        } else if ("usb_configuration".equals(name)) {
+                            mConfiguration = intState;
+                            // trigger an Intent broadcast
+                            if (mSystemReady) {
+                                update(mConnected == 0);
+                            }
+                        }
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG, "Could not parse switch state from event " + event);
+                    }
+                } else {
+                    String function = event.get("FUNCTION");
+                    String enabledStr = event.get("ENABLED");
+                    if (function != null && enabledStr != null) {
+                        // Note: we do not broadcast a change when a function is enabled or disabled.
+                        // We just record the state change for the next broadcast.
+                        int what = ("1".equals(enabledStr) ?
+                                MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
+                        Message msg = Message.obtain(mHandler, what);
+                        msg.obj = function;
+                        mHandler.sendMessage(msg);
+                    }
+                }
+            }
+        }
+    };
+
+   private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            // handle accessories attached at boot time
+            synchronized (mLock) {
+                if (mCurrentAccessory != null) {
+                    mSettingsManager.accessoryAttached(mCurrentAccessory);
+                }
+            }
+        }
+    };
+
+    public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) {
+        mContext = context;
+        mSettingsManager = settingsManager;
+        PackageManager pm = mContext.getPackageManager();
+        mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+
+        synchronized (mLock) {
+            init();  // set initial status
+
+            // Watch for USB configuration changes
+            if (mConfiguration >= 0) {
+                mUEventObserver.startObserving(USB_CONNECTED_MATCH);
+                mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
+                mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+            }
+        }
+    }
+
+    private final void init() {
+        char[] buffer = new char[1024];
+        boolean inAccessoryMode = false;
+
+        // Read initial USB state
+        mConfiguration = -1;
+        try {
+            FileReader file = new FileReader(USB_CONNECTED_PATH);
+            int len = file.read(buffer, 0, 1024);
+            file.close();
+            mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+            file = new FileReader(USB_CONFIGURATION_PATH);
+            len = file.read(buffer, 0, 1024);
+            file.close();
+            mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+        } catch (FileNotFoundException e) {
+            Slog.i(TAG, "This kernel does not have USB configuration switch support");
+        } catch (Exception e) {
+            Slog.e(TAG, "" , e);
+        }
+        if (mConfiguration < 0) {
+            // This may happen in the emulator or devices without USB device mode support
+            return;
+        }
+
+        // Read initial list of enabled and disabled functions (device mode)
+        try {
+            File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
+            for (int i = 0; i < files.length; i++) {
+                File file = new File(files[i], "enable");
+                FileReader reader = new FileReader(file);
+                int len = reader.read(buffer, 0, 1024);
+                reader.close();
+                int value = Integer.valueOf((new String(buffer, 0, len)).trim());
+                String functionName = files[i].getName();
+                if (value == 1) {
+                    mEnabledFunctions.add(functionName);
+                if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
+                        // The USB accessory driver is on by default, but it might have been
+                        // enabled before the USB service has initialized.
+                        inAccessoryMode = true;
+                    } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
+                        // adb is enabled/disabled automatically by the adbd daemon,
+                        // so don't treat it as a default function.
+                        mDefaultFunctions.add(functionName);
+                    }
+                } else {
+                    mDisabledFunctions.add(functionName);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "This kernel does not have USB composite class support");
+        } catch (Exception e) {
+            Slog.e(TAG, "" , e);
+        }
+
+        // handle the case where an accessory switched the driver to accessory mode
+        // before the framework finished booting
+        if (inAccessoryMode) {
+            readCurrentAccessoryLocked();
+
+            // FIXME - if we booted in accessory mode, then we have no way to figure out
+            // which functions are enabled by default.
+            // For now, assume that MTP or mass storage are the only possibilities
+            if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
+                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
+            } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
+                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
+            }
+        }
+    }
+
+    public void systemReady() {
+        synchronized (mLock) {
+            update(false);
+            if (mCurrentAccessory != null) {
+                Log.d(TAG, "accessoryAttached at systemReady");
+                // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
+                // to handle this later.
+                mContext.registerReceiver(mBootCompletedReceiver,
+                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+            }
+            mSystemReady = true;
+        }
+    }
+
+    /*
+     * Sends a message to update the USB connected and configured state (device mode).
+     * If delayed is true, then we add a small delay in sending the message to debounce
+     * the USB connection when enabling USB tethering.
+     */
+    private final void update(boolean delayed) {
+        mHandler.removeMessages(MSG_UPDATE_STATE);
+        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
+    }
+
+    /* returns the currently attached USB accessory (device mode) */
+    public UsbAccessory getCurrentAccessory() {
+        return mCurrentAccessory;
+    }
+
+    /* opens the currently attached USB accessory (device mode) */
+    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+        synchronized (mLock) {
+            if (mCurrentAccessory == null) {
+                throw new IllegalArgumentException("no accessory attached");
+            }
+            if (!mCurrentAccessory.equals(accessory)) {
+                Log.e(TAG, accessory.toString() + " does not match current accessory "
+                        + mCurrentAccessory);
+                throw new IllegalArgumentException("accessory not attached");
+            }
+            mSettingsManager.checkPermission(mCurrentAccessory);
+            return nativeOpenAccessory();
+        }
+    }
+
+    /*
+     * This handler is for deferred handling of events related to device mode and accessories.
+     */
+    private final Handler mHandler = new Handler() {
+        private void addEnabledFunctionsLocked(Intent intent) {
+            // include state of all USB functions in our extras
+            for (int i = 0; i < mEnabledFunctions.size(); i++) {
+                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
+            }
+            for (int i = 0; i < mDisabledFunctions.size(); i++) {
+                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
+            }
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized (mLock) {
+                switch (msg.what) {
+                    case MSG_UPDATE_STATE:
+                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
+                            if (mConnected == 0) {
+                                if (UsbManager.isFunctionEnabled(
+                                            UsbManager.USB_FUNCTION_ACCESSORY)) {
+                                    // make sure accessory mode is off, and restore default functions
+                                    Log.d(TAG, "exited USB accessory mode");
+                                    if (!UsbManager.setFunctionEnabled
+                                            (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+                                        Log.e(TAG, "could not disable accessory function");
+                                    }
+                                    int count = mDefaultFunctions.size();
+                                    for (int i = 0; i < count; i++) {
+                                        String function = mDefaultFunctions.get(i);
+                                        if (!UsbManager.setFunctionEnabled(function, true)) {
+                                            Log.e(TAG, "could not reenable function " + function);
+                                        }
+                                    }
+
+                                    if (mCurrentAccessory != null) {
+                                        mSettingsManager.accessoryDetached(mCurrentAccessory);
+                                        mCurrentAccessory = null;
+                                    }
+                                }
+                            }
+
+                            final ContentResolver cr = mContext.getContentResolver();
+                            if (Settings.Secure.getInt(cr,
+                                    Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+                                Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+                                return;
+                            }
+
+                            mLastConnected = mConnected;
+                            mLastConfiguration = mConfiguration;
+
+                            // send a sticky broadcast containing current USB state
+                            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
+                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
+                            addEnabledFunctionsLocked(intent);
+                            mContext.sendStickyBroadcast(intent);
+                        }
+                        break;
+                    case MSG_FUNCTION_ENABLED:
+                    case MSG_FUNCTION_DISABLED:
+                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
+                        break;
+                }
+            }
+        }
+    };
+
+    public void dump(FileDescriptor fd, PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("  USB Device State:");
+            pw.print("    Enabled Functions: ");
+            for (int i = 0; i < mEnabledFunctions.size(); i++) {
+                pw.print(mEnabledFunctions.get(i) + " ");
+            }
+            pw.println("");
+            pw.print("    Disabled Functions: ");
+            for (int i = 0; i < mDisabledFunctions.size(); i++) {
+                pw.print(mDisabledFunctions.get(i) + " ");
+            }
+            pw.println("");
+            pw.print("    Default Functions: ");
+            for (int i = 0; i < mDefaultFunctions.size(); i++) {
+                pw.print(mDefaultFunctions.get(i) + " ");
+            }
+            pw.println("");
+            pw.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
+        }
+    }
+
+    private native String[] nativeGetAccessoryStrings();
+    private native ParcelFileDescriptor nativeOpenAccessory();
+}
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
new file mode 100644
index 0000000..923b049
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * UsbHostManager manages USB state in host mode.
+ */
+public class UsbHostManager {
+    private static final String TAG = UsbHostManager.class.getSimpleName();
+    private static final boolean LOG = false;
+
+    // contains all connected USB devices
+    private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
+
+    // USB busses to exclude from USB host support
+    private final String[] mHostBlacklist;
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private final UsbSettingsManager mSettingsManager;
+
+    public UsbHostManager(Context context, UsbSettingsManager settingsManager) {
+        mContext = context;
+        mSettingsManager = settingsManager;
+        mHostBlacklist = context.getResources().getStringArray(
+                com.android.internal.R.array.config_usbHostBlacklist);
+    }
+
+    private boolean isBlackListed(String deviceName) {
+        int count = mHostBlacklist.length;
+        for (int i = 0; i < count; i++) {
+            if (deviceName.startsWith(mHostBlacklist[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* returns true if the USB device should not be accessible by applications */
+    private boolean isBlackListed(int clazz, int subClass, int protocol) {
+        // blacklist hubs
+        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
+
+        // blacklist HID boot devices (mouse and keyboard)
+        if (clazz == UsbConstants.USB_CLASS_HID &&
+                subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /* Called from JNI in monitorUsbHostBus() to report new USB devices */
+    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
+            int deviceClass, int deviceSubclass, int deviceProtocol,
+            /* array of quintuples containing id, class, subclass, protocol
+               and number of endpoints for each interface */
+            int[] interfaceValues,
+           /* array of quadruples containing address, attributes, max packet size
+              and interval for each endpoint */
+            int[] endpointValues) {
+
+        if (isBlackListed(deviceName) ||
+                isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
+            return;
+        }
+
+        synchronized (mLock) {
+            if (mDevices.get(deviceName) != null) {
+                Log.w(TAG, "device already on mDevices list: " + deviceName);
+                return;
+            }
+
+            int numInterfaces = interfaceValues.length / 5;
+            Parcelable[] interfaces = new UsbInterface[numInterfaces];
+            try {
+                // repackage interfaceValues as an array of UsbInterface
+                int intf, endp, ival = 0, eval = 0;
+                for (intf = 0; intf < numInterfaces; intf++) {
+                    int interfaceId = interfaceValues[ival++];
+                    int interfaceClass = interfaceValues[ival++];
+                    int interfaceSubclass = interfaceValues[ival++];
+                    int interfaceProtocol = interfaceValues[ival++];
+                    int numEndpoints = interfaceValues[ival++];
+
+                    Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
+                    for (endp = 0; endp < numEndpoints; endp++) {
+                        int address = endpointValues[eval++];
+                        int attributes = endpointValues[eval++];
+                        int maxPacketSize = endpointValues[eval++];
+                        int interval = endpointValues[eval++];
+                        endpoints[endp] = new UsbEndpoint(address, attributes,
+                                maxPacketSize, interval);
+                    }
+
+                    // don't allow if any interfaces are blacklisted
+                    if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
+                        return;
+                    }
+                    interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
+                            interfaceSubclass, interfaceProtocol, endpoints);
+                }
+            } catch (Exception e) {
+                // beware of index out of bound exceptions, which might happen if
+                // a device does not set bNumEndpoints correctly
+                Log.e(TAG, "error parsing USB descriptors", e);
+                return;
+            }
+
+            UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
+                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
+            mDevices.put(deviceName, device);
+            mSettingsManager.deviceAttached(device);
+        }
+    }
+
+    /* Called from JNI in monitorUsbHostBus to report USB device removal */
+    private void usbDeviceRemoved(String deviceName) {
+        synchronized (mLock) {
+            UsbDevice device = mDevices.remove(deviceName);
+            if (device != null) {
+                mSettingsManager.deviceDetached(device);
+            }
+        }
+    }
+
+    public void systemReady() {
+        synchronized (mLock) {
+            // Create a thread to call into native code to wait for USB host events.
+            // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
+            Runnable runnable = new Runnable() {
+                public void run() {
+                    monitorUsbHostBus();
+                }
+            };
+            new Thread(null, runnable, "UsbService host thread").start();
+        }
+    }
+
+    /* Returns a list of all currently attached USB devices */
+    public void getDeviceList(Bundle devices) {
+        synchronized (mLock) {
+            for (String name : mDevices.keySet()) {
+                devices.putParcelable(name, mDevices.get(name));
+            }
+        }
+    }
+
+    /* Opens the specified USB device */
+    public ParcelFileDescriptor openDevice(String deviceName) {
+        synchronized (mLock) {
+            if (isBlackListed(deviceName)) {
+                throw new SecurityException("USB device is on a restricted bus");
+            }
+            UsbDevice device = mDevices.get(deviceName);
+            if (device == null) {
+                // if it is not in mDevices, it either does not exist or is blacklisted
+                throw new IllegalArgumentException(
+                        "device " + deviceName + " does not exist or is restricted");
+            }
+            mSettingsManager.checkPermission(device);
+            return nativeOpenDevice(deviceName);
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("  USB Host State:");
+            for (String name : mDevices.keySet()) {
+                pw.println("    " + name + ": " + mDevices.get(name));
+            }
+        }
+    }
+
+    private native void monitorUsbHostBus();
+    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
+}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index a151af0..21e5997c 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -10,608 +10,142 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * See the License for the specific language governing permissions an
  * limitations under the License.
  */
 
 package com.android.server.usb;
 
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
 import android.os.ParcelFileDescriptor;
-import android.os.UEventObserver;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 
 /**
- * UsbService monitors for changes to USB state.
- * This includes code for both USB host support (where the android device is the host)
- * as well as USB device support (android device is connected to a USB host).
- * Accessory mode is a special case of USB device mode, where the android device is
- * connected to a USB host that supports the android accessory protocol.
+ * UsbService manages all USB related state, including both host and device support.
+ * Host related events and calls are delegated to UsbHostManager, and device related
+ * support is delegated to UsbDeviceManager.
  */
 public class UsbService extends IUsbManager.Stub {
-    private static final String TAG = UsbService.class.getSimpleName();
-    private static final boolean LOG = false;
-
-    private static final String USB_CONNECTED_MATCH =
-            "DEVPATH=/devices/virtual/switch/usb_connected";
-    private static final String USB_CONFIGURATION_MATCH =
-            "DEVPATH=/devices/virtual/switch/usb_configuration";
-    private static final String USB_FUNCTIONS_MATCH =
-            "DEVPATH=/devices/virtual/usb_composite/";
-    private static final String USB_CONNECTED_PATH =
-            "/sys/class/switch/usb_connected/state";
-    private static final String USB_CONFIGURATION_PATH =
-            "/sys/class/switch/usb_configuration/state";
-    private static final String USB_COMPOSITE_CLASS_PATH =
-            "/sys/class/usb_composite";
-
-    private static final int MSG_UPDATE_STATE = 0;
-    private static final int MSG_FUNCTION_ENABLED = 1;
-    private static final int MSG_FUNCTION_DISABLED = 2;
-
-    // Delay for debouncing USB disconnects.
-    // We often get rapid connect/disconnect events when enabling USB functions,
-    // which need debouncing.
-    private static final int UPDATE_DELAY = 1000;
-
-    // current connected and configuration state
-    private int mConnected;
-    private int mConfiguration;
-
-    // last broadcasted connected and configuration state
-    private int mLastConnected = -1;
-    private int mLastConfiguration = -1;
-
-    // lists of enabled and disabled USB functions (for USB device mode)
-    private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
-    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
-
-    // contains all connected USB devices (for USB host mode)
-    private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
-
-    // USB busses to exclude from USB host support
-    private final String[] mHostBlacklist;
-
-    private boolean mSystemReady;
-
-    private UsbAccessory mCurrentAccessory;
-    // USB functions that are enabled by default, to restore after exiting accessory mode
-    private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
-
     private final Context mContext;
-    private final Object mLock = new Object();
-    private final UsbDeviceSettingsManager mDeviceManager;
-    private final boolean mHasUsbHost;
-    private final boolean mHasUsbAccessory;
+    private UsbDeviceManager mDeviceManager;
+    private UsbHostManager mHostManager;
+    private final UsbSettingsManager mSettingsManager;
 
-    private final void readCurrentAccessoryLocked() {
-        if (mHasUsbAccessory) {
-            String[] strings = nativeGetAccessoryStrings();
-            if (strings != null) {
-                mCurrentAccessory = new UsbAccessory(strings);
-                Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
-                if (mSystemReady) {
-                    mDeviceManager.accessoryAttached(mCurrentAccessory);
-                }
-            } else {
-                Log.e(TAG, "nativeGetAccessoryStrings failed");
-            }
-        }
-    }
-
-    /*
-     * Handles USB function enable/disable events (device mode)
-     */
-    private final void functionEnabledLocked(String function, boolean enabled) {
-        if (enabled) {
-            if (!mEnabledFunctions.contains(function)) {
-                mEnabledFunctions.add(function);
-            }
-            mDisabledFunctions.remove(function);
-
-            if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
-                readCurrentAccessoryLocked();
-            }
-        } else {
-            if (!mDisabledFunctions.contains(function)) {
-                mDisabledFunctions.add(function);
-            }
-            mEnabledFunctions.remove(function);
-        }
-    }
-
-    /*
-     * Listens for uevent messages from the kernel to monitor the USB state (device mode)
-     */
-    private final UEventObserver mUEventObserver = new UEventObserver() {
-        @Override
-        public void onUEvent(UEventObserver.UEvent event) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Slog.v(TAG, "USB UEVENT: " + event.toString());
-            }
-
-            synchronized (mLock) {
-                String name = event.get("SWITCH_NAME");
-                String state = event.get("SWITCH_STATE");
-                if (name != null && state != null) {
-                    try {
-                        int intState = Integer.parseInt(state);
-                        if ("usb_connected".equals(name)) {
-                            mConnected = intState;
-                            // trigger an Intent broadcast
-                            if (mSystemReady) {
-                                // debounce disconnects to avoid problems bringing up USB tethering
-                                update(mConnected == 0);
-                            }
-                        } else if ("usb_configuration".equals(name)) {
-                            mConfiguration = intState;
-                            // trigger an Intent broadcast
-                            if (mSystemReady) {
-                                update(mConnected == 0);
-                            }
-                        }
-                    } catch (NumberFormatException e) {
-                        Slog.e(TAG, "Could not parse switch state from event " + event);
-                    }
-                } else {
-                    String function = event.get("FUNCTION");
-                    String enabledStr = event.get("ENABLED");
-                    if (function != null && enabledStr != null) {
-                        // Note: we do not broadcast a change when a function is enabled or disabled.
-                        // We just record the state change for the next broadcast.
-                        int what = ("1".equals(enabledStr) ?
-                                MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
-                        Message msg = Message.obtain(mHandler, what);
-                        msg.obj = function;
-                        mHandler.sendMessage(msg);
-                    }
-                }
-            }
-        }
-    };
-
-   private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            // handle accessories attached at boot time
-            synchronized (mLock) {
-                if (mCurrentAccessory != null) {
-                    mDeviceManager.accessoryAttached(mCurrentAccessory);
-                }
-            }
-        }
-    };
 
     public UsbService(Context context) {
         mContext = context;
-        mDeviceManager = new UsbDeviceSettingsManager(context);
+        mSettingsManager = new UsbSettingsManager(context);
         PackageManager pm = mContext.getPackageManager();
-        mHasUsbHost = pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST);
-        mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
-
-        mHostBlacklist = context.getResources().getStringArray(
-                com.android.internal.R.array.config_usbHostBlacklist);
-
-        synchronized (mLock) {
-            init();  // set initial status
-
-            // Watch for USB configuration changes
-            if (mConfiguration >= 0) {
-                mUEventObserver.startObserving(USB_CONNECTED_MATCH);
-                mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
-                mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
-            }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
+            mHostManager = new UsbHostManager(context, mSettingsManager);
         }
-    }
-
-    private final void init() {
-        char[] buffer = new char[1024];
-        boolean inAccessoryMode = false;
-
-        // Read initial USB state (device mode)
-        mConfiguration = -1;
-        try {
-            FileReader file = new FileReader(USB_CONNECTED_PATH);
-            int len = file.read(buffer, 0, 1024);
-            file.close();
-            mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
-
-            file = new FileReader(USB_CONFIGURATION_PATH);
-            len = file.read(buffer, 0, 1024);
-            file.close();
-            mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
-
-        } catch (FileNotFoundException e) {
-            Slog.i(TAG, "This kernel does not have USB configuration switch support");
-        } catch (Exception e) {
-            Slog.e(TAG, "" , e);
+        if (new File("/sys/class/usb_composite").exists()) {
+            mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
         }
-        if (mConfiguration < 0) {
-            // This may happen in the emulator or devices without USB device mode support
-            return;
-        }
-
-        // Read initial list of enabled and disabled functions (device mode)
-        try {
-            File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
-            for (int i = 0; i < files.length; i++) {
-                File file = new File(files[i], "enable");
-                FileReader reader = new FileReader(file);
-                int len = reader.read(buffer, 0, 1024);
-                reader.close();
-                int value = Integer.valueOf((new String(buffer, 0, len)).trim());
-                String functionName = files[i].getName();
-                if (value == 1) {
-                    mEnabledFunctions.add(functionName);
-                if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
-                        // The USB accessory driver is on by default, but it might have been
-                        // enabled before the USB service has initialized.
-                        inAccessoryMode = true;
-                    } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
-                        // adb is enabled/disabled automatically by the adbd daemon,
-                        // so don't treat it as a default function.
-                        mDefaultFunctions.add(functionName);
-                    }
-                } else {
-                    mDisabledFunctions.add(functionName);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "This kernel does not have USB composite class support");
-        } catch (Exception e) {
-            Slog.e(TAG, "" , e);
-        }
-
-        // handle the case where an accessory switched the driver to accessory mode
-        // before the framework finished booting
-        if (inAccessoryMode) {
-            readCurrentAccessoryLocked();
-
-            // FIXME - if we booted in accessory mode, then we have no way to figure out
-            // which functions are enabled by default.
-            // For now, assume that MTP or mass storage are the only possibilities
-            if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
-                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
-            } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
-                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
-            }
-        }
-    }
-
-    private boolean isBlackListed(String deviceName) {
-        int count = mHostBlacklist.length;
-        for (int i = 0; i < count; i++) {
-            if (deviceName.startsWith(mHostBlacklist[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /* returns true if the USB device should not be accessible by applications (host mode) */
-    private boolean isBlackListed(int clazz, int subClass, int protocol) {
-        // blacklist hubs
-        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
-
-        // blacklist HID boot devices (mouse and keyboard)
-        if (clazz == UsbConstants.USB_CLASS_HID &&
-                subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
-    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
-            int deviceClass, int deviceSubclass, int deviceProtocol,
-            /* array of quintuples containing id, class, subclass, protocol
-               and number of endpoints for each interface */
-            int[] interfaceValues,
-           /* array of quadruples containing address, attributes, max packet size
-              and interval for each endpoint */
-            int[] endpointValues) {
-
-        if (isBlackListed(deviceName) ||
-                isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            if (mDevices.get(deviceName) != null) {
-                Log.w(TAG, "device already on mDevices list: " + deviceName);
-                return;
-            }
-
-            int numInterfaces = interfaceValues.length / 5;
-            Parcelable[] interfaces = new UsbInterface[numInterfaces];
-            try {
-                // repackage interfaceValues as an array of UsbInterface
-                int intf, endp, ival = 0, eval = 0;
-                for (intf = 0; intf < numInterfaces; intf++) {
-                    int interfaceId = interfaceValues[ival++];
-                    int interfaceClass = interfaceValues[ival++];
-                    int interfaceSubclass = interfaceValues[ival++];
-                    int interfaceProtocol = interfaceValues[ival++];
-                    int numEndpoints = interfaceValues[ival++];
-
-                    Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
-                    for (endp = 0; endp < numEndpoints; endp++) {
-                        int address = endpointValues[eval++];
-                        int attributes = endpointValues[eval++];
-                        int maxPacketSize = endpointValues[eval++];
-                        int interval = endpointValues[eval++];
-                        endpoints[endp] = new UsbEndpoint(address, attributes,
-                                maxPacketSize, interval);
-                    }
-
-                    // don't allow if any interfaces are blacklisted
-                    if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
-                        return;
-                    }
-                    interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
-                            interfaceSubclass, interfaceProtocol, endpoints);
-                }
-            } catch (Exception e) {
-                // beware of index out of bound exceptions, which might happen if
-                // a device does not set bNumEndpoints correctly
-                Log.e(TAG, "error parsing USB descriptors", e);
-                return;
-            }
-
-            UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
-                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
-            mDevices.put(deviceName, device);
-            mDeviceManager.deviceAttached(device);
-        }
-    }
-
-    /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
-    private void usbDeviceRemoved(String deviceName) {
-        synchronized (mLock) {
-            UsbDevice device = mDevices.remove(deviceName);
-            if (device != null) {
-                mDeviceManager.deviceDetached(device);
-            }
-        }
-    }
-
-    private void initHostSupport() {
-        // Create a thread to call into native code to wait for USB host events.
-        // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
-        Runnable runnable = new Runnable() {
-            public void run() {
-                monitorUsbHostBus();
-            }
-        };
-        new Thread(null, runnable, "UsbService host thread").start();
     }
 
     public void systemReady() {
-        synchronized (mLock) {
-            if (mHasUsbHost) {
-                // start monitoring for connected USB devices
-                initHostSupport();
-            }
-
-            update(false);
-            if (mCurrentAccessory != null) {
-                Log.d(TAG, "accessoryAttached at systemReady");
-                // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
-                // to handle this later.
-                mContext.registerReceiver(mBootCompletedReceiver,
-                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
-            }
-            mSystemReady = true;
+        if (mDeviceManager != null) {
+            mDeviceManager.systemReady();
         }
-    }
-
-    /*
-     * Sends a message to update the USB connected and configured state (device mode).
-     * If delayed is true, then we add a small delay in sending the message to debounce
-     * the USB connection when enabling USB tethering.
-     */
-    private final void update(boolean delayed) {
-        mHandler.removeMessages(MSG_UPDATE_STATE);
-        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
+        if (mHostManager != null) {
+            mHostManager.systemReady();
+        }
     }
 
     /* Returns a list of all currently attached USB devices (host mdoe) */
     public void getDeviceList(Bundle devices) {
-        synchronized (mLock) {
-            for (String name : mDevices.keySet()) {
-                devices.putParcelable(name, mDevices.get(name));
-            }
+        if (mHostManager != null) {
+            mHostManager.getDeviceList(devices);
         }
     }
 
     /* Opens the specified USB device (host mode) */
     public ParcelFileDescriptor openDevice(String deviceName) {
-        synchronized (mLock) {
-            if (isBlackListed(deviceName)) {
-                throw new SecurityException("USB device is on a restricted bus");
-            }
-            UsbDevice device = mDevices.get(deviceName);
-            if (device == null) {
-                // if it is not in mDevices, it either does not exist or is blacklisted
-                throw new IllegalArgumentException(
-                        "device " + deviceName + " does not exist or is restricted");
-            }
-            mDeviceManager.checkPermission(device);
-            return nativeOpenDevice(deviceName);
+        if (mHostManager != null) {
+            return mHostManager.openDevice(deviceName);
+        } else {
+            return null;
         }
     }
 
     /* returns the currently attached USB accessory (device mode) */
     public UsbAccessory getCurrentAccessory() {
-        return mCurrentAccessory;
+        if (mDeviceManager != null) {
+            return mDeviceManager.getCurrentAccessory();
+        } else {
+            return null;
+        }
     }
 
     /* opens the currently attached USB accessory (device mode) */
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
-        synchronized (mLock) {
-            if (mCurrentAccessory == null) {
-                throw new IllegalArgumentException("no accessory attached");
-            }
-            if (!mCurrentAccessory.equals(accessory)) {
-                Log.e(TAG, accessory.toString() + " does not match current accessory "
-                        + mCurrentAccessory);
-                throw new IllegalArgumentException("accessory not attached");
-            }
-            mDeviceManager.checkPermission(mCurrentAccessory);
-            return nativeOpenAccessory();
+        if (mDeviceManager != null) {
+            return openAccessory(accessory);
+        } else {
+            return null;
         }
     }
 
     public void setDevicePackage(UsbDevice device, String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.setDevicePackage(device, packageName);
+        mSettingsManager.setDevicePackage(device, packageName);
     }
 
     public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.setAccessoryPackage(accessory, packageName);
+        mSettingsManager.setAccessoryPackage(accessory, packageName);
     }
 
     public boolean hasDevicePermission(UsbDevice device) {
-        return mDeviceManager.hasPermission(device);
+        return mSettingsManager.hasPermission(device);
     }
 
     public boolean hasAccessoryPermission(UsbAccessory accessory) {
-        return mDeviceManager.hasPermission(accessory);
+        return mSettingsManager.hasPermission(accessory);
     }
 
     public void requestDevicePermission(UsbDevice device, String packageName,
             PendingIntent pi) {
-        mDeviceManager.requestPermission(device, packageName, pi);
+        mSettingsManager.requestPermission(device, packageName, pi);
     }
 
     public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
             PendingIntent pi) {
-        mDeviceManager.requestPermission(accessory, packageName, pi);
+        mSettingsManager.requestPermission(accessory, packageName, pi);
     }
 
     public void grantDevicePermission(UsbDevice device, int uid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.grantDevicePermission(device, uid);
+        mSettingsManager.grantDevicePermission(device, uid);
     }
 
     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.grantAccessoryPermission(accessory, uid);
+        mSettingsManager.grantAccessoryPermission(accessory, uid);
     }
 
     public boolean hasDefaults(String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        return mDeviceManager.hasDefaults(packageName);
+        return mSettingsManager.hasDefaults(packageName);
     }
 
     public void clearDefaults(String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.clearDefaults(packageName);
+        mSettingsManager.clearDefaults(packageName);
     }
 
-    /*
-     * This handler is for deferred handling of events related to device mode and accessories.
-     */
-    private final Handler mHandler = new Handler() {
-        private void addEnabledFunctionsLocked(Intent intent) {
-            // include state of all USB functions in our extras
-            for (int i = 0; i < mEnabledFunctions.size(); i++) {
-                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
-            }
-            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
-            }
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            synchronized (mLock) {
-                switch (msg.what) {
-                    case MSG_UPDATE_STATE:
-                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
-                            if (mConnected == 0) {
-                                if (UsbManager.isFunctionEnabled(
-                                            UsbManager.USB_FUNCTION_ACCESSORY)) {
-                                    // make sure accessory mode is off, and restore default functions
-                                    Log.d(TAG, "exited USB accessory mode");
-                                    if (!UsbManager.setFunctionEnabled
-                                            (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
-                                        Log.e(TAG, "could not disable accessory function");
-                                    }
-                                    int count = mDefaultFunctions.size();
-                                    for (int i = 0; i < count; i++) {
-                                        String function = mDefaultFunctions.get(i);
-                                        if (!UsbManager.setFunctionEnabled(function, true)) {
-                                            Log.e(TAG, "could not reenable function " + function);
-                                        }
-                                    }
-
-                                    if (mCurrentAccessory != null) {
-                                        mDeviceManager.accessoryDetached(mCurrentAccessory);
-                                        mCurrentAccessory = null;
-                                    }
-                                }
-                            }
-
-                            final ContentResolver cr = mContext.getContentResolver();
-                            if (Settings.Secure.getInt(cr,
-                                    Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
-                                Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
-                                return;
-                            }
-
-                            mLastConnected = mConnected;
-                            mLastConfiguration = mConfiguration;
-
-                            // send a sticky broadcast containing current USB state
-                            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
-                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
-                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
-                            addEnabledFunctionsLocked(intent);
-                            mContext.sendStickyBroadcast(intent);
-                        }
-                        break;
-                    case MSG_FUNCTION_ENABLED:
-                    case MSG_FUNCTION_DISABLED:
-                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
-                        break;
-                }
-            }
-        }
-    };
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -622,40 +156,14 @@
             return;
         }
 
-        synchronized (mLock) {
-            pw.println("USB Manager State:");
+        pw.println("USB Manager State:");
 
-            pw.println("  USB Device State:");
-            pw.print("    Enabled Functions: ");
-            for (int i = 0; i < mEnabledFunctions.size(); i++) {
-                pw.print(mEnabledFunctions.get(i) + " ");
-            }
-            pw.println("");
-            pw.print("    Disabled Functions: ");
-            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                pw.print(mDisabledFunctions.get(i) + " ");
-            }
-            pw.println("");
-            pw.print("    Default Functions: ");
-            for (int i = 0; i < mDefaultFunctions.size(); i++) {
-                pw.print(mDefaultFunctions.get(i) + " ");
-            }
-            pw.println("");
-            pw.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
-            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
-
-            pw.println("  USB Host State:");
-            for (String name : mDevices.keySet()) {
-                pw.println("    " + name + ": " + mDevices.get(name));
-            }
+        if (mDeviceManager != null) {
             mDeviceManager.dump(fd, pw);
         }
+        if (mHostManager != null) {
+            mHostManager.dump(fd, pw);
+        }
+        mSettingsManager.dump(fd, pw);
     }
-
-    // host support
-    private native void monitorUsbHostBus();
-    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
-    // accessory support
-    private native String[] nativeGetAccessoryStrings();
-    private native ParcelFileDescriptor nativeOpenAccessory();
 }
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
similarity index 99%
rename from services/java/com/android/server/usb/UsbDeviceSettingsManager.java
rename to services/java/com/android/server/usb/UsbSettingsManager.java
index de0b114..9113677 100644
--- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -59,9 +59,9 @@
 import java.util.HashMap;
 import java.util.List;
 
-class UsbDeviceSettingsManager {
+class UsbSettingsManager {
 
-    private static final String TAG = "UsbDeviceSettingsManager";
+    private static final String TAG = "UsbSettingsManager";
     private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
 
     private final Context mContext;
@@ -363,7 +363,7 @@
     }
     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
-    public UsbDeviceSettingsManager(Context context) {
+    public UsbSettingsManager(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
         synchronized (mLock) {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 4e93fe2..a1c3283 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -12,7 +12,8 @@
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
     com_android_server_SystemServer.cpp \
-    com_android_server_UsbService.cpp \
+    com_android_server_UsbDeviceManager.cpp \
+    com_android_server_UsbHostManager.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     onload.cpp
diff --git a/services/jni/com_android_server_UsbDeviceManager.cpp b/services/jni/com_android_server_UsbDeviceManager.cpp
new file mode 100644
index 0000000..6954171
--- /dev/null
+++ b/services/jni/com_android_server_UsbDeviceManager.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbDeviceManagerJNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <stdio.h>
+#include <asm/byteorder.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/usb/f_accessory.h>
+
+#define DRIVER_NAME "/dev/usb_accessory"
+
+namespace android
+{
+
+static struct parcel_file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index)
+{
+    char buffer[256];
+
+    buffer[0] = 0;
+    int length = ioctl(fd, cmd, buffer);
+    if (buffer[0]) {
+        jstring obj = env->NewStringUTF(buffer);
+        env->SetObjectArrayElement(strArray, index, obj);
+        env->DeleteLocalRef(obj);
+    }
+}
+
+
+static jobjectArray android_server_UsbDeviceManager_getAccessoryStrings(JNIEnv *env, jobject thiz)
+{
+    int fd = open(DRIVER_NAME, O_RDWR);
+    if (fd < 0) {
+        LOGE("could not open %s", DRIVER_NAME);
+        return NULL;
+    }
+    jclass stringClass = env->FindClass("java/lang/String");
+    jobjectArray strArray = env->NewObjectArray(6, stringClass, NULL);
+    if (!strArray) goto out;
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_DESCRIPTION, strArray, 2);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_URI, strArray, 4);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_SERIAL, strArray, 5);
+
+out:
+    close(fd);
+    return strArray;
+}
+
+static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobject thiz)
+{
+    int fd = open(DRIVER_NAME, O_RDWR);
+    if (fd < 0) {
+        LOGE("could not open %s", DRIVER_NAME);
+        return NULL;
+    }
+    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
+    if (fileDescriptor == NULL) {
+        return NULL;
+    }
+    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+}
+
+static JNINativeMethod method_table[] = {
+    { "nativeGetAccessoryStrings",  "()[Ljava/lang/String;",
+                                    (void*)android_server_UsbDeviceManager_getAccessoryStrings },
+    { "nativeOpenAccessory",        "()Landroid/os/ParcelFileDescriptor;",
+                                    (void*)android_server_UsbDeviceManager_openAccessory },
+};
+
+int register_android_server_UsbDeviceManager(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("com/android/server/usb/UsbDeviceManager");
+    if (clazz == NULL) {
+        LOGE("Can't find com/android/server/usb/UsbDeviceManager");
+        return -1;
+    }
+
+    clazz = env->FindClass("android/os/ParcelFileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
+    LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
+                 "Unable to find constructor for android.os.ParcelFileDescriptor");
+
+    return jniRegisterNativeMethods(env, "com/android/server/usb/UsbDeviceManager",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbHostManager.cpp
similarity index 71%
rename from services/jni/com_android_server_UsbService.cpp
rename to services/jni/com_android_server_UsbHostManager.cpp
index 9cd04f6..9506d75 100644
--- a/services/jni/com_android_server_UsbService.cpp
+++ b/services/jni/com_android_server_UsbHostManager.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "UsbService"
+#define LOG_TAG "UsbHostManagerJNI"
 #include "utils/Log.h"
 
 #include "jni.h"
@@ -30,9 +30,6 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include <linux/usb/f_accessory.h>
-
-#define DRIVER_NAME "/dev/usb_accessory"
 
 namespace android
 {
@@ -134,7 +131,7 @@
     return 0;
 }
 
-static void android_server_UsbService_monitorUsbHostBus(JNIEnv *env, jobject thiz)
+static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *env, jobject thiz)
 {
     struct usb_host_context* context = usb_host_init();
     if (!context) {
@@ -145,7 +142,7 @@
     usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
 }
 
-static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, jstring deviceName)
+static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject thiz, jstring deviceName)
 {
     const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
     struct usb_device* device = usb_device_open(deviceNameStr);
@@ -168,72 +165,17 @@
         gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
 }
 
-static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index)
-{
-    char buffer[256];
-
-    buffer[0] = 0;
-    int length = ioctl(fd, cmd, buffer);
-    if (buffer[0]) {
-        jstring obj = env->NewStringUTF(buffer);
-        env->SetObjectArrayElement(strArray, index, obj);
-        env->DeleteLocalRef(obj);
-    }
-}
-
-
-static jobjectArray android_server_UsbService_getAccessoryStrings(JNIEnv *env, jobject thiz)
-{
-    int fd = open(DRIVER_NAME, O_RDWR);
-    if (fd < 0) {
-        LOGE("could not open %s", DRIVER_NAME);
-        return NULL;
-    }
-    jclass stringClass = env->FindClass("java/lang/String");
-    jobjectArray strArray = env->NewObjectArray(6, stringClass, NULL);
-    if (!strArray) goto out;
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0);
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1);
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_DESCRIPTION, strArray, 2);
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_URI, strArray, 4);
-    set_accessory_string(env, fd, ACCESSORY_GET_STRING_SERIAL, strArray, 5);
-
-out:
-    close(fd);
-    return strArray;
-}
-
-static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz)
-{
-    int fd = open(DRIVER_NAME, O_RDWR);
-    if (fd < 0) {
-        LOGE("could not open %s", DRIVER_NAME);
-        return NULL;
-    }
-    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
-    if (fileDescriptor == NULL) {
-        return NULL;
-    }
-    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
-        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
-}
-
 static JNINativeMethod method_table[] = {
-    { "monitorUsbHostBus", "()V", (void*)android_server_UsbService_monitorUsbHostBus },
+    { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
     { "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
-                                  (void*)android_server_UsbService_openDevice },
-    { "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
-                                  (void*)android_server_UsbService_getAccessoryStrings },
-    { "nativeOpenAccessory","()Landroid/os/ParcelFileDescriptor;",
-                                  (void*)android_server_UsbService_openAccessory },
+                                  (void*)android_server_UsbHostManager_openDevice },
 };
 
-int register_android_server_UsbService(JNIEnv *env)
+int register_android_server_UsbHostManager(JNIEnv *env)
 {
-    jclass clazz = env->FindClass("com/android/server/usb/UsbService");
+    jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager");
     if (clazz == NULL) {
-        LOGE("Can't find com/android/server/usb/UsbService");
+        LOGE("Can't find com/android/server/usb/UsbHostManager");
         return -1;
     }
     method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
@@ -254,7 +196,7 @@
     LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
                  "Unable to find constructor for android.os.ParcelFileDescriptor");
 
-    return jniRegisterNativeMethods(env, "com/android/server/usb/UsbService",
+    return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager",
             method_table, NELEM(method_table));
 }
 
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 0c46eee..469e818 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -29,7 +29,8 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
-int register_android_server_UsbService(JNIEnv* env);
+int register_android_server_UsbDeviceManager(JNIEnv* env);
+int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -57,7 +58,8 @@
     register_android_server_LightsService(env);
     register_android_server_AlarmManagerService(env);
     register_android_server_BatteryService(env);
-    register_android_server_UsbService(env);
+    register_android_server_UsbDeviceManager(env);
+    register_android_server_UsbHostManager(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);