Merge "Add a protocol property to the APNs and use it." into gingerbread
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/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index d2b3bc7..5ee1f60 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -18,17 +18,22 @@
 
 import android.content.pm.PackageManager;
 import android.content.pm.RegisteredServicesCache;
+import android.content.pm.ResolveInfo;
 import android.content.pm.XmlSerializerAndParser;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.text.TextUtils;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.List;
 
 /**
  * A cache of services that export the {@link IAccountAuthenticator} interface. This cache
@@ -63,11 +68,40 @@
                     com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
             final int prefId = sa.getResourceId(
                     com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
+            
+            boolean customTokens = false;
+            try {
+                // In HC this will be an attribute in authenticator.xml, this is a workaround
+                // using meta-data to avoid changes to the API. 
+                // If meta-data is absent the old behavior is preserved. 
+                // Authenticator will know if AccountManager supports customTokens or not.
+                PackageManager pm = mContext.getPackageManager();
+                List<ResolveInfo> resolveInfos = pm.queryIntentServices(
+                        new Intent(AccountManager.ACTION_AUTHENTICATOR_INTENT),
+                        PackageManager.GET_META_DATA);
+                for (ResolveInfo resolveInfo: resolveInfos) {
+                    android.content.pm.ServiceInfo si = resolveInfo.serviceInfo;
+                    if (!packageName.equals(si.packageName)) {
+                        continue;
+                    }
+                    Object ctString = si.metaData.get(AccountManager.ACTION_AUTHENTICATOR_INTENT 
+                            + ".customTokens");
+                    if (ctString != null) {
+                        customTokens = true;
+                    }
+                }
+            } catch (Throwable t) {
+                // Protected against invalid data in meta or unexpected 
+                // conditions - the authenticator will not have the new 
+                // features. 
+                Log.e(TAG, "Error getting customTokens metadata " + t);
+            }
+            
             if (TextUtils.isEmpty(accountType)) {
                 return null;
             }
-            return new AuthenticatorDescription(accountType, packageName, labelId, iconId, 
-                    smallIconId, prefId);
+            return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
+                    smallIconId, prefId, customTokens);
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index fd3a0d0..677b959 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -188,6 +188,24 @@
     public static final String KEY_ERROR_CODE = "errorCode";
     public static final String KEY_ERROR_MESSAGE = "errorMessage";
     public static final String KEY_USERDATA = "userdata";
+    /**
+     * Authenticators using 'customTokens' option will also get the UID of the
+     * caller
+     * @hide
+     */
+    public static final String KEY_CALLER_UID = "callerUid";
+
+    /**
+     * @hide 
+     */
+    public static final String KEY_CALLER_PID = "callerPid";
+
+    /**
+     * Boolean, if set and 'customTokens' the authenticator is responsible for
+     * notifications.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
 
     public static final String ACTION_AUTHENTICATOR_INTENT =
             "android.accounts.AccountAuthenticator";
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 9a8cc15..46e2574 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -91,6 +91,8 @@
 
     private final Context mContext;
 
+    private final PackageManager mPackageManager;
+
     private HandlerThread mMessageThread;
     private final MessageHandler mMessageHandler;
 
@@ -214,6 +216,7 @@
 
     public AccountManagerService(Context context) {
         mContext = context;
+        mPackageManager = context.getPackageManager();
 
         mOpenHelper = new DatabaseHelper(mContext);
 
@@ -520,6 +523,18 @@
         if (account == null) throw new IllegalArgumentException("account is null");
         checkManageAccountsPermission();
         long identityToken = clearCallingIdentity();
+
+        cancelNotification(getSigninRequiredNotificationId(account));
+        synchronized(mCredentialsPermissionNotificationIds) {
+            for (Pair<Pair<Account, String>, Integer> pair:
+                mCredentialsPermissionNotificationIds.keySet()) {
+                if (account.equals(pair.first.first)) {
+                    int id = mCredentialsPermissionNotificationIds.get(pair);
+                    cancelNotification(id);
+                }
+            }
+        }
+
         try {
             new RemoveAccountSession(response, account).bind();
         } finally {
@@ -842,19 +857,49 @@
 
     public void getAuthToken(IAccountManagerResponse response, final Account account,
             final String authTokenType, final boolean notifyOnAuthFailure,
-            final boolean expectActivityLaunch, final Bundle loginOptions) {
+            final boolean expectActivityLaunch, Bundle loginOptionsIn) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getAuthToken: " + account
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
         final int callerUid = Binder.getCallingUid();
-        final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+        final int callerPid = Binder.getCallingPid();
+
+        AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+            mAuthenticatorCache.getServiceInfo(
+                    AuthenticatorDescription.newKey(account.type));
+        final boolean customTokens =
+            authenticatorInfo != null && authenticatorInfo.type.customTokens;
+
+        // skip the check if customTokens
+        final boolean permissionGranted = customTokens ||
+            permissionIsGranted(account, authTokenType, callerUid);
+
+        final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
+            loginOptionsIn;
+        if (customTokens) {
+            // let authenticator know the identity of the caller
+            loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
+            loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
+            if (notifyOnAuthFailure) {
+                loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
+            }
+        }
 
         long identityToken = clearCallingIdentity();
         try {
             // if the caller has permission, do the peek. otherwise go the more expensive
             // route of starting a Session
-            if (permissionGranted) {
+            if (!customTokens && permissionGranted) {
                 String authToken = readAuthTokenFromDatabase(account, authTokenType);
                 if (authToken != null) {
                     Bundle result = new Bundle();
@@ -908,12 +953,14 @@
                                         "the type and name should not be empty");
                                 return;
                             }
-                            saveAuthTokenToDatabase(new Account(name, type),
-                                    authTokenType, authToken);
+                            if (!customTokens) {
+                                saveAuthTokenToDatabase(new Account(name, type),
+                                        authTokenType, authToken);
+                            }
                         }
 
                         Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
-                        if (intent != null && notifyOnAuthFailure) {
+                        if (intent != null && notifyOnAuthFailure && !customTokens) {
                             doNotification(
                                     account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
                                     intent);
@@ -972,6 +1019,10 @@
             AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
 
         Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
+        // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
+        // Since it was set in Eclair+ we can't change it without breaking apps using
+        // the intent from a non-Activity context.
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.addCategory(
                 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
 
@@ -1849,12 +1900,12 @@
     }
 
     private boolean inSystemImage(int callerUid) {
-        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+        String[] packages = mPackageManager.getPackagesForUid(callerUid);
         for (String name : packages) {
             try {
-                PackageInfo packageInfo =
-                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
-                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
+                if (packageInfo != null
+                        && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                     return true;
                 }
             } catch (PackageManager.NameNotFoundException e) {
@@ -1872,7 +1923,7 @@
                 && hasExplicitlyGrantedPermission(account, authTokenType);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
-                    + callerUid + ", account " + account
+                    + callerUid + ", " + account
                     + ": is authenticator? " + fromAuthenticator
                     + ", has explicit permission? " + hasExplicitGrants);
         }
@@ -1884,7 +1935,7 @@
                 mAuthenticatorCache.getAllServices()) {
             if (serviceInfo.type.type.equals(accountType)) {
                 return (serviceInfo.uid == callingUid) ||
-                        (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid)
+                        (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
                                 == PackageManager.SIGNATURE_MATCH);
             }
         }
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index c6515672..4d3769a 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -44,9 +44,16 @@
     /** The package name that can be used to lookup the resources from above. */
     final public String packageName;
 
