USB: Support for new USB gadget drivers

Change-Id: Id08df50acb873a94f4765a991ee6a6f5b898ddf5
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2769004..a23bacf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -406,8 +406,8 @@
             }
 
             try {
-                Slog.i(TAG, "USB Observer");
-                // Listen for USB changes
+                Slog.i(TAG, "USB Service");
+                // Manage USB host and device support
                 usb = new UsbService(context);
                 ServiceManager.addService(Context.USB_SERVICE, usb);
             } catch (Throwable e) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index ca8a184..e03f9a0 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,7 +19,6 @@
 import android.app.PendingIntent;
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,10 +32,16 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.FileUtils;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Parcelable;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.provider.Settings;
@@ -46,7 +51,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -55,57 +60,43 @@
  * 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 String USB_STATE_MATCH =
+            "DEVPATH=/devices/virtual/android_usb/android0";
+    private static final String ACCESSORY_START_MATCH =
+            "DEVPATH=/devices/virtual/misc/usb_accessory";
+    private static final String FUNCTIONS_PATH =
+            "/sys/class/android_usb/android0/functions";
+    private static final String STATE_PATH =
+            "/sys/class/android_usb/android0/state";
+    private static final String MASS_STORAGE_FILE_PATH =
+            "/sys/class/android_usb/f_mass_storage/lun/file";
 
     private static final int MSG_UPDATE_STATE = 0;
-    private static final int MSG_FUNCTION_ENABLED = 1;
-    private static final int MSG_FUNCTION_DISABLED = 2;
+    private static final int MSG_ENABLE_ADB = 1;
+    private static final int MSG_SET_PRIMARY_FUNCTION = 2;
+    private static final int MSG_SET_DEFAULT_FUNCTION = 3;
+    private static final int MSG_SYSTEM_READY = 4;
 
     // 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 UsbHandler mHandler;
     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;
-    ContentResolver mContentResolver;
-    private final Object mLock = new Object();
+    private final ContentResolver mContentResolver;
     private final UsbSettingsManager mSettingsManager;
     private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
 
     // for adb connected notifications
-    private boolean mAdbNotificationShown = false;
+    private boolean mAdbNotificationShown;
     private Notification mAdbNotification;
     private boolean mAdbEnabled;
 
@@ -115,17 +106,14 @@
         }
         @Override
         public void onChange(boolean selfChange) {
-            mAdbEnabled = (Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.ADB_ENABLED, 0) > 0);
-            // setting this secure property will start or stop adbd
-           SystemProperties.set("persist.service.adb.enable", mAdbEnabled ? "1" : "0");
-           updateAdbNotification();
+            boolean enable = (Settings.Secure.getInt(mContentResolver,
+                    Settings.Secure.ADB_ENABLED, 0) > 0);
+            mHandler.sendMessage(MSG_ENABLE_ADB, enable);
         }
     }
 
-    private void updateAdbNotification() {
+    private void updateAdbNotification(boolean adbEnabled) {
         if (mNotificationManager == null) return;
-        boolean adbEnabled = mAdbEnabled && (mConnected == 1);
         if (adbEnabled) {
             if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
 
@@ -173,38 +161,6 @@
         }
     }
 
-    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);
-            }
-
-            if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
-                readCurrentAccessoryLocked();
-            }
-        } else {
-            mEnabledFunctions.remove(function);
-        }
-    }
-
     /*
      * Listens for uevent messages from the kernel to monitor the USB state
      */
@@ -215,53 +171,13 @@
                 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);
-                }
+            String state = event.get("USB_STATE");
+            String accessory = event.get("ACCESSORY");
+            if (state != null) {
+                mHandler.updateState(state);
+            } else if ("START".equals(accessory)) {
+                Slog.d(TAG, "got accessory start");
+                setPrimaryFunction(UsbManager.USB_FUNCTION_ACCESSORY);
             }
         }
     };
@@ -273,229 +189,365 @@
         PackageManager pm = mContext.getPackageManager();
         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
 
