DO NOT MERGE: Backport USB accessory support to gingerbread

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/Android.mk b/Android.mk
index b817cc6..009c80c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -113,6 +113,7 @@
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/database/IContentObserver.aidl \
+	core/java/android/hardware/usb/IUsbManager.aidl \
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/IThrottleManager.aidl \
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 50ec34f..fb9335b7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -62,6 +62,8 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.hardware.SensorManager;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbManager;
 import android.location.ILocationManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
@@ -191,6 +193,7 @@
     private SearchManager mSearchManager = null;
     private SensorManager mSensorManager = null;
     private StorageManager mStorageManager = null;
+    private UsbManager mUsbManager = null;
     private Vibrator mVibrator = null;
     private LayoutInflater mLayoutInflater = null;
     private StatusBarManager mStatusBarManager = null;
@@ -954,6 +957,8 @@
             return getSensorManager();
         } else if (STORAGE_SERVICE.equals(name)) {
             return getStorageManager();
+        } else if (USB_SERVICE.equals(name)) {
+            return getUsbManager();
         } else if (VIBRATOR_SERVICE.equals(name)) {
             return getVibrator();
         } else if (STATUS_BAR_SERVICE.equals(name)) {
@@ -1148,6 +1153,17 @@
         return mStorageManager;
     }
 