-    /** A constructor for a full AuthenticatorDescription */
+    /** Authenticator handles its own token caching and permission screen 
+      * @hide
+      */
+    final public boolean customTokens;
+
+    /** A constructor for a full AuthenticatorDescription
+     *  @hide 
+     */
     public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
-            int smallIconId, int prefId) {
+            int smallIconId, int prefId, boolean customTokens) {
         if (type == null) throw new IllegalArgumentException("type cannot be null");
         if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
         this.type = type;
@@ -55,6 +62,12 @@
         this.iconId = iconId;
         this.smallIconId = smallIconId;
         this.accountPreferencesId = prefId;
+        this.customTokens = customTokens;
+    }
+
+    public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
+            int smallIconId, int prefId) {
+        this(type, packageName, labelId, iconId, smallIconId, prefId, false);
     }
 
     /**
@@ -74,6 +87,7 @@
         this.iconId = 0;
         this.smallIconId = 0;
         this.accountPreferencesId = 0;
+        this.customTokens = false;
     }
 
     private AuthenticatorDescription(Parcel source) {
@@ -83,6 +97,7 @@
         this.iconId = source.readInt();
         this.smallIconId = source.readInt();
         this.accountPreferencesId = source.readInt();
+        this.customTokens = source.readByte() == 1;
     }
 
     /** @inheritDoc */
@@ -115,6 +130,7 @@
         dest.writeInt(iconId);
         dest.writeInt(smallIconId);
         dest.writeInt(accountPreferencesId);