-        synchronized (mLock) {
-            init();  // set initial status
-
-            // make sure the ADB_ENABLED setting value matches the secure property value
-            mAdbEnabled = "1".equals(SystemProperties.get("persist.service.adb.enable"));
-            Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED,
-                    mAdbEnabled ? 1 : 0);
-
-            // register observer to listen for settings changes
-            mContentResolver.registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
-                            false, new AdbSettingsObserver());
-
-            // Watch for USB configuration changes
-            if (mConfiguration >= 0) {
-                mUEventObserver.startObserving(USB_CONNECTED_MATCH);
-                mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
-                mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+        // create a thread for our Handler
+        HandlerThread thread = new HandlerThread("UsbDeviceManager",
+                Process.THREAD_PRIORITY_BACKGROUND) {
+            protected void onLooperPrepared() {
+                mHandler = new UsbHandler();
             }
-        }
-    }
-
-    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);
-                    }
-                }
-            }
-        } 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 (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
-                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
-            } else if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
-                mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
-            }
-        }
+        };
+        thread.start();
     }
 
     public void systemReady() {
-        synchronized (mLock) {
-                mNotificationManager = (NotificationManager)
-                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mSystemReady = true;
 
-            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));
+        mNotificationManager = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        // make sure the ADB_ENABLED setting value matches the current state
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+
+        mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
+    }
+
+     private static String addFunction(String functions, String function) {
+        if (!containsFunction(functions, function)) {
+            if (functions.length() > 0) {
+                functions += ",";
             }
-            mSystemReady = true;
+            functions += function;
         }
+        return functions;
     }
 
-    /*
-     * 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");
+    private static String removeFunction(String functions, String function) {
+        String[] split = functions.split(",");
+        for (int i = 0; i < split.length; i++) {
+            if (function.equals(split[i])) {
+                split[i] = null;
             }
-            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();
         }
+        StringBuilder builder = new StringBuilder();
+         for (int i = 0; i < split.length; i++) {
+            String s = split[i];
+            if (s != null) {
+                if (builder.length() > 0) {
+                    builder.append(",");
+                }
+                builder.append(s);
+            }
+        }
+        return builder.toString();
     }
 
-    /*
-     * This handler is for deferred handling of events related to device mode and accessories.
-     */
-    private final Handler mHandler = new Handler() {
+    private static boolean containsFunction(String functions, String function) {
+        int index = functions.indexOf(function);
+        if (index < 0) return false;
+        if (index > 0 && functions.charAt(index - 1) != ',') return false;
+        int charAfter = index + function.length();
+        if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+        return true;
+    }
 
-        @Override
-        public void handleMessage(Message msg) {
-            synchronized (mLock) {
-                switch (msg.what) {
-                    case MSG_UPDATE_STATE:
-                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
-                            updateAdbNotification();
-                            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);
-                                        }
-                                    }
+    private final class UsbHandler extends Handler {
 
-                                    if (mCurrentAccessory != null) {
-                                        mSettingsManager.accessoryDetached(mCurrentAccessory);
-                                        mCurrentAccessory = null;
-                                    }
-                                }
-                            }
+        // current USB state
+        private boolean mConnected;
+        private boolean mConfigured;
+        private String mCurrentFunctions;
+        private String mDefaultFunctions;
+        private UsbAccessory mCurrentAccessory;
+        private boolean mDeferAccessoryAttached;
 
-                            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;
-                            }
+        public UsbHandler() {
+            // Read initial USB state
+            try {
+                mCurrentFunctions = FileUtils.readTextFile(
+                        new File(FUNCTIONS_PATH), 0, null).trim();
+                mDefaultFunctions = mCurrentFunctions;
+                String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+                updateState(state);
 
-                            mLastConnected = mConnected;
-                            mLastConfiguration = mConfiguration;
+                mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
 
-                            // 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);
-                            for (int i = 0; i < mEnabledFunctions.size(); i++) {
-                                intent.putExtra(mEnabledFunctions.get(i), true);
-                            }
-                            mContext.sendStickyBroadcast(intent);
-                        }
-                        break;
-                    case MSG_FUNCTION_ENABLED:
-                    case MSG_FUNCTION_DISABLED:
-                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
-                        break;
+                // Upgrade step for previous versions that used persist.service.adb.enable
+                String value = SystemProperties.get("persist.service.adb.enable", "");
+                if (value.length() > 0) {
+                    char enable = value.charAt(0);
+                    if (enable == '1') {
+                        setAdbEnabled(true);
+                    } else if (enable == '0') {
+                        setAdbEnabled(false);
+                    }
+                    SystemProperties.set("persist.service.adb.enable", "");
+                }
+
+                // register observer to listen for settings changes
+                mContentResolver.registerContentObserver(
+                        Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
+                                false, new AdbSettingsObserver());
+
+                // Watch for USB configuration changes
+                mUEventObserver.startObserving(USB_STATE_MATCH);
+                mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+            } catch (Exception e) {
+                Slog.e(TAG, "Error initializing UsbHandler", e);
+            }
+        }
+
+        public void sendMessage(int what, boolean arg) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.arg1 = (arg ? 1 : 0);
+            sendMessage(m);
+        }
+
+        public void sendMessage(int what, Object arg) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.obj = arg;
+            sendMessage(m);
+        }
+
+        public void updateState(String state) {
+            int connected, configured;
+
+            if ("DISCONNECTED".equals(state)) {
+                connected = 0;
+                configured = 0;
+            } else if ("CONNECTED".equals(state)) {
+                connected = 1;
+                configured = 0;
+            } else if ("CONFIGURED".equals(state)) {
+                connected = 1;
+                configured = 1;
+            } else {
+                Slog.e(TAG, "unknown state " + state);
+                return;
+            }
+            removeMessages(MSG_UPDATE_STATE);
+            Message msg = Message.obtain(this, MSG_UPDATE_STATE);
+            msg.arg1 = connected;
+            msg.arg2 = configured;
+            // debounce disconnects to avoid problems bringing up USB tethering
+            sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
+        }
+
+        private boolean setUsbConfig(String config) {
+            // set the new configuration
+            SystemProperties.set("sys.usb.config", config);
+            // wait for the transition to complete.
+            // give up after 1 second.
+            for (int i = 0; i < 20; i++) {
+                // State transition is done when sys.usb.conf.done is set to the new configuration
+                if (config.equals(SystemProperties.get("sys.usb.state"))) return true;
+                try {
+                    // try again in 50ms
+                    Thread.sleep(50);
+                } catch (InterruptedException e) {
+                }
+            }
+            return false;
+        }
+
+        private void setCurrentFunctions(String functions) {
+            if (!mCurrentFunctions.equals(functions)) {
+                if (!setUsbConfig("none") || !setUsbConfig(functions)) {
+                    Log.e(TAG, "Failed to switch USB configuration to " + functions);
+                    // revert to previous configuration if we fail
+                    setUsbConfig(mCurrentFunctions);
+                } else {
+                    mCurrentFunctions = functions;
                 }
             }
         }