+    private UsbManager getUsbManager() {
+        synchronized (mSync) {
+            if (mUsbManager == null) {
+                IBinder b = ServiceManager.getService(USB_SERVICE);
+                IUsbManager service = IUsbManager.Stub.asInterface(b);
+                mUsbManager = new UsbManager(service);
+            }
+        }
+        return mUsbManager;
+    }
+
     private Vibrator getVibrator() {
         synchronized (mSync) {
             if (mVibrator == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 85c29b8..ec2d640 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1567,6 +1567,17 @@
     public static final String SIP_SERVICE = "sip";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.hardware.usb.UsbManager} for access to USB devices (as a USB host)
+     * and for controlling this device's behavior as a USB device.
+     *
+     * @see #getSystemService
+     * @see android.harware.usb.UsbManager
+     * @hide
+     */
+    public static final String USB_SERVICE = "usb";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 922f8cd..a779925 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -785,6 +785,13 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports connecting to USB accessories.
+     * @hide
+     */
+    public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The SIP API is enabled on the device.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java
deleted file mode 100644
index 18790d2..0000000
--- a/core/java/android/hardware/UsbManager.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.hardware;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-/**
- * Class for accessing USB state information.
- * @hide
- */
-public class UsbManager {
-   /**
-     * Broadcast Action:  A sticky broadcast for USB state change events.
-     *
-     * This is a sticky broadcast for clients that includes USB connected/disconnected state,
-     * the USB configuration that is currently set and a bundle containing name/value pairs
-     * with the names of the functions and a value of either {@link #USB_FUNCTION_ENABLED}
-     * or {@link #USB_FUNCTION_DISABLED}.
-     * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE},
-     * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}.
-     */
-    public static final String ACTION_USB_STATE =
-            "android.hardware.action.USB_STATE";
-
-    /**
-     * Boolean extra indicating whether USB is connected or disconnected.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
-     */
-    public static final String USB_CONNECTED = "connected";
-
-    /**
-     * Integer extra containing currently set USB configuration.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
-     */
-    public static final String USB_CONFIGURATION = "configuration";
-
-    /**
-     * Name of the USB mass storage USB function.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
-
-    /**
-     * Name of the adb USB function.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_ADB = "adb";
-
-    /**
-     * Name of the RNDIS ethernet USB function.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_RNDIS = "rndis";
-
-    /**
-     * Name of the MTP USB function.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_MTP = "mtp";
-
-    /**
-     * Value indicating that a USB function is enabled.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_ENABLED = "enabled";
-
-    /**
-     * Value indicating that a USB function is disabled.
-     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
-     */
-    public static final String USB_FUNCTION_DISABLED = "disabled";
-
-    private static File getFunctionEnableFile(String function) {
-        return new File("/sys/class/usb_composite/" + function + "/enable");
-    }
-
-    /**
-     * Returns true if the specified USB function is supported by the kernel.
-     * Note that a USB function maybe supported but disabled.
-     */
-    public static boolean isFunctionSupported(String function) {
-        return getFunctionEnableFile(function).exists();
-    }
-
-    /**
-     * Returns true if the specified USB function is currently enabled.
-     */
-    public static boolean isFunctionEnabled(String function) {
-        try {
-            FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
-            boolean enabled = (stream.read() == '1');
-            stream.close();
-            return enabled;
-        } catch (IOException e) {
-            return false;
-        }
-    }
-}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
new file mode 100644
index 0000000..9be7e77
--- /dev/null
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package android.hardware.usb;
+
+import android.hardware.usb.UsbAccessory;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+interface IUsbManager
+{
+    /* Returns the currently attached USB accessory */
+    UsbAccessory getCurrentAccessory();
+
+    /* Returns a file descriptor for communicating with the USB accessory.
+     * This file descriptor can be used with standard Java file operations.
+     */
+    ParcelFileDescriptor openAccessory(in UsbAccessory accessory);
+
+    /* Sets the default package for a USB accessory
+     * (or clears it if the package name is null)
+     */
+    void setAccessoryPackage(in UsbAccessory accessory, String packageName);
+
+    /* Grants permission for the given UID to access the accessory */
+    void grantAccessoryPermission(in UsbAccessory accessory, int uid);
+
+    /* Returns true if the USB manager has default preferences or permissions for the package */
+    boolean hasDefaults(String packageName, int uid);
+
+    /* Clears default preferences and permissions for the package */
+    oneway void clearDefaults(String packageName, int uid);
+}
diff --git a/core/java/android/hardware/usb/UsbAccessory.aidl b/core/java/android/hardware/usb/UsbAccessory.aidl
new file mode 100644
index 0000000..1c15f1c
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbAccessory.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+parcelable UsbAccessory;
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
new file mode 100644
index 0000000..a7e953d
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -0,0 +1,145 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class representing a USB accessory.
+ * @hide
+ */
+public class UsbAccessory implements Parcelable {
+
+    private static final String TAG = "UsbAccessory";
+
+    private final String mManufacturer;
+    private final String mModel;
+    private final String mType;
+    private final String mVersion;
+
+    /**
+     * UsbAccessory should only be instantiated by UsbService implementation
+     * @hide
+     */
+    public UsbAccessory(String manufacturer, String model, String type, String version) {
+        mManufacturer = manufacturer;
+        mModel = model;
+        mType = type;
+        mVersion = version;
+    }
+
+    /**
+     * UsbAccessory should only be instantiated by UsbService implementation
+     * @hide
+     */
+    public UsbAccessory(String[] strings) {
+        mManufacturer = strings[0];
+        mModel = strings[1];
+        mType = strings[2];
+        mVersion = strings[3];
+    }
+
+    /**
+     * Returns the manufacturer of the accessory.
+     *
+     * @return the accessory manufacturer
+     */
+    public String getManufacturer() {
+        return mManufacturer;
+    }
+
+    /**
+     * Returns the model name of the accessory.
+     *
+     * @return the accessory model
+     */
+    public String getModel() {
+        return mModel;
+    }
+
+    /**
+     * Returns the type of the accessory.
+     *
+     * @return the accessory type
+     */
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the version of the accessory.
+     *
+     * @return the accessory version
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    private static boolean compare(String s1, String s2) {
+        if (s1 == null) return (s2 == null);
+        return s1.equals(s2);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof UsbAccessory) {
+            UsbAccessory accessory = (UsbAccessory)obj;
+            return (compare(mManufacturer, accessory.getManufacturer()) &&
+                    compare(mModel, accessory.getModel()) &&
+                    compare(mType, accessory.getType()) &&
+                    compare(mVersion, accessory.getVersion()));
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "UsbAccessory[mManufacturer=" + mManufacturer +
+                            ", mModel=" + mModel +
+                            ", mType=" + mType +
+                            ", mVersion=" + mVersion + "]";
+    }
+
+    public static final Parcelable.Creator<UsbAccessory> CREATOR =
+        new Parcelable.Creator<UsbAccessory>() {
+        public UsbAccessory createFromParcel(Parcel in) {
+            String manufacturer = in.readString();
+            String model = in.readString();
+            String type = in.readString();
+            String version = in.readString();
+            return new UsbAccessory(manufacturer, model, type, version);
+        }
+
+        public UsbAccessory[] newArray(int size) {
+            return new UsbAccessory[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mManufacturer);
+        parcel.writeString(mModel);
+        parcel.writeString(mType);
+        parcel.writeString(mVersion);
+   }
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
new file mode 100644
index 0000000..a350274
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+
+
+package android.hardware.usb;
+
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * This class allows you to access the state of USB.
+ *
+ * <p>You can obtain an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ *
+ * {@samplecode
+ * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ * }
+ * @hide
+ */
+public class UsbManager {
+    private static final String TAG = "UsbManager";
+
+   /**
+     * Broadcast Action:  A sticky broadcast for USB state change events when in device mode.
+     *
+     * This is a sticky broadcast for clients that includes USB connected/disconnected state,
+     * <ul>
+     * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
+     * <li> {@link #USB_CONFIGURATION} a Bundle containing name/value pairs where the name
+     * is the name of a USB function and the value is either {@link #USB_FUNCTION_ENABLED}
+     * or {@link #USB_FUNCTION_DISABLED}.  The possible function names include
+     * {@link #USB_FUNCTION_MASS_STORAGE}, {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS},
+     * {@link #USB_FUNCTION_MTP} and {@link #USB_FUNCTION_ACCESSORY}.
+     * </ul>
+     */
+    public static final String ACTION_USB_STATE =
+            "android.hardware.usb.action.USB_STATE";
+
+   /**
+     * Broadcast Action:  A broadcast for USB accessory attached event.
+     *
+     * This intent is sent when a USB accessory is attached.
+     * <ul>
+     * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
+     * for the attached accessory
+     * </ul>
+     */
+    public static final String ACTION_USB_ACCESSORY_ATTACHED =
+            "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
+
+   /**
+     * Broadcast Action:  A broadcast for USB accessory detached event.
+     *
+     * This intent is sent when a USB accessory is detached.
+     * <ul>
+     * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
+     * for the attached accessory that was detached
+     * </ul>
+     */
+    public static final String ACTION_USB_ACCESSORY_DETACHED =
+            "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
+
+    /**
+     * Boolean extra indicating whether USB is connected or disconnected.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
+     */
+    public static final String USB_CONNECTED = "connected";
+
+    /**
+     * Integer extra containing currently set USB configuration.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
+     */
+    public static final String USB_CONFIGURATION = "configuration";
+
+    /**
+     * Name of the USB mass storage USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
+
+    /**
+     * Name of the adb USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_ADB = "adb";
+
+    /**
+     * Name of the RNDIS ethernet USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_RNDIS = "rndis";
+
+    /**
+     * Name of the MTP USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_MTP = "mtp";
+
+    /**
+     * Name of the Accessory USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_ACCESSORY = "accessory";
+
+    /**
+     * Value indicating that a USB function is enabled.
+     * Used in {@link #USB_CONFIGURATION} extras bundle for the
+     * {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_ENABLED = "enabled";
+
+    /**
+     * Value indicating that a USB function is disabled.
+     * Used in {@link #USB_CONFIGURATION} extras bundle for the
+     * {@link #ACTION_USB_STATE} broadcast
+     */
+    public static final String USB_FUNCTION_DISABLED = "disabled";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
+     * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
+     * containing the UsbAccessory object for the accessory.
+     */
+    public static final String EXTRA_ACCESSORY = "accessory";
+
+    private IUsbManager mService;
+
+    /**
+     * {@hide}
+     */
+    public UsbManager(IUsbManager service) {
+        mService = service;
+    }
+
+    /**
+     * Returns a list of currently attached USB accessories.
+     * (in the current implementation there can be at most one)
+     *
+     * @return list of USB accessories, or null if none are attached.
+     */
+    public UsbAccessory[] getAccessoryList() {
+        try {
+            UsbAccessory accessory = mService.getCurrentAccessory();
+            if (accessory == null) {
+                return null;
+            } else {
+                return new UsbAccessory[] { accessory };
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getAccessoryList" , e);
+            return null;
+        }
+    }
+
+    /**
+     * Opens a file descriptor for reading and writing data to the USB accessory.
+     *
+     * @param accessory the USB accessory to open
+     * @return file descriptor, or null if the accessor could not be opened.
+     */
+    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+        try {
+            return mService.openAccessory(accessory);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in openAccessory" , e);
+            return null;
+        }
+    }
+
+    private static File getFunctionEnableFile(String function) {
+        return new File("/sys/class/usb_composite/" + function + "/enable");
+    }
+
+    /**
+     * Returns true if the specified USB function is supported by the kernel.
+     * Note that a USB function maybe supported but disabled.
+     *
+     * @param function name of the USB function
+     * @return true if the USB function is supported.
+     */
+    public static boolean isFunctionSupported(String function) {
+        return getFunctionEnableFile(function).exists();
+    }
+
+    /**
+     * Returns true if the specified USB function is currently enabled.
+     *
+     * @param function name of the USB function
+     * @return true if the USB function is enabled.
+     */
+    public static boolean isFunctionEnabled(String function) {
+        try {
+            FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
+            boolean enabled = (stream.read() == '1');
+            stream.close();
+            return enabled;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Enables or disables a USB function.
+     *
+     * @hide
+     */
+    public static boolean setFunctionEnabled(String function, boolean enable) {
+        try {
+            FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function));
+            stream.write(enable ? '1' : '0');
+            stream.close();
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 1c8b330..4c13460 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -90,6 +90,17 @@
         delete(key);
     }
 
+    /**
+     * Removes the mapping at the specified index.
+     * @hide
+     */
+    public void removeAt(int index) {
+        if (mValues[index] != DELETED) {
+            mValues[index] = DELETED;
+            mGarbage = true;
+        }
+    }
+    
     private void gc() {
         // Log.e("SparseArray", "gc start with " + mSize);
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 841de06..fb2a72b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -68,7 +68,7 @@
 
     protected void onCreate(Bundle savedInstanceState, Intent intent,
             CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
-            boolean alwaysUseOption) {
+            boolean alwaysUseOption, boolean alwaysChoose) {
         super.onCreate(savedInstanceState);
         mPm = getPackageManager();
         intent.setComponent(null);
@@ -90,9 +90,10 @@
             mClearDefaultHint.setVisibility(View.GONE);
         }
         mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList);
-        if (mAdapter.getCount() > 1) {
+        int count = mAdapter.getCount();
+        if (count > 1 || (count == 1 && alwaysChoose)) {
             ap.mAdapter = mAdapter;
-        } else if (mAdapter.getCount() == 1) {
+        } else if (count == 1) {
             startActivity(mAdapter.intentForPosition(0));
             finish();
             return;
@@ -103,11 +104,22 @@
         setupAlert();
     }
 
+    protected void onCreate(Bundle savedInstanceState, Intent intent,
+            CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean alwaysUseOption) {
+        onCreate(savedInstanceState, intent, title, initialIntents, rList, alwaysUseOption, false);
+      }
+
     public void onClick(DialogInterface dialog, int which) {
         ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
         Intent intent = mAdapter.intentForPosition(which);
+        boolean alwaysCheck = (mAlwaysCheck != null && mAlwaysCheck.isChecked());
+        onIntentSelected(ri, intent, alwaysCheck);
+        finish();
+    }
 
-        if ((mAlwaysCheck != null) && mAlwaysCheck.isChecked()) {
+    protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
+        if (alwaysCheck) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
 
@@ -190,7 +202,6 @@
         if (intent != null) {
             startActivity(intent);
         }
-        finish();
     }
 
     private final class DisplayResolveInfo {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e3323b3..1ac22bc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -82,9 +82,9 @@
     <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
     <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
 
-    <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" />
-    <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
-    <protected-broadcast android:name="android.hardware.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
 
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
@@ -453,13 +453,13 @@
         android:label="@string/permlab_flashlight"
         android:description="@string/permdesc_flashlight" />
 
-    <!-- Allows an application to access USB devices
+    <!-- Allows an application to manage preferences and permissions for USB devices
          @hide -->
-    <permission android:name="android.permission.ACCESS_USB"
+    <permission android:name="android.permission.MANAGE_USB"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
         android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_accessUsb"
-        android:description="@string/permdesc_accessUsb" />
+        android:label="@string/permlab_manageUsb"
+        android:description="@string/permdesc_manageUsb" />
 
     <!-- Allows access to hardware peripherals.  Intended only for hardware testing -->
     <permission android:name="android.permission.HARDWARE_TEST"
@@ -1330,6 +1330,12 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <activity android:name="com.android.server.usb.UsbResolverActivity"
+                android:theme="@style/Theme.Dialog.Alert"
+                android:finishOnCloseSystemDialogs="true"
+                android:excludeFromRecents="true">
+        </activity>
+
         <service android:name="com.android.server.LoadAverageService"
                 android:exported="true" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 075cc66..37203eb 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -927,9 +927,9 @@
         the flashlight.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_accessUsb">access USB devices</string>
+    <string name="permlab_manageUsb">manage preferences and permissions for USB devices</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessUsb">Allows the application to access USB devices.</string>
+    <string name="permdesc_manageUsb">Allows the application to manage preferences and permissions for USB devices.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_hardware_test">test hardware</string>
@@ -1926,6 +1926,8 @@
     <string name="clearDefaultHintMsg">Clear default in Home Settings &gt; Applications &gt; Manage applications.</string>
     <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action.  for example, there may be many ringtone pickers installed.  A dialog is shown to the user allowing him to pick which activity should be used.  This is the title. -->
     <string name="chooseActivity">Select an action</string>
+    <!-- title for the USB activity chooser. -->
+    <string name="chooseUsbActivity">Select an application for the USB device</string>
     <!-- Text to display when there are no activities found to display in the
          activity chooser. See the "Select an action" title. -->
     <string name="noApplications">No applications can perform this action.</string>
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml
new file mode 100644
index 0000000..29df966
--- /dev/null
+++ b/data/etc/android.hardware.usb.accessory.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device supports USB accessories. -->
+<permissions>
+    <feature name="android.hardware.usb.accessory" />
+</permissions>
diff --git a/libs/usb/src/com/google/android/usb/UsbAccessory.java b/libs/usb/src/com/google/android/usb/UsbAccessory.java
new file mode 100644
index 0000000..931f42e
--- /dev/null
+++ b/libs/usb/src/com/google/android/usb/UsbAccessory.java
@@ -0,0 +1,96 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+package com.google.android.usb;
+
+/**
+ * A class representing a USB accessory.
+ */
+public final class UsbAccessory {
+
+    private final String mManufacturer;
+    private final String mModel;
+    private final String mType;
+    private final String mVersion;
+
+    /* package */ UsbAccessory(android.hardware.usb.UsbAccessory accessory) {
+        mManufacturer = accessory.getManufacturer();
+        mModel = accessory.getModel();
+        mType = accessory.getType();
+        mVersion = accessory.getVersion();
+    }
+
+    /**
+     * Returns the manufacturer of the accessory.
+     *
+     * @return the accessory manufacturer
+     */
+    public String getManufacturer() {
+        return mManufacturer;
+    }
+
+    /**
+     * Returns the model name of the accessory.
+     *
+     * @return the accessory model
+     */
+    public String getModel() {
+        return mModel;
+    }
+
+    /**
+     * Returns the type of the accessory.
+     *
+     * @return the accessory type
+     */
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the version of the accessory.
+     *
+     * @return the accessory version
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    private static boolean compare(String s1, String s2) {
+        if (s1 == null) return (s2 == null);
+        return s1.equals(s2);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof UsbAccessory) {
+            UsbAccessory accessory = (UsbAccessory)obj;
+            return (compare(mManufacturer, accessory.getManufacturer()) &&
+                    compare(mModel, accessory.getModel()) &&
+                    compare(mType, accessory.getType()) &&
+                    compare(mVersion, accessory.getVersion()));
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "UsbAccessory[mManufacturer=" + mManufacturer +
+                            ", mModel=" + mModel +
+                            ", mType=" + mType +
+                            ", mVersion=" + mVersion + "]";
+    }
+}
diff --git a/libs/usb/src/com/google/android/usb/UsbManager.java b/libs/usb/src/com/google/android/usb/UsbManager.java
new file mode 100644
index 0000000..d7afb95
--- /dev/null
+++ b/libs/usb/src/com/google/android/usb/UsbManager.java
@@ -0,0 +1,127 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+
+package com.google.android.usb;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.IUsbManager;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * This class allows you to access the state of USB, both in host and device mode.
+ *
+ * <p>You can obtain an instance of this class by calling {@link #getInstance}
+ *
+ */
+public class UsbManager {
+    private static final String TAG = "UsbManager";
+
+   /**
+     * Broadcast Action:  A broadcast for USB accessory attached event.
+     *
+     * This intent is sent when a USB accessory is attached.
+     * Call {@link #getAccessory(android.content.Intent)} to retrieve the
+     * {@link com.google.android.usb.UsbAccessory} for the attached accessory.
+     */
+    public static final String ACTION_USB_ACCESSORY_ATTACHED =
+            "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
+
+   /**
+     * Broadcast Action:  A broadcast for USB accessory detached event.
+     *
+     * This intent is sent when a USB accessory is detached.
+     * Call {@link #getAccessory(android.content.Intent)} to retrieve the
+     * {@link com.google.android.usb.UsbAccessory} for the attached accessory that was detached.
+     */
+    public static final String ACTION_USB_ACCESSORY_DETACHED =
+            "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
+
+    private final IUsbManager mService;
+
+    private UsbManager(IUsbManager service) {
+        mService = service;
+    }
+
+    /**
+     * Returns a new instance of this class.
+     *
+     * @return UsbManager instance.
+     */
+    public static UsbManager getInstance() {
+        IBinder b = ServiceManager.getService(Context.USB_SERVICE);
+        return new UsbManager(IUsbManager.Stub.asInterface(b));
+    }
+
+    /**
+     * Returns the {@link com.google.android.usb.UsbAccessory} for
+     * a {@link #ACTION_USB_ACCESSORY_ATTACHED} or {@link #ACTION_USB_ACCESSORY_ATTACHED}
+     * broadcast Intent
+     *
+     * @return UsbAccessory for the broadcast.
+     */
+    public static UsbAccessory getAccessory(Intent intent) {
+        android.hardware.usb.UsbAccessory accessory =
+            intent.getParcelableExtra(android.hardware.usb.UsbManager.EXTRA_ACCESSORY);
+        if (accessory == null) {
+            return null;
+        } else {
+            return new UsbAccessory(accessory);
+        }
+    }
+
+    /**
+     * Returns a list of currently attached USB accessories.
+     * (in the current implementation there can be at most one)
+     *
+     * @return list of USB accessories, or null if none are attached.
+     */
+    public UsbAccessory[] getAccessoryList() {
+        try {
+            android.hardware.usb.UsbAccessory accessory = mService.getCurrentAccessory();
+            if (accessory == null) {
+                return null;
+            } else {
+                return new UsbAccessory[] { new UsbAccessory(accessory) };
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getAccessoryList" , e);
+            return null;
+        }
+    }
+
+    /**
+     * Opens a file descriptor for reading and writing data to the USB accessory.
+     *
+     * @param accessory the USB accessory to open
+     * @return file descriptor, or null if the accessor could not be opened.
+     */
+    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+        try {
+            return mService.openAccessory(new android.hardware.usb.UsbAccessory(
+                    accessory.getManufacturer(),accessory.getModel(),
+                    accessory.getType(), accessory.getVersion()));
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in openAccessory" , e);
+            return null;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 1368baa..43dfb96 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -30,7 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.hardware.UsbManager;
+import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index a5333da..b26dac7 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -38,7 +38,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.hardware.UsbManager;
+import android.hardware.usb.UsbManager;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BatteryManager;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 25175e2..0f03f75 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import com.android.server.am.ActivityManagerService;
+import com.android.server.usb.UsbService;
 import com.android.internal.app.ShutdownThread;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.SamplingProfilerIntegration;
@@ -397,9 +398,10 @@
             }
 
             try {
-                Slog.i(TAG, "USB Observer");
+                Slog.i(TAG, "USB Service");
                 // Listen for USB changes
                 usb = new UsbService(context);
+                ServiceManager.addService(Context.USB_SERVICE, usb);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting UsbService", e);
             }
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java
deleted file mode 100644
index 578db0e..0000000
--- a/services/java/com/android/server/UsbService.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.hardware.UsbManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UEventObserver;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.util.ArrayList;
-
-/**
- * <p>UsbService monitors for changes to USB state.
- */
-class UsbService {
-    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 = 0;
-
-    // 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 final Context mContext;
-
-    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 (this) {
-                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
-                                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.
-                        boolean enabled = "1".equals(enabledStr);
-                        if (enabled) {
-                            if (!mEnabledFunctions.contains(function)) {
-                                mEnabledFunctions.add(function);
-                            }
-                            mDisabledFunctions.remove(function);
-                        } else {
-                            if (!mDisabledFunctions.contains(function)) {
-                                mDisabledFunctions.add(function);
-                            }
-                            mEnabledFunctions.remove(function);
-                        }
-                    }
-                }
-            }
-        }
-    };
-
-    public UsbService(Context context) {
-        mContext = context;
-        init();  // set initial status
-
-        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];
-
-        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)
-            return;
-
-        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);
-                } 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);
-        }
-    }
-
-    void systemReady() {
-        synchronized (this) {
-            update(false);
-            mSystemReady = true;
-        }
-    }
-
-    private final void update(boolean delayed) {
-        mHandler.removeMessages(MSG_UPDATE);
-        mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
-    }
-
-    private final Handler mHandler = new Handler() {
-        private void addEnabledFunctions(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) {
-            switch (msg.what) {
-                case MSG_UPDATE:
-                    synchronized (this) {
-                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
-
-                            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);
-                            addEnabledFunctions(intent);
-                            mContext.sendStickyBroadcast(intent);
-                        }
-                    }
-                    break;
-            }
-        }
-    };
-}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 7652a26..f774b29 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -26,7 +26,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.hardware.UsbManager;
+import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.InterfaceConfiguration;
 import android.net.IConnectivityManager;
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
new file mode 100644
index 0000000..07fb1cc
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -0,0 +1,551 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.FileUtils;
+import android.os.Process;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+class UsbDeviceSettingsManager {
+
+    private static final String TAG = "UsbDeviceSettingsManager";
+    private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
+
+    private final Context mContext;
+
+    // maps UID to user approved USB accessories
+    private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap =
+            new SparseArray<ArrayList<AccessoryFilter>>();
+    // Maps AccessoryFilter to user preferred application package
+    private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
+            new HashMap<AccessoryFilter, String>();
+
+    private final Object mLock = new Object();
+
+    // This class is used to describe a USB accessory.
+    // When used in HashMaps all values must be specified,
+    // but wildcards can be used for any of the fields in
+    // the package meta-data.
+    private static class AccessoryFilter {
+        // USB accessory manufacturer (or null for unspecified)
+        public final String mManufacturer;
+        // USB accessory model (or null for unspecified)
+        public final String mModel;
+        // USB accessory type (or null for unspecified)
+        public final String mType;
+        // USB accessory version (or null for unspecified)
+        public final String mVersion;
+
+        public AccessoryFilter(String manufacturer, String model, String type, String version) {
+            mManufacturer = manufacturer;
+            mModel = model;
+            mType = type;
+            mVersion = version;
+        }
+
+        public AccessoryFilter(UsbAccessory accessory) {
+            mManufacturer = accessory.getManufacturer();
+            mModel = accessory.getModel();
+            mType = accessory.getType();
+            mVersion = accessory.getVersion();
+        }
+
+        public static AccessoryFilter read(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            String manufacturer = null;
+            String model = null;
+            String type = null;
+            String version = null;
+
+            int count = parser.getAttributeCount();
+            for (int i = 0; i < count; i++) {
+                String name = parser.getAttributeName(i);
+                String value = parser.getAttributeValue(i);
+
+                if ("manufacturer".equals(name)) {
+                    manufacturer = value;
+                } else if ("model".equals(name)) {
+                    model = value;
+                } else if ("type".equals(name)) {
+                    type = value;
+                } else if ("version".equals(name)) {
+                    version = value;
+                }
+             }
+             return new AccessoryFilter(manufacturer, model, type, version);
+        }
+
+        public void write(XmlSerializer serializer)throws IOException {
+            serializer.startTag(null, "usb-accessory");
+            if (mManufacturer != null) {
+                serializer.attribute(null, "manufacturer", mManufacturer);
+            }
+            if (mModel != null) {
+                serializer.attribute(null, "model", mModel);
+            }
+            if (mType != null) {
+                serializer.attribute(null, "type", mType);
+            }
+            if (mVersion != null) {
+                serializer.attribute(null, "version", mVersion);
+            }
+            serializer.endTag(null, "usb-accessory");
+        }
+
+        public boolean matches(UsbAccessory acc) {
+            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+            if (mModel != null && !acc.getModel().equals(mModel)) return false;
+            if (mType != null && !acc.getType().equals(mType)) return false;
+            if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
+            return true;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            // can't compare if we have wildcard strings
+            if (mManufacturer == null || mModel == null || mType == null || mVersion == null) {
+                return false;
+            }
+            if (obj instanceof AccessoryFilter) {
+                AccessoryFilter filter = (AccessoryFilter)obj;
+                return (mManufacturer.equals(filter.mManufacturer) &&
+                        mModel.equals(filter.mModel) &&
+                        mType.equals(filter.mType) &&
+                        mVersion.equals(filter.mVersion));
+            }
+            if (obj instanceof UsbAccessory) {
+                UsbAccessory accessory = (UsbAccessory)obj;
+                return (mManufacturer.equals(accessory.getManufacturer()) &&
+                        mModel.equals(accessory.getModel()) &&
+                        mType.equals(accessory.getType()) &&
+                        mVersion.equals(accessory.getVersion()));
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+                    (mModel == null ? 0 : mModel.hashCode()) ^
+                    (mType == null ? 0 : mType.hashCode()) ^
+                    (mVersion == null ? 0 : mVersion.hashCode()));
+        }
+
+        @Override
+        public String toString() {
+            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+                                "\", mModel=\"" + mModel +
+                                "\", mType=\"" + mType +
+                                "\", mVersion=\"" + mVersion + "\"]";
+        }
+    }
+
+    private class MyPackageMonitor extends PackageMonitor {
+        public void onPackageRemoved(String packageName, int uid) {
+            synchronized (mLock) {
+                // clear all activity preferences for the package
+                if (clearPackageDefaultsLocked(packageName)) {
+                    writeSettingsLocked();
+                }
+            }
+        }
+
+        public void onUidRemoved(int uid) {
+            synchronized (mLock) {
+                // clear all permissions for the UID
+                if (clearUidDefaultsLocked(uid)) {
+                    writeSettingsLocked();
+                }
+            }
+        }
+    }
+    MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+    public UsbDeviceSettingsManager(Context context) {
+        mContext = context;
+        synchronized (mLock) {
+            readSettingsLocked();
+        }
+        mPackageMonitor.register(context, true);
+    }
+
+    private void readAccessoryPermission(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int uid = -1;
+        ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>();
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            if ("uid".equals(parser.getAttributeName(i))) {
+                uid = Integer.parseInt(parser.getAttributeValue(i));
+                break;
+            }
+        }
+        XmlUtils.nextElement(parser);
+        while ("usb-accessory".equals(parser.getName())) {
+            filters.add(AccessoryFilter.read(parser));
+            XmlUtils.nextElement(parser);
+        }
+        mAccessoryPermissionMap.put(uid, filters);
+    }
+
+    private void readPreference(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String packageName = null;
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            if ("package".equals(parser.getAttributeName(i))) {
+                packageName = parser.getAttributeValue(i);
+                break;
+            }
+        }
+        XmlUtils.nextElement(parser);
+        if ("usb-accessory".equals(parser.getName())) {
+            AccessoryFilter filter = AccessoryFilter.read(parser);
+            mAccessoryPreferenceMap.put(filter, packageName);
+        }
+        XmlUtils.nextElement(parser);
+    }
+
+    private void readSettingsLocked() {
+        FileInputStream stream = null;
+        try {
+            stream = new FileInputStream(sSettingsFile);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("accessory-permission".equals(tagName)) {
+                    readAccessoryPermission(parser);
+                } else if ("preference".equals(tagName)) {
+                    readPreference(parser);
+                 } else {
+                    XmlUtils.nextElement(parser);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, "settings file not found");
+        } catch (Exception e) {
+            Log.e(TAG, "error reading settings file, deleting to start fresh", e);
+            sSettingsFile.delete();
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private void writeSettingsLocked() {
+        FileOutputStream fos = null;
+        try {
+            FileOutputStream fstr = new FileOutputStream(sSettingsFile);
+            Log.d(TAG, "writing settings to " + fstr);
+            BufferedOutputStream str = new BufferedOutputStream(fstr);
+            FastXmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(str, "utf-8");
+            serializer.startDocument(null, true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startTag(null, "settings");
+
+            int count = mAccessoryPermissionMap.size();
+            for (int i = 0; i < count; i++) {
+                int uid = mAccessoryPermissionMap.keyAt(i);
+                ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
+                serializer.startTag(null, "accessory-permission");
+                serializer.attribute(null, "uid", Integer.toString(uid));
+                int filterCount = filters.size();
+                for (int j = 0; j < filterCount; j++) {
+                    filters.get(j).write(serializer);
+                }
+                serializer.endTag(null, "accessory-permission");
+            }
+
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                serializer.startTag(null, "preference");
+                serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
+                filter.write(serializer);
+                serializer.endTag(null, "preference");
+            }
+
+            serializer.endTag(null, "settings");
+            serializer.endDocument();
+
+            str.flush();
+            FileUtils.sync(fstr);
+            str.close();
+        } catch (Exception e) {
+            Log.e(TAG, "error writing settings file, deleting to start fresh", e);
+            sSettingsFile.delete();
+        }
+    }
+
+    // Checks to see if a package matches an accessory.
+    private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
+            UsbAccessory accessory) {
+        ActivityInfo ai = info.activityInfo;
+        PackageManager pm = mContext.getPackageManager();
+
+        XmlResourceParser parser = null;
+        try {
+            parser = ai.loadXmlMetaData(pm, metaDataName);
+            if (parser == null) {
+                Log.w(TAG, "no meta-data for " + info);
+                return false;
+            }
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if (accessory != null && "usb-accessory".equals(tagName)) {
+                    AccessoryFilter filter = AccessoryFilter.read(parser);
+                    if (filter.matches(accessory)) {
+                        return true;
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Unable to load component info " + info.toString(), e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return false;
+    }
+
+    private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
+            UsbAccessory accessory, Intent intent) {
+        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent,
+                PackageManager.GET_META_DATA);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageMatchesLocked(resolveInfo, intent.getAction(), accessory)) {
+                matches.add(resolveInfo);
+            }
+        }
+        return matches;
+    }
+
+    public void accessoryAttached(UsbAccessory accessory) {
+        Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ArrayList<ResolveInfo> matches;
+        String defaultPackage;
+        synchronized (mLock) {
+            matches = getAccessoryMatchesLocked(accessory, accessoryIntent);
+            // Launch our default activity directly, if we have one.
+            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
+            defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
+        }
+
+        if (defaultPackage != null) {
+            int count = matches.size();
+            for (int i = 0; i < count; i++) {
+                ResolveInfo rInfo = matches.get(i);
+                if (rInfo.activityInfo != null &&
+                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
+                    try {
+                        accessoryIntent.setComponent(new ComponentName(
+                                defaultPackage, rInfo.activityInfo.name));
+                        mContext.startActivity(accessoryIntent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.e(TAG, "startActivity failed", e);
+                    }
+                    return;
+                }
+            }
+        }
+
+        Intent intent = new Intent(mContext, UsbResolverActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent);
+        intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "unable to start UsbResolverActivity");
+        }
+    }
+
+    public void accessoryDetached(UsbAccessory accessory) {
+        Intent intent = new Intent(
+                UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        mContext.sendBroadcast(intent);
+    }
+
+    public void checkPermission(UsbAccessory accessory) {
+        if (accessory == null) return;
+        synchronized (mLock) {
+            ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
+            if (filterList != null) {
+                int count = filterList.size();
+                for (int i = 0; i < count; i++) {
+                    AccessoryFilter filter = filterList.get(i);
+                    if (filter.equals(accessory)) {
+                        // permission allowed
+                        return;
+                    }
+                }
+            }
+        }
+        throw new SecurityException("User has not given permission to accessory " + accessory);
+    }
+
+    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
+        AccessoryFilter filter = new AccessoryFilter(accessory);
+        boolean changed = false;
+        synchronized (mLock) {
+            if (packageName == null) {
+                changed = (mAccessoryPreferenceMap.remove(filter) != null);
+            } else {
+                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
+                if (changed) {
+                    mAccessoryPreferenceMap.put(filter, packageName);
+                }
+            }
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid);
+            if (filterList == null) {
+                filterList = new ArrayList<AccessoryFilter>();
+                mAccessoryPermissionMap.put(uid, filterList);
+            } else {
+                int count = filterList.size();
+                for (int i = 0; i < count; i++) {
+                    if (filterList.get(i).equals(accessory)) return;
+                }
+            }
+            filterList.add(new AccessoryFilter(accessory));
+            writeSettingsLocked();
+        }
+    }
+
+    public boolean hasDefaults(String packageName, int uid) {
+        synchronized (mLock) {
+            if (mAccessoryPermissionMap.get(uid) != null) return true;
+            if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
+            return false;
+        }
+    }
+
+    public void clearDefaults(String packageName, int uid) {
+        synchronized (mLock) {
+            boolean packageCleared = clearPackageDefaultsLocked(packageName);
+            boolean uidCleared = clearUidDefaultsLocked(uid);
+            if (packageCleared || uidCleared) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    private boolean clearUidDefaultsLocked(int uid) {
+        boolean cleared = false;
+        int index = mAccessoryPermissionMap.indexOfKey(uid);
+        if (index >= 0) {
+            mAccessoryPermissionMap.removeAt(index);
+            cleared = true;
+        }
+        return cleared;
+    }
+
+    private boolean clearPackageDefaultsLocked(String packageName) {
+        boolean cleared = false;
+        synchronized (mLock) {
+            if (mAccessoryPreferenceMap.containsValue(packageName)) {
+                // make a copy of the key set to avoid ConcurrentModificationException
+                Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
+                for (int i = 0; i < keys.length; i++) {
+                    Object key = keys[i];
+                    if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
+                        mAccessoryPreferenceMap.remove(key);
+                        cleared = true;
+                    }
+                }
+            }
+            return cleared;
+        }
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("  Accessory permissions:");
+            int count = mAccessoryPermissionMap.size();
+            for (int i = 0; i < count; i++) {
+                int uid = mAccessoryPermissionMap.keyAt(i);
+                pw.println("    " + "uid " + uid + ":");
+                ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
+                for (AccessoryFilter filter : filters) {
+                    pw.println("      " + filter);
+                }
+            }
+            pw.println("  Accessory preferences:");
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                pw.println("    " + filter + ": " + mAccessoryPreferenceMap.get(filter));
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/services/java/com/android/server/usb/UsbResolverActivity.java
new file mode 100644
index 0000000..02669fd
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbResolverActivity.java
@@ -0,0 +1,91 @@
+/*
+ * 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 and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import com.android.internal.app.ResolverActivity;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/* Activity for choosing an application for a USB device or accessory */
+public class UsbResolverActivity extends ResolverActivity {
+    public static final String TAG = "UsbResolverActivity";
+    public static final String EXTRA_RESOLVE_INFOS = "rlist";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Intent intent = getIntent();
+        Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+        if (!(targetParcelable instanceof Intent)) {
+            Log.w("UsbResolverActivity", "Target is not an intent: " + targetParcelable);
+            finish();
+            return;
+        }
+        Intent target = (Intent)targetParcelable;
+        ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
+        Log.d(TAG, "rList.size() " + rList.size());
+        CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
+        super.onCreate(savedInstanceState, target, title, null, rList,
+                true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */
+                true  /* Set alwaysChoose to display activity when only one choice is available.
+                         This is necessary because this activity is needed for the user to allow
+                         the application permission to access the device */
+                );
+    }
+
+    protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
+        try {
+            IBinder b = ServiceManager.getService(USB_SERVICE);
+            IUsbManager service = IUsbManager.Stub.asInterface(b);
+            int uid = ri.activityInfo.applicationInfo.uid;
+            String action = intent.getAction();
+
+            if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
+                UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(
+                        UsbManager.EXTRA_ACCESSORY);
+                // grant permission for the accessory
+                service.grantAccessoryPermission(accessory, uid);
+                // set or clear default setting
+                if (alwaysCheck) {
+                    service.setAccessoryPackage(accessory, ri.activityInfo.packageName);
+                } else {
+                    service.setAccessoryPackage(accessory, null);
+                }
+            }
+
+            try {
+                startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "startActivity failed", e);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "onIntentSelected failed", e);
+        }
+    }
+}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
new file mode 100644
index 0000000..16e697c
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+package com.android.server.usb;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+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.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.
+ */
+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>();
+
+    private boolean mSystemReady;
+
+    private UsbAccessory mCurrentAccessory;
+    // functions to restore after exiting accessory mode
+    private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>();
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private final UsbDeviceSettingsManager mDeviceManager;
+    private final boolean mHasUsbAccessory;
+
+    /*
+     * Handles USB function enable/disable events (device mode)
+     */
+    private final void functionEnabledLocked(String function, boolean enabled) {
+        boolean enteringAccessoryMode =
+            (mHasUsbAccessory && enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function));
+
+        if (enteringAccessoryMode) {
+            // keep a list of functions to reenable after exiting accessory mode
+            mAccessoryRestoreFunctions.clear();
+            int count = mEnabledFunctions.size();
+            for (int i = 0; i < count; i++) {
+                String f = mEnabledFunctions.get(i);
+                // RNDIS should not be restored and adb is handled automatically
+                if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) &&
+                    !UsbManager.USB_FUNCTION_ADB.equals(f) &&
+                    !UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) {
+                    mAccessoryRestoreFunctions.add(f);
+                }
+            }
+        }
+        if (enabled) {
+            if (!mEnabledFunctions.contains(function)) {
+                mEnabledFunctions.add(function);
+            }
+            mDisabledFunctions.remove(function);
+        } else {
+            if (!mDisabledFunctions.contains(function)) {
+                mDisabledFunctions.add(function);
+            }
+            mEnabledFunctions.remove(function);
+        }
+
+        if (enteringAccessoryMode) {
+            String[] strings = nativeGetAccessoryStrings();
+            if (strings != null) {
+                mCurrentAccessory = new UsbAccessory(strings);
+                Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+                mDeviceManager.accessoryAttached(mCurrentAccessory);
+            } else {
+                Log.e(TAG, "nativeGetAccessoryStrings failed");
+            }
+        }
+    }
+
+    /*
+     * 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);
+                    }
+                }
+            }
+        }
+    };
+
+    public UsbService(Context context) {
+        mContext = context;
+        mDeviceManager = new UsbDeviceSettingsManager(context);
+        PackageManager pm = mContext.getPackageManager();
+        mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+
+        init();  // set initial status
+
+        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];
+
+        // 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 (mConfiguration < 0)
+            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);
+                } 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);
+        }
+    }
+
+    public void systemReady() {
+        synchronized (mLock) {
+            update(false);
+            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() {
+        synchronized (mLock) {
+            mDeviceManager.checkPermission(mCurrentAccessory);
+            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");
+            }
+            mDeviceManager.checkPermission(mCurrentAccessory);
+            return nativeOpenAccessory();
+        }
+    }
+
+    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        mDeviceManager.setAccessoryPackage(accessory, packageName);
+    }
+
+    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        mDeviceManager.grantAccessoryPermission(accessory, uid);
+    }
+
+    public boolean hasDefaults(String packageName, int uid) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        return mDeviceManager.hasDefaults(packageName, uid);
+    }
+
+    public void clearDefaults(String packageName, int uid) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        mDeviceManager.clearDefaults(packageName, uid);
+    }
+
+    /*
+     * 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 && mCurrentAccessory != null) {
+                                // turn off accessory mode when we are disconnected
+                                if (UsbManager.setFunctionEnabled(
+                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+                                    Log.d(TAG, "exited USB accessory mode");
+
+                                    // restore previously enabled functions
+                                    for (String function : mAccessoryRestoreFunctions) {
+                                        if (UsbManager.setFunctionEnabled(function, true)) {
+                                            Log.e(TAG, "could not reenable function " + function);
+                                        }
+                                    }
+                                    mAccessoryRestoreFunctions.clear();
+
+                                    mDeviceManager.accessoryDetached(mCurrentAccessory);
+                                    mCurrentAccessory = null;
+
+                                    // this will cause an immediate reset of the USB bus,
+                                    // so there is no point in sending the
+                                    // function disabled broadcast.
+                                    return;
+                                } else {
+                                    Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY");
+                                }
+                            }
+
+                            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)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump UsbManager from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mLock) {
+            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.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+            pw.println("  mCurrentAccessory: " + mCurrentAccessory);
+
+            mDeviceManager.dump(fd, pw);
+        }
+    }
+
+    // accessory support
+    private native String[] nativeGetAccessoryStrings();
+    private native ParcelFileDescriptor nativeOpenAccessory();
+}
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
index dff8665..08a526e 100644
--- a/services/jni/com_android_server_UsbService.cpp
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -23,14 +23,119 @@
 #include "utils/Vector.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 file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+    jfieldID mDescriptor;
+} gFileDescriptorOffsets;
+
+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]) {
+        LOGD("string %d: %s", index, buffer);
+        jstring obj = env->NewStringUTF(buffer);
+        env->SetObjectArrayElement(strArray, index, obj);
+        env->DeleteLocalRef(obj);
+    }
+}
+
+
+static jobjectArray android_server_UsbService_getAccessoryStrings(JNIEnv *env, jobject thiz)
+{
+    LOGD("getAccessoryStrings");
+    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(4, 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_TYPE, strArray, 2);
+    set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);
+
+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 = env->NewObject(gFileDescriptorOffsets.mClass,
+        gFileDescriptorOffsets.mConstructor);
+    if (fileDescriptor != NULL) {
+        env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
+    } else {
+        return NULL;
+    }
+    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+}
+
+static JNINativeMethod method_table[] = {
+    { "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
+                                  (void*)android_server_UsbService_getAccessoryStrings },
+    { "nativeOpenAccessory","()Landroid/os/ParcelFileDescriptor;",
+                                  (void*)android_server_UsbService_openAccessory },
+};
+
 int register_android_server_UsbService(JNIEnv *env)
 {
+   jclass clazz = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
+    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
+    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
+                 "Unable to find descriptor field in java.io.FileDescriptor");
 
-    return 0;
+   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/UsbService",
+            method_table, NELEM(method_table));
 }
 
 };