+        dest.writeByte((byte) (customTokens ? 1 : 0));
     }
 
     /** Used to create the object from a parcel. */
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/Usb.java b/core/java/android/hardware/Usb.java
deleted file mode 100644
index 57271d4..0000000
--- a/core/java/android/hardware/Usb.java
+++ /dev/null
@@ -1,99 +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;
-
-/**
- * Class for accessing USB state information.
- * @hide
- */
-public class Usb {
-   /**
-     * Broadcast Action:  A broadcast for USB connected events.
-     *
-     * The extras bundle will name/value pairs with the name of the function
-     * 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_CONNECTED =
-            "android.hardware.action.USB_CONNECTED";
-
-   /**
-     * Broadcast Action:  A broadcast for USB disconnected events.
-     */
-    public static final String ACTION_USB_DISCONNECTED =
-            "android.hardware.action.USB_DISCONNECTED";
-
-   /**
-     * Broadcast Action:  A sticky broadcast for USB state change events.
-     *
-     * This is a sticky broadcast for clients that are interested in both USB connect and
-     * disconnect events.  If you are only concerned with one or the other, you can use
-     * {@link #ACTION_USB_CONNECTED} or {@link #ACTION_USB_DISCONNECTED} to avoid receiving
-     * unnecessary broadcasts.  The boolean {@link #USB_CONNECTED} extra indicates whether
-     * USB is connected or disconnected.
-     * The extras bundle will also contain name/value pairs with the name of the function
-     * 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";
-
-    /**
-     * Name of the USB mass storage USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} 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_CONNECTED} broadcast
-     */
-    public static final String USB_FUNCTION_ADB = "adb";
-
-    /**
-     * Name of the RNDIS ethernet USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
-     */
-    public static final String USB_FUNCTION_RNDIS = "rndis";
-
-    /**
-     * Name of the MTP USB function.
-     * Used in extras for the {@link #ACTION_USB_CONNECTED} 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_CONNECTED} 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_CONNECTED} broadcast
-     */
-    public static final String USB_FUNCTION_DISABLED = "disabled";
-}
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/os/Looper.java b/core/java/android/os/Looper.java
index d360140..994c242 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.util.Config;
+import android.util.Log;
 import android.util.Printer;
 
 /**
@@ -106,6 +107,12 @@
     public static final void loop() {
         Looper me = myLooper();
         MessageQueue queue = me.mQueue;
+        
+        // Make sure the identity of this thread is that of the local process,
+        // and keep track of what that identity token actually is.
+        Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
+        
         while (true) {
             Message msg = queue.next(); // might block
             //if (!me.mRun) {
@@ -124,6 +131,17 @@
                 if (me.mLogging!= null) me.mLogging.println(
                         "<<<<< Finished to    " + msg.target + " "
                         + msg.callback);
+                
+                // Make sure that during the course of dispatching the
+                // identity of the thread wasn't corrupted.
+                final long newIdent = Binder.clearCallingIdentity();
+                if (ident != newIdent) {
+                    Log.wtf("Looper", "Thread identity changed from 0x"
+                            + Long.toHexString(ident) + " to 0x"
+                            + Long.toHexString(newIdent) + " while dispatching to "
+                            + msg.target + " " + msg.callback + " what=" + msg.what);
+                }
+                
                 msg.recycle();
             }
         }
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/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f0e5517..060f858 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -511,7 +511,7 @@
         String args[] = {
             "--setuid=1000",
             "--setgid=1000",
-            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
+            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
             "--capabilities=130104352,130104352",
             "--runtime-init",
             "--nice-name=system_server",
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..80a0904
--- /dev/null
+++ b/data/etc/android.hardware.usb.accessory.xml
@@ -0,0 +1,22 @@
+<?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" />
+    <library name="com.android.future.usb.accessory"
+            file="/system/framework/com.android.future.usb.accessory.jar" />
+</permissions>
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 421ad663..46baf9d 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -592,10 +592,6 @@
     // mKeyRepeatState.lastKeyEntry in addition to the one we return.
     entry->refCount += 1;
 
-    if (entry->repeatCount == 1) {
-        entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
-    }
-
     mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
     return entry;
 }
@@ -645,6 +641,12 @@
             resetKeyRepeatLocked();
         }
 
+        if (entry->repeatCount == 1) {
+            entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
+        } else {
+            entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
+        }
+
         entry->dispatchInProgress = true;
         resetTargetsLocked();
 
diff --git a/libs/usb/Android.mk b/libs/usb/Android.mk
new file mode 100644
index 0000000..129828f
--- /dev/null
+++ b/libs/usb/Android.mk
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE:= com.android.future.usb.accessory
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/usb/src/com/android/future/usb/UsbAccessory.java b/libs/usb/src/com/android/future/usb/UsbAccessory.java
new file mode 100644
index 0000000..cdd2b73
--- /dev/null
+++ b/libs/usb/src/com/android/future/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.android.future.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/android/future/usb/UsbManager.java b/libs/usb/src/com/android/future/usb/UsbManager.java
new file mode 100644
index 0000000..f74b291
--- /dev/null
+++ b/libs/usb/src/com/android/future/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.android.future.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/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
new file mode 100644
index 0000000..d555961
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AccessoryChatGB
+
+LOCAL_JAVA_LIBRARIES := com.android.future.usb.accessory
+
+# Force an old SDK version to make sure we aren't using newer UsbManager APIs
+LOCAL_SDK_VERSION := 8
+
+include $(BUILD_PACKAGE)
diff --git a/libs/usb/tests/AccessoryChat/AndroidManifest.xml b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
new file mode 100644
index 0000000..d6093ae
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.accessorychat">
+
+    <application>
+        <uses-library android:name="com.android.future.usb.accessory" />
+
+        <activity android:name="AccessoryChat" android:label="Accessory Chat GB">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+            </intent-filter>
+
+            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
+                android:resource="@xml/accessory_filter" />
+        </activity>
+    </application>
+    <uses-sdk android:minSdkVersion="10" />
+</manifest>
diff --git a/libs/usb/tests/AccessoryChat/README.txt b/libs/usb/tests/AccessoryChat/README.txt
new file mode 100644
index 0000000..d2ce11e
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/README.txt
@@ -0,0 +1,10 @@
+This is a test app for the USB accessory APIs.  It consists of two parts:
+
+AccessoryChat - A Java app with a chat-like UI that sends and receives strings
+                via the UsbAccessory class.
+
+accessorychat - A C command-line program that communicates with AccessoryChat.
+                This program behaves as if it were a USB accessory.
+                It builds both for the host (Linux PC) and as an android
+                command line program, which will work if run as root on an
+                android device with USB host support
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
new file mode 100644
index 0000000..5a7b30e
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build for Linux (desktop) host
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := accessorychat.c usbhost.c
+
+LOCAL_MODULE := accessorychat
+
+LOCAL_C_INCLUDES += bionic/libc/kernel/common
+LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_LDLIBS += -lpthread
+LOCAL_CFLAGS := -g -O0
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
new file mode 100644
index 0000000..94cc0ce
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <usbhost/usbhost.h>
+#include <linux/usb/f_accessory.h>
+
+struct usb_device *sDevice = NULL;
+
+static void* read_thread(void* arg) {
+    int endpoint = (int)arg;
+    int ret = 0;
+
+    while (sDevice && ret >= 0) {
+        char    buffer[16384];
+
+        ret = usb_device_bulk_transfer(sDevice, endpoint, buffer, sizeof(buffer), 1000);
+        if (ret < 0 && errno == ETIMEDOUT)
+            ret = 0;
+        if (ret > 0) {
+            fwrite(buffer, 1, ret, stdout);
+            printf("\n");
+            fflush(stdout);
+        }
+    }
+
+    return NULL;
+}
+
+static void* write_thread(void* arg) {
+    int endpoint = (int)arg;
+    int ret = 0;
+
+    while (ret >= 0) {
+        char    buffer[16384];
+        char *line = fgets(buffer, sizeof(buffer), stdin);
+        if (!line || !sDevice)
+            break;
+        ret = usb_device_bulk_transfer(sDevice, endpoint, line, strlen(line), 1000);
+    }
+
+    return NULL;
+}
+
+static void send_string(struct usb_device *device, int index, const char* string) {
+    int ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+            ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0);
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+    uint16_t vendorId, productId;
+    int ret;
+    pthread_t th;
+
+    struct usb_device *device = usb_device_open(devname);
+    if (!device) {
+        fprintf(stderr, "usb_device_open failed\n");
+        return 0;
+    }
+
+    vendorId = usb_device_get_vendor_id(device);
+    productId = usb_device_get_product_id(device);
+
+    if (vendorId == 0x18D1 || vendorId == 0x22B8) {
+        if (!sDevice && (productId == 0x2D00 || productId == 0x2D01)) {
+            struct usb_descriptor_header* desc;
+            struct usb_descriptor_iter iter;
+            struct usb_interface_descriptor *intf = NULL;
+            struct usb_endpoint_descriptor *ep1 = NULL;
+            struct usb_endpoint_descriptor *ep2 = NULL;
+
+            printf("Found android device in accessory mode\n");
+            sDevice = device;
+
+            usb_descriptor_iter_init(device, &iter);
+            while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) {
+                if (desc->bDescriptorType == USB_DT_INTERFACE) {
+                    intf = (struct usb_interface_descriptor *)desc;
+                } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+                    if (ep1)
+                        ep2 = (struct usb_endpoint_descriptor *)desc;
+                    else
+                        ep1 = (struct usb_endpoint_descriptor *)desc;
+                }
+            }
+
+            if (!intf) {
+                fprintf(stderr, "interface not found\n");
+                exit(1);
+            }
+            if (!ep1 || !ep2) {
+                fprintf(stderr, "endpoints not found\n");
+                exit(1);
+            }
+
+            if (usb_device_claim_interface(device, intf->bInterfaceNumber)) {
+                fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno);
+                exit(1);
+            }
+
+            if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+                pthread_create(&th, NULL, read_thread, (void *)ep1->bEndpointAddress);
+                pthread_create(&th, NULL, write_thread, (void *)ep2->bEndpointAddress);
+            } else {
+                pthread_create(&th, NULL, read_thread, (void *)ep2->bEndpointAddress);
+                pthread_create(&th, NULL, write_thread, (void *)ep1->bEndpointAddress);
+            }
+        } else {
+            printf("Found possible android device - attempting to switch to accessory mode\n");
+
+            send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");
+            send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat");
+            send_string(device, ACCESSORY_STRING_TYPE, "Sample Program");
+            send_string(device, ACCESSORY_STRING_VERSION, "1.0");
+
+            ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+                    ACCESSORY_START, 0, 0, 0, 0, 0);
+            return 0;
+        }
+    }
+
+    if (device != sDevice)
+        usb_device_close(device);
+
+    return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+    if (sDevice && !strcmp(usb_device_get_name(sDevice), devname)) {
+        usb_device_close(sDevice);
+        sDevice = NULL;
+        // exit when we are disconnected
+        return 1;
+    }
+    return 0;
+}
+
+
+int main(int argc, char* argv[]) {
+    struct usb_host_context* context = usb_host_init();
+    if (!context) {
+        fprintf(stderr, "usb_host_init failed");
+        return 1;
+    }
+
+    // this will never return so it is safe to pass thiz directly
+    usb_host_run(context, usb_device_added, usb_device_removed, NULL, NULL);
+    return 0;
+}
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c b/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c
new file mode 100644
index 0000000..f5a7c3f
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define DEBUG 1
+#if DEBUG
+
+#ifdef USE_LIBLOG
+#define LOG_TAG "usbhost"
+#include "utils/Log.h"
+#define D LOGD
+#else
+#define D printf
+#endif
+
+#else
+#define D(...)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/inotify.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pthread.h>
+
+#include <linux/usbdevice_fs.h>
+#include <asm/byteorder.h>
+
+#include "usbhost/usbhost.h"
+
+#define USB_FS_DIR "/dev/bus/usb"
+#define USB_FS_ID_SCANNER   "/dev/bus/usb/%d/%d"
+#define USB_FS_ID_FORMAT    "/dev/bus/usb/%03d/%03d"
+
+
+struct usb_host_context {
+    int fd;
+};
+
+struct usb_device {
+    char dev_name[64];
+    unsigned char desc[4096];
+    int desc_length;
+    int fd;
+    int writeable;
+};
+
+static inline int badname(const char *name)
+{
+    while(*name) {
+        if(!isdigit(*name++)) return 1;
+    }
+    return 0;
+}
+
+/* returns true if one of the callbacks indicates we are done */
+static int find_existing_devices(usb_device_added_cb added_cb,
+                                  usb_device_removed_cb removed_cb,
+                                  void *client_data)
+{
+    char busname[32], devname[32];
+    DIR *busdir , *devdir ;
+    struct dirent *de;
+    int done = 0;
+
+    busdir = opendir(USB_FS_DIR);
+    if(busdir == 0) return 1;
+
+    while ((de = readdir(busdir)) != 0 && !done) {
+        if(badname(de->d_name)) continue;
+
+        snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name);
+        devdir = opendir(busname);
+        if(devdir == 0) continue;
+
+        while ((de = readdir(devdir)) && !done) {
+            if(badname(de->d_name)) continue;
+
+            snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
+            done = added_cb(devname, client_data);
+        } // end of devdir while
+        closedir(devdir);
+    } //end of busdir while
+    closedir(busdir);
+
+    return done;
+}
+
+struct usb_host_context *usb_host_init()
+{
+    struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
+    if (!context) {
+        fprintf(stderr, "out of memory in usb_host_context\n");
+        return NULL;
+    }
+    context->fd = inotify_init();
+    if (context->fd < 0) {
+        fprintf(stderr, "inotify_init failed\n");
+        free(context);
+        return NULL;
+    }
+    return context;
+}
+
+void usb_host_cleanup(struct usb_host_context *context)
+{
+    close(context->fd);
+    free(context);
+}
+
+void usb_host_run(struct usb_host_context *context,
+                  usb_device_added_cb added_cb,
+                  usb_device_removed_cb removed_cb,
+                  usb_discovery_done_cb discovery_done_cb,
+                  void *client_data)
+{
+    struct inotify_event* event;
+    char event_buf[512];
+    char path[100];
+    int i, ret, done = 0;
+    int wd, wds[10];
+    int wd_count = sizeof(wds) / sizeof(wds[0]);
+
+    D("Created device discovery thread\n");
+
+    /* watch for files added and deleted within USB_FS_DIR */
+    memset(wds, 0, sizeof(wds));
+    /* watch the root for new subdirectories */
+    wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
+    if (wds[0] < 0) {
+        fprintf(stderr, "inotify_add_watch failed\n");
+        if (discovery_done_cb)
+            discovery_done_cb(client_data);
+        return;
+    }
+
+    /* watch existing subdirectories of USB_FS_DIR */
+    for (i = 1; i < wd_count; i++) {
+        snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i);
+        ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
+        if (ret > 0)
+            wds[i] = ret;
+    }
+
+    /* check for existing devices first, after we have inotify set up */
+    done = find_existing_devices(added_cb, removed_cb, client_data);
+    if (discovery_done_cb)
+        done |= discovery_done_cb(client_data);
+
+    while (!done) {
+        ret = read(context->fd, event_buf, sizeof(event_buf));
+        if (ret >= (int)sizeof(struct inotify_event)) {
+            event = (struct inotify_event *)event_buf;
+            wd = event->wd;
+            if (wd == wds[0]) {
+                i = atoi(event->name);
+                snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name);
+                D("new subdirectory %s: index: %d\n", path, i);
+                if (i > 0 && i < wd_count) {
+                ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
+                if (ret > 0)
+                    wds[i] = ret;
+                }
+            } else {
+                for (i = 1; i < wd_count && !done; i++) {
+                    if (wd == wds[i]) {
+                        snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name);
+                        if (event->mask == IN_CREATE) {
+                            D("new device %s\n", path);
+                            done = added_cb(path, client_data);
+                        } else if (event->mask == IN_DELETE) {
+                            D("gone device %s\n", path);
+                            done = removed_cb(path, client_data);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+struct usb_device *usb_device_open(const char *dev_name)
+{
+    int fd, did_retry = 0, writeable = 1;
+
+    D("usb_device_open %s\n", dev_name);
+
+retry:
+    fd = open(dev_name, O_RDWR);
+    if (fd < 0) {
+        /* if we fail, see if have read-only access */
+        fd = open(dev_name, O_RDONLY);
+        D("usb_device_open open returned %d errno %d\n", fd, errno);
+        if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) {
+            /* work around race condition between inotify and permissions management */
+            sleep(1);
+            did_retry = 1;
+            goto retry;
+        }
+
+        if (fd < 0)
+            return NULL;
+        writeable = 0;
+        D("[ usb open read-only %s fd = %d]\n", dev_name, fd);
+    }
+
+    struct usb_device* result = usb_device_new(dev_name, fd);
+    if (result)
+        result->writeable = writeable;
+    return result;
+}
+
+void usb_device_close(struct usb_device *device)
+{
+    close(device->fd);
+    free(device);
+}
+
+struct usb_device *usb_device_new(const char *dev_name, int fd)
+{
+    struct usb_device *device = calloc(1, sizeof(struct usb_device));
+    int length;
+
+    D("usb_device_new %s fd: %d\n", dev_name, fd);
+
+    if (lseek(fd, 0, SEEK_SET) != 0)
+        goto failed;
+    length = read(fd, device->desc, sizeof(device->desc));
+    D("usb_device_new read returned %d errno %d\n", length, errno);
+    if (length < 0)
+        goto failed;
+
+    strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1);
+    device->fd = fd;
+    device->desc_length = length;
+    // assume we are writeable, since usb_device_get_fd will only return writeable fds
+    device->writeable = 1;
+    return device;
+
+failed:
+    close(fd);
+    free(device);
+    return NULL;
+}
+
+static int usb_device_reopen_writeable(struct usb_device *device)
+{
+    if (device->writeable)
+        return 1;
+
+    int fd = open(device->dev_name, O_RDWR);
+    if (fd >= 0) {
+        close(device->fd);
+        device->fd = fd;
+        device->writeable = 1;
+        return 1;
+    }
+    D("usb_device_reopen_writeable failed errno %d\n", errno);
+    return 0;
+}
+
+int usb_device_get_fd(struct usb_device *device)
+{
+    if (!usb_device_reopen_writeable(device))
+        return -1;
+    return device->fd;
+}
+
+const char* usb_device_get_name(struct usb_device *device)
+{
+    return device->dev_name;
+}
+
+int usb_device_get_unique_id(struct usb_device *device)
+{
+    int bus = 0, dev = 0;
+    sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev);
+    return bus * 1000 + dev;
+}
+
+int usb_device_get_unique_id_from_name(const char* name)
+{
+    int bus = 0, dev = 0;
+    sscanf(name, USB_FS_ID_SCANNER, &bus, &dev);
+    return bus * 1000 + dev;
+}
+
+char* usb_device_get_name_from_unique_id(int id)
+{
+    int bus = id / 1000;
+    int dev = id % 1000;
+    char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT));
+    snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev);
+    return result;
+}
+
+uint16_t usb_device_get_vendor_id(struct usb_device *device)
+{
+    struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
+    return __le16_to_cpu(desc->idVendor);
+}
+
+uint16_t usb_device_get_product_id(struct usb_device *device)
+{
+    struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
+    return __le16_to_cpu(desc->idProduct);
+}
+
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
+{
+    return (struct usb_device_descriptor*)device->desc;
+}
+
+char* usb_device_get_string(struct usb_device *device, int id)
+{
+    char string[256];
+    __u16 buffer[128];
+    __u16 languages[128];
+    int i, result;
+    int languageCount = 0;
+
+    string[0] = 0;
+    memset(languages, 0, sizeof(languages));
+
+    // read list of supported languages
+    result = usb_device_control_transfer(device,
+            USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
+            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0);
+    if (result > 0)
+        languageCount = (result - 2) / 2;
+
+    for (i = 1; i <= languageCount; i++) {
+        memset(buffer, 0, sizeof(buffer));
+
+        result = usb_device_control_transfer(device,
+                USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
+                (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0);
+        if (result > 0) {
+            int i;
+            // skip first word, and copy the rest to the string, changing shorts to bytes.
+            result /= 2;
+            for (i = 1; i < result; i++)
+                string[i - 1] = buffer[i];
+            string[i - 1] = 0;
+            return strdup(string);
+        }
+    }
+
+    return NULL;
+}
+
+char* usb_device_get_manufacturer_name(struct usb_device *device)
+{
+    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+
+    if (desc->iManufacturer)
+        return usb_device_get_string(device, desc->iManufacturer);
+    else
+        return NULL;
+}
+
+char* usb_device_get_product_name(struct usb_device *device)
+{
+    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+
+    if (desc->iProduct)
+        return usb_device_get_string(device, desc->iProduct);
+    else
+        return NULL;
+}
+
+char* usb_device_get_serial(struct usb_device *device)
+{
+    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+
+    if (desc->iSerialNumber)
+        return usb_device_get_string(device, desc->iSerialNumber);
+    else
+        return NULL;
+}
+
+int usb_device_is_writeable(struct usb_device *device)
+{
+    return device->writeable;
+}
+
+void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)
+{
+    iter->config = device->desc;
+    iter->config_end = device->desc + device->desc_length;
+    iter->curr_desc = device->desc;
+}
+
+struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter)
+{
+    struct usb_descriptor_header* next;
+    if (iter->curr_desc >= iter->config_end)
+        return NULL;
+    next = (struct usb_descriptor_header*)iter->curr_desc;
+    iter->curr_desc += next->bLength;
+    return next;
+}
+
+int usb_device_claim_interface(struct usb_device *device, unsigned int interface)
+{
+    return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+}
+
+int usb_device_release_interface(struct usb_device *device, unsigned int interface)
+{
+    return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface);
+}
+
+int usb_device_connect_kernel_driver(struct usb_device *device,
+        unsigned int interface, int connect)
+{
+    struct usbdevfs_ioctl ctl;
+
+    ctl.ifno = interface;
+    ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT);
+    ctl.data = NULL;
+    return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);
+}
+
+int usb_device_control_transfer(struct usb_device *device,
+                            int requestType,
+                            int request,
+                            int value,
+                            int index,
+                            void* buffer,
+                            int length,
+                            unsigned int timeout)
+{
+    struct usbdevfs_ctrltransfer  ctrl;
+
+    // this usually requires read/write permission
+    if (!usb_device_reopen_writeable(device))
+        return -1;
+
+    memset(&ctrl, 0, sizeof(ctrl));
+    ctrl.bRequestType = requestType;
+    ctrl.bRequest = request;
+    ctrl.wValue = value;
+    ctrl.wIndex = index;
+    ctrl.wLength = length;
+    ctrl.data = buffer;
+    ctrl.timeout = timeout;
+    return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);
+}
+
+int usb_device_bulk_transfer(struct usb_device *device,
+                            int endpoint,
+                            void* buffer,
+                            int length,
+                            unsigned int timeout)
+{
+    struct usbdevfs_bulktransfer  ctrl;
+
+    memset(&ctrl, 0, sizeof(ctrl));
+    ctrl.ep = endpoint;
+    ctrl.len = length;
+    ctrl.data = buffer;
+    ctrl.timeout = timeout;
+    return ioctl(device->fd, USBDEVFS_BULK, &ctrl);
+}
+
+struct usb_request *usb_request_new(struct usb_device *dev,
+        const struct usb_endpoint_descriptor *ep_desc)
+{
+    struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb));
+    if (!urb)
+        return NULL;
+
+    if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
+        urb->type = USBDEVFS_URB_TYPE_BULK;
+    else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+        urb->type = USBDEVFS_URB_TYPE_INTERRUPT;
+    else {
+        D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+        free(urb);
+        return NULL;
+    }
+    urb->endpoint = ep_desc->bEndpointAddress;
+
+    struct usb_request *req = calloc(1, sizeof(struct usb_request));
+    if (!req) {
+        free(urb);
+        return NULL;
+    }
+
+    req->dev = dev;
+    req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize);
+    req->private_data = urb;
+    req->endpoint = urb->endpoint;
+    urb->usercontext = req;
+
+    return req;
+}
+
+void usb_request_free(struct usb_request *req)
+{
+    free(req->private_data);
+    free(req);
+}
+
+int usb_request_queue(struct usb_request *req)
+{
+    struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data;
+    int res;
+
+    urb->status = -1;
+    urb->buffer = req->buffer;
+    urb->buffer_length = req->buffer_length;
+
+    do {
+        res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
+    } while((res < 0) && (errno == EINTR));
+
+    return res;
+}
+
+struct usb_request *usb_request_wait(struct usb_device *dev)
+{
+    struct usbdevfs_urb *urb = NULL;
+    struct usb_request *req = NULL;
+    int res;
+
+    while (1) {
+        int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb);
+        D("USBDEVFS_REAPURB returned %d\n", res);
+        if (res < 0) {
+            if(errno == EINTR) {
+                continue;
+            }
+            D("[ reap urb - error ]\n");
+            return NULL;
+        } else {
+            D("[ urb @%p status = %d, actual = %d ]\n",
+                urb, urb->status, urb->actual_length);
+            req = (struct usb_request*)urb->usercontext;
+            req->actual_length = urb->actual_length;
+        }
+        break;
+    }
+    return req;
+}
+
+int usb_request_cancel(struct usb_request *req)
+{
+    struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
+    return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, &urb);
+}
+
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h b/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h
new file mode 100644
index 0000000..9a6b59c
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#ifndef __USB_HOST_H
+#define __USB_HOST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+struct usb_host_context;
+struct usb_endpoint_descriptor;
+
+struct usb_descriptor_iter {
+    unsigned char*  config;
+    unsigned char*  config_end;
+    unsigned char*  curr_desc;
+};
+
+struct usb_request
+{
+    struct usb_device *dev;
+    void* buffer;
+    int buffer_length;
+    int actual_length;
+    int max_packet_size;
+    void *private_data; /* struct usbdevfs_urb* */
+    int endpoint;
+    void *client_data;  /* free for use by client */
+};
+
+/* Callback for notification when new USB devices are attached.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
+
+/* Callback for notification when USB devices are removed.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
+
+/* Callback indicating that initial device discovery is done.
+ * Return true to exit from usb_host_run.
+ */
+typedef int (* usb_discovery_done_cb)(void *client_data);
+
+/* Call this to initialize the USB host library. */
+struct usb_host_context *usb_host_init(void);
+
+/* Call this to cleanup the USB host library. */
+void usb_host_cleanup(struct usb_host_context *context);
+
+/* Call this to monitor the USB bus for new and removed devices.
+ * This is intended to be called from a dedicated thread,
+ * as it will not return until one of the callbacks returns true.
+ * added_cb will be called immediately for each existing USB device,
+ * and subsequently each time a new device is added.
+ * removed_cb is called when USB devices are removed from the bus.
+ * discovery_done_cb is called after the initial discovery of already
+ * connected devices is complete.
+ */
+void usb_host_run(struct usb_host_context *context,
+                  usb_device_added_cb added_cb,
+                  usb_device_removed_cb removed_cb,
+                  usb_discovery_done_cb discovery_done_cb,
+                  void *client_data);
+
+/* Creates a usb_device object for a USB device */
+struct usb_device *usb_device_open(const char *dev_name);
+
+/* Releases all resources associated with the USB device */
+void usb_device_close(struct usb_device *device);
+
+/* Creates a usb_device object for already open USB device */
+struct usb_device *usb_device_new(const char *dev_name, int fd);
+
+/* Returns the file descriptor for the usb_device */
+int usb_device_get_fd(struct usb_device *device);
+
+/* Returns the name for the USB device, which is the same as
+ * the dev_name passed to usb_device_open()
+ */
+const char* usb_device_get_name(struct usb_device *device);
+
+/* Returns a unique ID for the device.
+ *Currently this is generated from the dev_name path.
+ */
+int usb_device_get_unique_id(struct usb_device *device);
+
+/* Returns a unique ID for the device name.
+ * Currently this is generated from the device path.
+ */
+int usb_device_get_unique_id_from_name(const char* name);
+
+/* Returns the device name for the unique ID.
+ * Call free() to deallocate the returned string */
+char* usb_device_get_name_from_unique_id(int id);
+
+/* Returns the USB vendor ID from the device descriptor for the USB device */
+uint16_t usb_device_get_vendor_id(struct usb_device *device);
+
+/* Returns the USB product ID from the device descriptor for the USB device */
+uint16_t usb_device_get_product_id(struct usb_device *device);
+
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
+
+/* Returns a USB descriptor string for the given string ID.
+ * Used to implement usb_device_get_manufacturer_name,
+ * usb_device_get_product_name and usb_device_get_serial.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_string(struct usb_device *device, int id);
+
+/* Returns the manufacturer name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_manufacturer_name(struct usb_device *device);
+
+/* Returns the product name for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_product_name(struct usb_device *device);
+
+/* Returns the USB serial number for the USB device.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_serial(struct usb_device *device);
+
+/* Returns true if we have write access to the USB device,
+ * and false if we only have access to the USB device configuration.
+ */
+int usb_device_is_writeable(struct usb_device *device);
+
+/* Initializes a usb_descriptor_iter, which can be used to iterate through all
+ * the USB descriptors for a USB device.
+ */
+void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);
+
+/* Returns the next USB descriptor for a device, or NULL if we have reached the
+ * end of the list.
+ */
+struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);
+
+/* Claims the specified interface of a USB device */
+int usb_device_claim_interface(struct usb_device *device, unsigned int interface);
+
+/* Releases the specified interface of a USB device */
+int usb_device_release_interface(struct usb_device *device, unsigned int interface);
+
+/* Requests the kernel to connect or disconnect its driver for the specified interface.
+ * This can be used to ask the kernel to disconnect its driver for a device
+ * so usb_device_claim_interface can claim it instead.
+ */
+int usb_device_connect_kernel_driver(struct usb_device *device,
+        unsigned int interface, int connect);
+
+/* Sends a control message to the specified device on endpoint zero */
+int usb_device_control_transfer(struct usb_device *device,
+                            int requestType,
+                            int request,
+                            int value,
+                            int index,
+                            void* buffer,
+                            int length,
+                            unsigned int timeout);
+
+/* Reads or writes on a bulk endpoint.
+ * Returns number of bytes transferred, or negative value for error.
+ */
+int usb_device_bulk_transfer(struct usb_device *device,
+                            int endpoint,
+                            void* buffer,
+                            int length,
+                            unsigned int timeout);
+
+/* Creates a new usb_request. */
+struct usb_request *usb_request_new(struct usb_device *dev,
+        const struct usb_endpoint_descriptor *ep_desc);
+
+/* Releases all resources associated with the request */
+void usb_request_free(struct usb_request *req);
+
+/* Submits a read or write request on the specified device */
+int usb_request_queue(struct usb_request *req);
+
+ /* Waits for the results of a previous usb_request_queue operation.
+  * Returns a usb_request, or NULL for error.
+  */
+struct usb_request *usb_request_wait(struct usb_device *dev);
+
+/* Cancels a pending usb_request_queue() operation. */
+int usb_request_cancel(struct usb_request *req);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __USB_HOST_H */
diff --git a/libs/usb/tests/AccessoryChat/res/layout/accessory_chat.xml b/libs/usb/tests/AccessoryChat/res/layout/accessory_chat.xml
new file mode 100644
index 0000000..596ecbf
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/res/layout/accessory_chat.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+    <ScrollView android:id="@+id/scroll"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        >
+        <TextView android:id="@+id/log"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="25dp"
+            android:textSize="12sp"
+            android:textColor="#ffffffff"
+            />
+    </ScrollView>
+
+    <EditText android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:capitalize="sentences"
+        android:autoText="true"
+        android:singleLine="true"
+        />
+
+</LinearLayout>
+
+
diff --git a/libs/usb/tests/AccessoryChat/res/xml/accessory_filter.xml b/libs/usb/tests/AccessoryChat/res/xml/accessory_filter.xml
new file mode 100644
index 0000000..588946f
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/res/xml/accessory_filter.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+    <usb-accessory manufacturer="Google, Inc." model="AccessoryChat" type="Sample Program" version="1.0" />
+</resources>
diff --git a/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
new file mode 100644
index 0000000..5cf02c7
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
@@ -0,0 +1,153 @@
+/*
+ * 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.accessorychat;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.future.usb.UsbAccessory;
+import com.android.future.usb.UsbManager;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class AccessoryChat extends Activity implements Runnable, TextView.OnEditorActionListener {
+
+    private static final String TAG = "AccessoryChat";
+    TextView mLog;
+    EditText mEditText;
+    ParcelFileDescriptor mFileDescriptor;
+    FileInputStream mInputStream;
+    FileOutputStream mOutputStream;
+
+    private static final int MESSAGE_LOG = 1;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.accessory_chat);
+        mLog = (TextView)findViewById(R.id.log);
+        mEditText = (EditText)findViewById(R.id.message);
+        mEditText.setOnEditorActionListener(this);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        Intent intent = getIntent();
+        Log.d(TAG, "intent: " + intent);
+        UsbManager manager = UsbManager.getInstance();
+        UsbAccessory[] accessories = manager.getAccessoryList();
+        UsbAccessory accessory = (accessories == null ? null : accessories[0]);
+        if (accessory != null) {
+            mFileDescriptor = manager.openAccessory(accessory);
+            if (mFileDescriptor != null) {
+                FileDescriptor fd = mFileDescriptor.getFileDescriptor();
+                mInputStream = new FileInputStream(fd);
+                mOutputStream = new FileOutputStream(fd);
+                Thread thread = new Thread(null, this, "AccessoryChat");
+                thread.start();
+            } else {
+                Log.d(TAG, "openAccessory fail");
+            }
+        } else {
+            Log.d(TAG, "mAccessory is null");
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mFileDescriptor != null) {
+            try {
+                mFileDescriptor.close();
+            } catch (IOException e) {
+            } finally {
+                mFileDescriptor = null;
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+       super.onDestroy();
+    }
+
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_DONE && mOutputStream != null) {
+            try {
+                mOutputStream.write(v.getText().toString().getBytes());
+            } catch (IOException e) {
+                Log.e(TAG, "write failed", e);
+            }
+            v.setText("");
+            return true;
+        }
+        Log.d(TAG, "onEditorAction " + actionId + " event: " + event);
+        return false;
+    }
+
+    public void run() {
+        int ret = 0;
+        byte[] buffer = new byte[16384];
+        while (ret >= 0) {
+            try {
+                ret = mInputStream.read(buffer);
+            } catch (IOException e) {
+                break;
+            }
+
+            if (ret > 0) {
+                Message m = Message.obtain(mHandler, MESSAGE_LOG);
+                String text = new String(buffer, 0, ret);
+                Log.d(TAG, "chat: " + text);
+                m.obj = text;
+                mHandler.sendMessage(m);
+            }
+        }
+        Log.d(TAG, "thread out");
+    }
+
+   Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_LOG:
+                    mLog.setText(mLog.getText() + "\n" + (String)msg.obj);
+                    break;
+             }
+        }
+    };
+}
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 5c52783..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.Usb;
+import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -83,7 +83,7 @@
     private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Usb.ACTION_USB_STATE)) {
+            if (intent.getAction().equals(UsbManager.ACTION_USB_STATE)) {
                 handleUsbStateChanged(intent);
             }
         }
@@ -175,7 +175,7 @@
         super.onResume();
 
         mStorageManager.registerListener(mStorageListener);
-        registerReceiver(mUsbStateReceiver, new IntentFilter(Usb.ACTION_USB_STATE));
+        registerReceiver(mUsbStateReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE));
         try {
             mAsyncStorageHandler.post(new Runnable() {
                 @Override
@@ -199,7 +199,7 @@
     }
 
     private void handleUsbStateChanged(Intent intent) {
-        boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+        boolean connected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
         if (!connected) {
             // It was disconnected from the plug, so finish
             finish();
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index e8c1613..b26dac72 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.Usb;
+import android.hardware.usb.UsbManager;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BatteryManager;
@@ -352,14 +352,12 @@
                     mBatteryFull = batteryFull;
                     updateLights();
                 }
-            } else if (action.equals(Usb.ACTION_USB_STATE)) {
+            } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
                 Bundle extras = intent.getExtras();
-                boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
-                boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
-                                    extras.getString(Usb.USB_FUNCTION_ADB)));
+                boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
+                boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
+                                    extras.getString(UsbManager.USB_FUNCTION_ADB)));
                 updateAdbNotification(usbConnected && adbEnabled);
-            } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) {
-                updateAdbNotification(false);
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
@@ -464,7 +462,7 @@
         // register for battery changed notifications
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Usb.ACTION_USB_STATE);
+        filter.addAction(UsbManager.ACTION_USB_STATE);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index df69b76..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;
@@ -122,7 +123,7 @@
         BluetoothA2dpService bluetoothA2dp = null;
         HeadsetObserver headset = null;
         DockObserver dock = null;
-        UsbObserver usb = null;
+        UsbService usb = null;
         UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
         ThrottleService throttle = null;
@@ -397,11 +398,12 @@
             }
 
             try {
-                Slog.i(TAG, "USB Observer");
+                Slog.i(TAG, "USB Service");
                 // Listen for USB changes
-                usb = new UsbObserver(context);
+                usb = new UsbService(context);
+                ServiceManager.addService(Context.USB_SERVICE, usb);
             } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting UsbObserver", e);
+                Slog.e(TAG, "Failure starting UsbService", e);
             }
 
             try {
@@ -493,7 +495,7 @@
         final BatteryService batteryF = battery;
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
-        final UsbObserver usbF = usb;
+        final UsbService usbF = usb;
         final ThrottleService throttleF = throttle;
         final UiModeManagerService uiModeF = uiMode;
         final AppWidgetService appWidgetF = appWidget;
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
deleted file mode 100644
index d08fe9b..0000000
--- a/services/java/com/android/server/UsbObserver.java
+++ /dev/null
@@ -1,207 +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.Usb;
-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>UsbObserver monitors for changes to USB state.
- */
-class UsbObserver extends UEventObserver {
-    private static final String TAG = UsbObserver.class.getSimpleName();
-    private static final boolean LOG = false;
-
-    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_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;
-
-    private int mUsbConfig = 0;
-    private int mPreviousUsbConfig = 0;
-
-    // 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 PowerManagerService mPowerManager;
-
-    public UsbObserver(Context context) {
-        mContext = context;
-        init();  // set initial status
-
-        startObserving(USB_CONFIGURATION_MATCH);
-        startObserving(USB_FUNCTIONS_MATCH);
-    }
-
-    @Override
-    public void onUEvent(UEventObserver.UEvent event) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Slog.v(TAG, "USB UEVENT: " + event.toString());
-        }
-
-        synchronized (this) {
-            String switchState = event.get("SWITCH_STATE");
-            if (switchState != null) {
-                try {
-                    int newConfig = Integer.parseInt(switchState);
-                    if (newConfig != mUsbConfig) {
-                        mPreviousUsbConfig = mUsbConfig;
-                        mUsbConfig = newConfig;
-                        // trigger an Intent broadcast
-                        if (mSystemReady) {
-                            update();
-                        }
-                    }
-                } 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);
-                    }
-                }
-            }
-        }
-    }
-    private final void init() {
-        char[] buffer = new char[1024];
-
-        try {
-            FileReader file = new FileReader(USB_CONFIGURATION_PATH);
-            int len = file.read(buffer, 0, 1024);
-            mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
-
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "This kernel does not have USB configuration switch support");
-        } catch (Exception e) {
-            Slog.e(TAG, "" , e);
-        }
-
-        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);
-                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();
-            mSystemReady = true;
-        }
-    }
-
-    private final void update() {
-        mHandler.sendEmptyMessage(MSG_UPDATE);
-    }
-
-    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), Usb.USB_FUNCTION_ENABLED);
-            }
-            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
-            }
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE:
-                    synchronized (this) {
-                        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;
-                        }
-                        // Send an Intent containing connected/disconnected state
-                        // and the enabled/disabled state of all USB functions
-                        Intent intent;
-                        boolean usbConnected = (mUsbConfig != 0);
-                        if (usbConnected) {
-                            intent = new Intent(Usb.ACTION_USB_CONNECTED);
-                            addEnabledFunctions(intent);
-                        } else {
-                            intent = new Intent(Usb.ACTION_USB_DISCONNECTED);
-                        }
-                        mContext.sendBroadcast(intent);
-
-                        // send a sticky broadcast for clients interested in both connect and disconnect
-                        intent = new Intent(Usb.ACTION_USB_STATE);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                        intent.putExtra(Usb.USB_CONNECTED, usbConnected);
-                        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 a73a4ce..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.Usb;
+import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.InterfaceConfiguration;
 import android.net.IConnectivityManager;