-    };
+
+        private void setAdbEnabled(boolean enable) {
+            if (enable != mAdbEnabled) {
+                mAdbEnabled = enable;
+                String functions;
+                if (enable) {
+                    functions = addFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+                    mDefaultFunctions = addFunction(mDefaultFunctions,
+                            UsbManager.USB_FUNCTION_ADB);
+                } else {
+                    functions = removeFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+                    mDefaultFunctions = removeFunction(mDefaultFunctions,
+                            UsbManager.USB_FUNCTION_ADB);
+                }
+                SystemProperties.set("persist.sys.usb.config", mDefaultFunctions);
+                setCurrentFunctions(functions);
+                updateAdbNotification(mAdbEnabled && mConnected);
+            }
+        }
+
+        private void setEnabledFunctions(String functionList) {
+            if (mAdbEnabled) {
+                functionList = addFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+            } else {
+                functionList = removeFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+            }
+            setCurrentFunctions(functionList);
+        }
+
+        private void updateCurrentAccessory() {
+            if (!mHasUsbAccessory) return;
+
+            if (mConfigured) {
+                String[] strings = nativeGetAccessoryStrings();
+                if (strings != null) {
+                    mCurrentAccessory = new UsbAccessory(strings);
+                    Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+                    // defer accessoryAttached if system is not ready
+                    if (mSystemReady) {
+                        mSettingsManager.accessoryAttached(mCurrentAccessory);
+                    } else {
+                        mDeferAccessoryAttached = true;
+                    }
+                } else {
+                    Log.e(TAG, "nativeGetAccessoryStrings failed");
+                }
+            } else if (!mConnected) {
+                // make sure accessory mode is off
+                // and restore default functions
+                Log.d(TAG, "exited USB accessory mode");
+                setEnabledFunctions(mDefaultFunctions);
+
+                if (mCurrentAccessory != null) {
+                    if (mSystemReady) {
+                        mSettingsManager.accessoryDetached(mCurrentAccessory);
+                    }
+                    mCurrentAccessory = null;
+                }
+            }
+        }
+
+        private void updateUsbState() {
+            // 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);
+            intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
+
+            if (mCurrentFunctions != null) {
+                String[] functions = mCurrentFunctions.split(",");
+                for (int i = 0; i < functions.length; i++) {
+                    intent.putExtra(functions[i], true);
+                }
+            }
+
+            mContext.sendStickyBroadcast(intent);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            String function;
+
+            switch (msg.what) {
+                case MSG_UPDATE_STATE:
+                    mConnected = (msg.arg1 == 1);
+                    mConfigured = (msg.arg2 == 1);
+                    updateAdbNotification(mAdbEnabled && mConnected);
+                    if (containsFunction(mCurrentFunctions,
+                            UsbManager.USB_FUNCTION_ACCESSORY)) {
+                        updateCurrentAccessory();
+                    }
+
+                    if (!mConnected) {
+                        // restore defaults when USB is disconnected
+                        setCurrentFunctions(mDefaultFunctions);
+                    }
+                    if (mSystemReady) {
+                        updateUsbState();
+                    }
+                    break;
+                case MSG_ENABLE_ADB:
+                    setAdbEnabled(msg.arg1 == 1);
+                    break;
+                case MSG_SET_PRIMARY_FUNCTION:
+                    function = (String)msg.obj;
+                    if (function == null) {
+                        function = mDefaultFunctions;
+                    }
+                    setEnabledFunctions(function);
+                    break;
+                case MSG_SET_DEFAULT_FUNCTION:
+                    function = (String)msg.obj;
+                    if (mAdbEnabled) {
+                        function = addFunction(function, UsbManager.USB_FUNCTION_ADB);
+                    }
+                    SystemProperties.set("persist.sys.usb.config", function);
+                    mDefaultFunctions = function;
+                    break;
+                case MSG_SYSTEM_READY:
+                    updateAdbNotification(mAdbEnabled && mConnected);
+                    updateUsbState();
+                    if (mCurrentAccessory != null && mDeferAccessoryAttached) {
+                        mSettingsManager.accessoryAttached(mCurrentAccessory);
+                    }
+                    break;
+            }
+        }
+
+        public UsbAccessory getCurrentAccessory() {
+            return mCurrentAccessory;
+        }
+
+        public void dump(FileDescriptor fd, PrintWriter pw) {
+            pw.println("  USB Device State:");
+            pw.println("    Current Functions: " + mCurrentFunctions);
+            pw.println("    Default Functions: " + mDefaultFunctions);
+            pw.println("    mConnected: " + mConnected);
+            pw.println("    mConfigured: " + mConfigured);
+            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
+        }
+    }
+
+    /* returns the currently attached USB accessory */
+    public UsbAccessory getCurrentAccessory() {
+        return mHandler.getCurrentAccessory();
+    }
+
+    /* opens the currently attached USB accessory */
+        public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+            UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
+            if (currentAccessory == null) {
+                throw new IllegalArgumentException("no accessory attached");
+            }
+            if (!currentAccessory.equals(accessory)) {
+                String error = accessory.toString()
+                        + " does not match current accessory "
+                        + currentAccessory;
+                throw new IllegalArgumentException(error);
+            }
+            mSettingsManager.checkPermission(accessory);
+            return nativeOpenAccessory();
+        }
+
+    public void setPrimaryFunction(String function) {
+        mHandler.sendMessage(MSG_SET_PRIMARY_FUNCTION, function);
+    }
+
+    public void setDefaultFunction(String function) {
+        if (function == null) {
+            throw new NullPointerException();
+        }
+        mHandler.sendMessage(MSG_SET_DEFAULT_FUNCTION, function);
+    }
+
+    public void setMassStorageBackingFile(String path) {
+        if (path == null) path = "";
+        try {
+            FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path);
+        } catch (IOException e) {
+           Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH);
+        }
+    }
 
     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("    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);
+        if (mHandler != null) {
+            mHandler.dump(fd, pw);
         }
     }
 
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 21e5997c..193638f 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -50,7 +50,7 @@
         if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
             mHostManager = new UsbHostManager(context, mSettingsManager);
         }
-        if (new File("/sys/class/usb_composite").exists()) {
+        if (new File("/sys/class/android_usb").exists()) {
             mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
         }
     }
@@ -92,7 +92,7 @@
     /* opens the currently attached USB accessory (device mode) */
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
         if (mDeviceManager != null) {
-            return openAccessory(accessory);
+            return mDeviceManager.openAccessory(accessory);
         } else {
             return null;
         }
@@ -146,6 +146,33 @@
         mSettingsManager.clearDefaults(packageName);
     }
 
+    public void setPrimaryFunction(String function) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        if (mDeviceManager != null) {
+            mDeviceManager.setPrimaryFunction(function);
+        } else {
+            throw new IllegalStateException("USB device mode not supported");
+        }
+    }
+
+    public void setDefaultFunction(String function) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        if (mDeviceManager != null) {
+            mDeviceManager.setDefaultFunction(function);
+        } else {
+            throw new IllegalStateException("USB device mode not supported");
+        }
+    }
+
+    public void setMassStorageBackingFile(String path) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        if (mDeviceManager != null) {
+            mDeviceManager.setMassStorageBackingFile(path);
+        } else {
+            throw new IllegalStateException("USB device mode not supported");
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)