@@ -111,14 +111,6 @@
     private boolean mUsbMassStorageOff;  // track the status of USB Mass Storage
     private boolean mUsbConnected;       // track the status of USB connection
 
-    // mUsbHandler message
-    static final int USB_STATE_CHANGE = 1;
-    static final int USB_DISCONNECTED = 0;
-    static final int USB_CONNECTED = 1;
-
-    // Time to delay before processing USB disconnect events
-    static final long USB_DISCONNECT_DELAY = 1000;
-
     public Tethering(Context context, Looper looper) {
         mContext = context;
         mLooper = looper;
@@ -143,7 +135,7 @@
 
         mStateReceiver = new StateReceiver();
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Usb.ACTION_USB_STATE);
+        filter.addAction(UsbManager.ACTION_USB_STATE);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(Intent.ACTION_BOOT_COMPLETED);
         mContext.registerReceiver(mStateReceiver, filter);
@@ -429,25 +421,12 @@
         }
     }
 
-    private Handler mUsbHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            mUsbConnected = (msg.arg1 == USB_CONNECTED);
-            updateUsbStatus();
-        }
-    };
-
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(Usb.ACTION_USB_STATE)) {
-                // process connect events immediately, but delay handling disconnects
-                // to debounce USB configuration changes
-                boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
-                Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
-                        (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
-                mUsbHandler.removeMessages(USB_STATE_CHANGE);
-                mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
+            if (action.equals(UsbManager.ACTION_USB_STATE)) {
+                mUsbConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+                updateUsbStatus();
             } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
                 mUsbMassStorageOff = false;
                 updateUsbStatus();
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..0cb788e
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -0,0 +1,554 @@
+/*
+ * 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));
+        }
+
+        int count = matches.size();
+        // don't show the resolver activity if there are no choices available
+        if (count == 0) return;
+
+        if (defaultPackage != null) {
+            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..6825f19
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -0,0 +1,420 @@
+/*
+ * 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;
+    // USB functions that are enabled by default, to restore after exiting accessory mode
+    private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private final UsbDeviceSettingsManager mDeviceManager;
+    private final boolean 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 (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);
+                    // adb is enabled/disabled automatically by the adbd daemon,
+                    // so don't treat it as a default function
+                    if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
+                        mDefaultFunctions.add(functionName);
+                    }
+                } else {
+                    mDisabledFunctions.add(functionName);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "This kernel does not have USB composite class support");
+        } catch (Exception e) {
+            Slog.e(TAG, "" , e);
+        }
+    }
+
+    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) {
+                                // make sure accessory mode is off, and restore default functions
+                                if (UsbManager.setFunctionEnabled(
+                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+                                    Log.d(TAG, "exited USB accessory mode");
+
+                                    int count = mDefaultFunctions.size();
+                                    for (int i = 0; i < count; i++) {
+                                        String function = mDefaultFunctions.get(i);
+                                        if (UsbManager.setFunctionEnabled(function, true)) {
+                                            Log.e(TAG, "could not reenable function " + function);
+                                        }
+                                    }
+                                }
+
+                                if (mCurrentAccessory != null) {
+                                    mDeviceManager.accessoryDetached(mCurrentAccessory);
+                                    mCurrentAccessory = null;
+                                }
+                            }
+
+                            final ContentResolver cr = mContext.getContentResolver();
+                            if (Settings.Secure.getInt(cr,
+                                    Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+                                Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+                                return;
+                            }
+
+                            mLastConnected = mConnected;
+                            mLastConfiguration = mConfiguration;
+
+                            // send a sticky broadcast containing current USB state
+                            Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
+                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
+                            addEnabledFunctionsLocked(intent);
+                            mContext.sendStickyBroadcast(intent);
+                        }
+                        break;
+                    case MSG_FUNCTION_ENABLED:
+                    case MSG_FUNCTION_DISABLED:
+                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
+                        break;
+                }
+            }
+        }
+    };
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != 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.print("    Default Functions: ");
+            for (int i = 0; i < mDefaultFunctions.size(); i++) {
+                pw.print(mDefaultFunctions.get(i) + " ");
+            }
+            pw.println("");
+            pw.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
+
+            mDeviceManager.dump(fd, pw);
+        }
+    }
+
+    // accessory support
+    private native String[] nativeGetAccessoryStrings();
+    private native ParcelFileDescriptor nativeOpenAccessory();
+}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index c90879d..de8f158 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -8,6 +8,7 @@
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
     com_android_server_SystemServer.cpp \
+    com_android_server_UsbService.cpp \
     com_android_server_VibratorService.cpp \
 	com_android_server_location_GpsLocationProvider.cpp \
     onload.cpp
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
new file mode 100644
index 0000000..08a526e
--- /dev/null
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbService"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#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");
+
+   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));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index cd4f0a4..613683b 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -9,6 +9,7 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_UsbService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -32,6 +33,7 @@
     register_android_server_LightsService(env);
     register_android_server_AlarmManagerService(env);
     register_android_server_BatteryService(env);
+    register_android_server_UsbService(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
diff --git a/tools/layoutlib/.gitignore b/tools/layoutlib/.gitignore
index 0ec5000..c5e82d7 100644
--- a/tools/layoutlib/.gitignore
+++ b/tools/layoutlib/.gitignore
@@ -1,2 +1 @@
-bridge/bin
-create/bin
+bin
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 5cfda29..9fb000e 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -1,9 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
-	<classpathentry kind="src" path="tests"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index 7e70f33..687a91f 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -33,3 +33,6 @@
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/tools/layoutlib/bridge/tests/.classpath b/tools/layoutlib/bridge/tests/.classpath
new file mode 100644
index 0000000..9cc2433d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_bridge"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/bridge/tests/.project b/tools/layoutlib/bridge/tests/.project
new file mode 100644
index 0000000..2325eed
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_bridge-tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
new file mode 100644
index 0000000..e303638
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := layoutlib-tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := layoutlib kxml2-2.3.0 junit
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
deleted file mode 100644
index a3219e7..0000000
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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.layoutlib.bridge;
-
-import com.android.ninepatch.NinePatch;
-
-import java.net.URL;
-
-import junit.framework.TestCase;
-
-public class NinePatchTest extends TestCase {
-
-    private NinePatch mPatch;
-
-    @Override
-    protected void setUp() throws Exception {
-        URL url = this.getClass().getClassLoader().getResource(
-                "com/android/layoutlib/testdata/button.9.png");
-
-        mPatch = NinePatch.load(url, false /* convert */);
-    }
-
-    public void test9PatchLoad() throws Exception {
-        assertNotNull(mPatch);
-    }
-
-    public void test9PatchMinSize() {
-        int[] padding = new int[4];
-        mPatch.getPadding(padding);
-        assertEquals(13, padding[0]);
-        assertEquals(3, padding[1]);
-        assertEquals(13, padding[2]);
-        assertEquals(4, padding[3]);
-        assertEquals(36, mPatch.getWidth());
-        assertEquals(25, mPatch.getHeight());
-    }
-}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/button.9.png b/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/button.9.png
deleted file mode 100644
index 9d52f40..0000000
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/button.9.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
similarity index 81%
rename from tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
rename to tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
index ba3c51a..ec4edac 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
+++ b/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
-
-import android.graphics.Matrix;
-import android.text.TextPaint;
+package android.graphics;
 
 import junit.framework.TestCase;
 
 /**
  *
  */
-public class AndroidGraphicsTests extends TestCase {
+public class Matrix_DelegateTest extends TestCase {
 
     @Override
     protected void setUp() throws Exception {
@@ -36,14 +33,17 @@
         super.tearDown();
     }
 
-    public void testMatrix() {
+    public void testIdentity() {
         Matrix m1 = new Matrix();
 
         assertTrue(m1.isIdentity());
 
         m1.setValues(new float[] { 1,0,0, 0,1,0, 0,0,1 });
         assertTrue(m1.isIdentity());
+    }
 
+    public void testCopyConstructor() {
+        Matrix m1 = new Matrix();
         Matrix m2 = new Matrix(m1);
 
         float[] v1 = new float[9];
@@ -55,9 +55,4 @@
             assertEquals(v1[i], v2[i]);
         }
     }
-
-    public void textTextPaint() {
-        TextPaint p = new TextPaint();
-        assertNotNull(p);
-    }
 }
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
similarity index 100%
rename from tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
rename to tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
similarity index 100%
rename from tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
rename to tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/layout1.xml b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml
similarity index 100%
rename from tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/layout1.xml
rename to tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml