Merge "Avoid JUnit4 collision in android-support-test, mockito-target"
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 2fd6740..eb07c05 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -6,7 +6,9 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test apct-perftests-utils
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    apct-perftests-utils
 
 LOCAL_PACKAGE_NAME := CorePerfTests
 
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index ecb8d95..815bc80 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.core.frameworks.perftests">
+    package="android.core.perftests">
 
     <application>
         <uses-library android:name="android.test.runner" />
@@ -8,6 +8,6 @@
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.core.frameworks.perftests"/>
+        android:targetPackage="android.core.perftests"/>
 
 </manifest>
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index d444c92..a8d1f26 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -27,7 +27,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.core.frameworks.perftests.R;
+import android.core.perftests.R;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/graphics/Android.mk b/apct-tests/perftests/graphics/Android.mk
index afdd743..cba35f3 100644
--- a/apct-tests/perftests/graphics/Android.mk
+++ b/apct-tests/perftests/graphics/Android.mk
@@ -3,11 +3,13 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := apct-perftests-utils android-support-test
-
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    apct-perftests-utils
+
 LOCAL_PACKAGE_NAME := GraphicsPerfTests
 
 include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/graphics/AndroidManifest.xml b/apct-tests/perftests/graphics/AndroidManifest.xml
index 5416458..6c8bc07 100644
--- a/apct-tests/perftests/graphics/AndroidManifest.xml
+++ b/apct-tests/perftests/graphics/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.frameworks.perftests">
+    package="android.graphics.perftests">
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
@@ -16,6 +16,6 @@
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.frameworks.perftests"/>
+        android:targetPackage="android.graphics.perftests"/>
 
 </manifest>
diff --git a/apct-tests/perftests/graphics/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/graphics/src/android/graphics/perftests/RenderNodePerfTest.java
new file mode 100644
index 0000000..19047d3
--- /dev/null
+++ b/apct-tests/perftests/graphics/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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.graphics.perftests;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class RenderNodePerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testMeasureRenderNodeJniOverhead() {
+        RenderNode node = RenderNode.create("benchmark", null);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            node.setTranslationX(1.0f);
+        }
+    }
+}
diff --git a/apct-tests/perftests/graphics/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/graphics/src/android/graphics/perftests/VectorDrawablePerfTest.java
index 2af3b04..a15be2e 100644
--- a/apct-tests/perftests/graphics/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/graphics/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -30,7 +30,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import com.android.frameworks.perftests.R;
+import android.graphics.perftests.R;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/misc/Android.mk b/apct-tests/perftests/misc/Android.mk
new file mode 100644
index 0000000..db5d643
--- /dev/null
+++ b/apct-tests/perftests/misc/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    apct-perftests-utils
+
+LOCAL_PACKAGE_NAME := MiscPerfTests
+
+include $(BUILD_PACKAGE)
+
diff --git a/apct-tests/perftests/misc/AndroidManifest.xml b/apct-tests/perftests/misc/AndroidManifest.xml
new file mode 100644
index 0000000..9d9d5f4
--- /dev/null
+++ b/apct-tests/perftests/misc/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.misc.perftests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.perftests.utils.StubActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.misc.perftests"/>
+
+</manifest>
diff --git a/apct-tests/perftests/misc/src/android/util/perftests/LogPerfTest.java b/apct-tests/perftests/misc/src/android/util/perftests/LogPerfTest.java
new file mode 100644
index 0000000..07cd33f
--- /dev/null
+++ b/apct-tests/perftests/misc/src/android/util/perftests/LogPerfTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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.util.perftests;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LogPerfTest {
+
+    private final String[] strings = new String[] {
+            "This is a test log string 1",
+            "This is a test log string 2",
+            "This is a test log string 3",
+            "This is a test log string 4",
+            "This is a test log string 5",
+    };
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testLogPerf() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int i = 0;
+        while (state.keepRunning()) {
+            Log.d("LogPerfTest", strings[(i++) % strings.length]);
+        }
+    }
+}
diff --git a/apct-tests/perftests/misc/src/java/lang/perftests/SystemPerfTest.java b/apct-tests/perftests/misc/src/java/lang/perftests/SystemPerfTest.java
new file mode 100644
index 0000000..6a49c03
--- /dev/null
+++ b/apct-tests/perftests/misc/src/java/lang/perftests/SystemPerfTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 java.lang.perftests;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SystemPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testNanoTimePerf() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.nanoTime();
+        }
+    }
+}
diff --git a/api/system-current.txt b/api/system-current.txt
index 3e47ac7..3197322 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15944,6 +15944,7 @@
     method public java.lang.String getSerial();
     method public boolean releaseInterface(android.hardware.usb.UsbInterface);
     method public android.hardware.usb.UsbRequest requestWait();
+    method public boolean resetDevice();
     method public boolean setConfiguration(android.hardware.usb.UsbConfiguration);
     method public boolean setInterface(android.hardware.usb.UsbInterface);
   }
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 6e4c9de..fafe116 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -17,6 +17,7 @@
 package android.hardware.usb;
 
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbPort;
@@ -116,4 +117,7 @@
 
     /* Sets the port's current role. */
     void setPortRoles(in String portId, int powerRole, int dataRole);
+
+   /* Sets USB device connection handler. */
+   void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
 }
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index c062b3a..54fea52 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -16,6 +16,7 @@
 
 package android.hardware.usb;
 
+import android.annotation.SystemApi;
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileDescriptor;
@@ -215,8 +216,20 @@
     }
 
     /**
+     * Reset USB port for the connected device.
+     *
+     * @return true if reset succeeds.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean resetDevice() {
+        return native_reset_device();
+    }
+
+    /**
      * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation
-     * Note that this may return requests queued on multiple 
+     * Note that this may return requests queued on multiple
      * {@link android.hardware.usb.UsbEndpoint}s.
      * When multiple endpoints are in use, {@link android.hardware.usb.UsbRequest#getEndpoint} and
      * {@link android.hardware.usb.UsbRequest#getClientData} can be useful in determining
@@ -263,4 +276,5 @@
             int offset, int length, int timeout);
     private native UsbRequest native_request_wait();
     private native String native_get_serial();
+    private native boolean native_reset_device();
 }
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 629db06..df4785e 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -19,7 +19,9 @@
 
 import com.android.internal.util.Preconditions;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
@@ -469,8 +471,20 @@
      * {@hide}
      */
     public void grantPermission(UsbDevice device) {
+        grantPermission(device, Process.myUid());
+    }
+
+    /**
+     * Grants permission for USB device to given uid without showing system dialog.
+     * Only system components can call this function.
+     * @param device to request permissions for
+     * @uid uid to give permission
+     *
+     * {@hide}
+     */
+    public void grantPermission(UsbDevice device, int uid) {
         try {
-            mService.grantDevicePermission(device, Process.myUid());
+            mService.grantDevicePermission(device, uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -488,11 +502,9 @@
         try {
             int uid = mContext.getPackageManager()
                 .getPackageUidAsUser(packageName, mContext.getUserId());
-            mService.grantDevicePermission(device, uid);
+            grantPermission(device, uid);
         } catch (NameNotFoundException e) {
             Log.e(TAG, "Package " + packageName + " not found.", e);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -631,6 +643,26 @@
         }
     }
 
+    /**
+     * Sets the component that will handle USB device connection.
+     * <p>
+     * Setting component allows to specify external USB host manager to handle use cases, where
+     * selection dialog for an activity that will handle USB device is undesirable.
+     * Only system components can call this function, as it requires the MANAGE_USB permission.
+     *
+     * @param usbDeviceConnectionHandler The component to handle usb connections,
+     * {@code null} to unset.
+     *
+     * {@hide}
+     */
+    public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
+        try {
+            mService.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String addFunction(String functions, String function) {
         if (USB_FUNCTION_NONE.equals(functions)) {
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index fe7cdcc..52579e5 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -44,6 +44,7 @@
     }
 
     public native final void writeInterfaceToken(String interfaceName);
+    public native final void writeBool(boolean val);
     public native final void writeInt8(byte val);
     public native final void writeInt16(short val);
     public native final void writeInt32(int val);
@@ -52,6 +53,8 @@
     public native final void writeDouble(double val);
     public native final void writeString(String val);
 
+    public native final void writeBoolArray(int size, boolean[] val);
+    public native final void writeBoolVector(boolean[] val);
     public native final void writeInt8Array(int size, byte[] val);
     public native final void writeInt8Vector(byte[] val);
     public native final void writeInt16Array(int size, short[] val);
@@ -70,6 +73,7 @@
     public native final void writeStrongBinder(IHwBinder binder);
 
     public native final void enforceInterface(String interfaceName);
+    public native final boolean readBool();
     public native final byte readInt8();
     public native final short readInt16();
     public native final int readInt32();
@@ -78,6 +82,8 @@
     public native final double readDouble();
     public native final String readString();
 
+    public native final boolean[] readBoolArray(int size);
+    public native final boolean[] readBoolVector();
     public native final byte[] readInt8Array(int size);
     public native final byte[] readInt8Vector();
     public native final short[] readInt16Array(int size);
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index bce5ec1..51d818b 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -24,6 +24,10 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
 
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * <p>A display list records a series of graphics related operations and can replay
  * them later. Display lists are usually built by recording operations on a
@@ -128,6 +132,12 @@
  */
 public class RenderNode {
 
+ // Use a Holder to allow static initialization in the boot image.
+    private static class NoImagePreloadHolder {
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
+    }
+
     private boolean mValid;
     // Do not access directly unless you are ThreadedRenderer
     final long mNativeRenderNode;
@@ -135,6 +145,7 @@
 
     private RenderNode(String name, View owningView) {
         mNativeRenderNode = nCreate(name);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
         mOwningView = owningView;
         if (mOwningView instanceof SurfaceView) {
             nRequestPositionUpdates(mNativeRenderNode, (SurfaceView) mOwningView);
@@ -145,6 +156,7 @@
      * @see RenderNode#adopt(long)
      */
     private RenderNode(long nativePtr) {
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, nativePtr);
         mNativeRenderNode = nativePtr;
         mOwningView = null;
     }
@@ -812,99 +824,143 @@
     // Intentionally not static because it acquires a reference to 'this'
     private native long nCreate(String name);
 
-    private static native void nDestroyRenderNode(long renderNode);
+    private static native long nGetNativeFinalizer();
     private static native void nSetDisplayList(long renderNode, long newData);
-
-    // Matrix
-
-    private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
-    private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
-    private static native boolean nHasIdentityMatrix(long renderNode);
-
-    // Properties
-
-    private static native boolean nOffsetTopAndBottom(long renderNode, int offset);
-    private static native boolean nOffsetLeftAndRight(long renderNode, int offset);
-    private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
-            int right, int bottom);
-    private static native boolean nSetBottom(long renderNode, int bottom);
-    private static native boolean nSetRight(long renderNode, int right);
-    private static native boolean nSetTop(long renderNode, int top);
-    private static native boolean nSetLeft(long renderNode, int left);
-    private static native boolean nSetCameraDistance(long renderNode, float distance);
-    private static native boolean nSetPivotY(long renderNode, float pivotY);
-    private static native boolean nSetPivotX(long renderNode, float pivotX);
-    private static native boolean nSetLayerType(long renderNode, int layerType);
-    private static native boolean nSetLayerPaint(long renderNode, long paint);
-    private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
-    private static native boolean nSetClipBounds(long renderNode, int left, int top,
-            int right, int bottom);
-    private static native boolean nSetClipBoundsEmpty(long renderNode);
-    private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
-    private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
-    private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
-            int right, int bottom, float radius, float alpha);
-    private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
-            float alpha);
-    private static native boolean nSetOutlineEmpty(long renderNode);
-    private static native boolean nSetOutlineNone(long renderNode);
-    private static native boolean nHasShadow(long renderNode);
-    private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
-    private static native boolean nSetRevealClip(long renderNode,
-            boolean shouldClip, float x, float y, float radius);
-    private static native boolean nSetAlpha(long renderNode, float alpha);
-    private static native boolean nSetHasOverlappingRendering(long renderNode,
-            boolean hasOverlappingRendering);
-    private static native boolean nSetElevation(long renderNode, float lift);
-    private static native boolean nSetTranslationX(long renderNode, float translationX);
-    private static native boolean nSetTranslationY(long renderNode, float translationY);
-    private static native boolean nSetTranslationZ(long renderNode, float translationZ);
-    private static native boolean nSetRotation(long renderNode, float rotation);
-    private static native boolean nSetRotationX(long renderNode, float rotationX);
-    private static native boolean nSetRotationY(long renderNode, float rotationY);
-    private static native boolean nSetScaleX(long renderNode, float scaleX);
-    private static native boolean nSetScaleY(long renderNode, float scaleY);
-    private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
-    private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
-
-    private static native boolean nHasOverlappingRendering(long renderNode);
-    private static native boolean nGetClipToOutline(long renderNode);
-    private static native float nGetAlpha(long renderNode);
-    private static native float nGetCameraDistance(long renderNode);
-    private static native float nGetScaleX(long renderNode);
-    private static native float nGetScaleY(long renderNode);
-    private static native float nGetElevation(long renderNode);
-    private static native float nGetTranslationX(long renderNode);
-    private static native float nGetTranslationY(long renderNode);
-    private static native float nGetTranslationZ(long renderNode);
-    private static native float nGetRotation(long renderNode);
-    private static native float nGetRotationX(long renderNode);
-    private static native float nGetRotationY(long renderNode);
-    private static native boolean nIsPivotExplicitlySet(long renderNode);
-    private static native float nGetPivotX(long renderNode);
-    private static native float nGetPivotY(long renderNode);
     private static native void nOutput(long renderNode);
     private static native int nGetDebugSize(long renderNode);
-
     private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback);
 
-    ///////////////////////////////////////////////////////////////////////////
     // Animations
-    ///////////////////////////////////////////////////////////////////////////
 
     private static native void nAddAnimator(long renderNode, long animatorPtr);
     private static native void nEndAllAnimators(long renderNode);
 
     ///////////////////////////////////////////////////////////////////////////
-    // Finalization
+    // Fast native methods
     ///////////////////////////////////////////////////////////////////////////
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            nDestroyRenderNode(mNativeRenderNode);
-        } finally {
-            super.finalize();
-        }
-    }
+    // Matrix
+
+    @FastNative
+    private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
+    @FastNative
+    private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
+    @FastNative
+    private static native boolean nHasIdentityMatrix(long renderNode);
+
+    // Properties
+
+    @FastNative
+    private static native boolean nOffsetTopAndBottom(long renderNode, int offset);
+    @FastNative
+    private static native boolean nOffsetLeftAndRight(long renderNode, int offset);
+    @FastNative
+    private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
+            int right, int bottom);
+    @FastNative
+    private static native boolean nSetBottom(long renderNode, int bottom);
+    @FastNative
+    private static native boolean nSetRight(long renderNode, int right);
+    @FastNative
+    private static native boolean nSetTop(long renderNode, int top);
+    @FastNative
+    private static native boolean nSetLeft(long renderNode, int left);
+    @FastNative
+    private static native boolean nSetCameraDistance(long renderNode, float distance);
+    @FastNative
+    private static native boolean nSetPivotY(long renderNode, float pivotY);
+    @FastNative
+    private static native boolean nSetPivotX(long renderNode, float pivotX);
+    @FastNative
+    private static native boolean nSetLayerType(long renderNode, int layerType);
+    @FastNative
+    private static native boolean nSetLayerPaint(long renderNode, long paint);
+    @FastNative
+    private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
+    @FastNative
+    private static native boolean nSetClipBounds(long renderNode, int left, int top,
+            int right, int bottom);
+    @FastNative
+    private static native boolean nSetClipBoundsEmpty(long renderNode);
+    @FastNative
+    private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
+    @FastNative
+    private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+    @FastNative
+    private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
+            int right, int bottom, float radius, float alpha);
+    @FastNative
+    private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
+            float alpha);
+    @FastNative
+    private static native boolean nSetOutlineEmpty(long renderNode);
+    @FastNative
+    private static native boolean nSetOutlineNone(long renderNode);
+    @FastNative
+    private static native boolean nHasShadow(long renderNode);
+    @FastNative
+    private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
+    @FastNative
+    private static native boolean nSetRevealClip(long renderNode,
+            boolean shouldClip, float x, float y, float radius);
+    @FastNative
+    private static native boolean nSetAlpha(long renderNode, float alpha);
+    @FastNative
+    private static native boolean nSetHasOverlappingRendering(long renderNode,
+            boolean hasOverlappingRendering);
+    @FastNative
+    private static native boolean nSetElevation(long renderNode, float lift);
+    @FastNative
+    private static native boolean nSetTranslationX(long renderNode, float translationX);
+    @FastNative
+    private static native boolean nSetTranslationY(long renderNode, float translationY);
+    @FastNative
+    private static native boolean nSetTranslationZ(long renderNode, float translationZ);
+    @FastNative
+    private static native boolean nSetRotation(long renderNode, float rotation);
+    @FastNative
+    private static native boolean nSetRotationX(long renderNode, float rotationX);
+    @FastNative
+    private static native boolean nSetRotationY(long renderNode, float rotationY);
+    @FastNative
+    private static native boolean nSetScaleX(long renderNode, float scaleX);
+    @FastNative
+    private static native boolean nSetScaleY(long renderNode, float scaleY);
+    @FastNative
+    private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
+    @FastNative
+    private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
+
+    @FastNative
+    private static native boolean nHasOverlappingRendering(long renderNode);
+    @FastNative
+    private static native boolean nGetClipToOutline(long renderNode);
+    @FastNative
+    private static native float nGetAlpha(long renderNode);
+    @FastNative
+    private static native float nGetCameraDistance(long renderNode);
+    @FastNative
+    private static native float nGetScaleX(long renderNode);
+    @FastNative
+    private static native float nGetScaleY(long renderNode);
+    @FastNative
+    private static native float nGetElevation(long renderNode);
+    @FastNative
+    private static native float nGetTranslationX(long renderNode);
+    @FastNative
+    private static native float nGetTranslationY(long renderNode);
+    @FastNative
+    private static native float nGetTranslationZ(long renderNode);
+    @FastNative
+    private static native float nGetRotation(long renderNode);
+    @FastNative
+    private static native float nGetRotationX(long renderNode);
+    @FastNative
+    private static native float nGetRotationY(long renderNode);
+    @FastNative
+    private static native boolean nIsPivotExplicitlySet(long renderNode);
+    @FastNative
+    private static native float nGetPivotX(long renderNode);
+    @FastNative
+    private static native float nGetPivotY(long renderNode);
 }
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index 1ba9fc5..f899c00 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -246,6 +246,18 @@
     return result;
 }
 
+static jboolean
+android_hardware_UsbDeviceConnection_reset_device(JNIEnv *env, jobject thiz)
+{
+    struct usb_device* device = get_device_from_object(env, thiz);
+    if (!device) {
+        ALOGE("device is closed in native_reset_device");
+        return JNI_FALSE;
+    }
+    int ret = usb_device_reset(device);
+    return (ret == 0) ? JNI_TRUE : JNI_FALSE;
+}
+
 static const JNINativeMethod method_table[] = {
     {"native_open",             "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
                                         (void *)android_hardware_UsbDeviceConnection_open},
@@ -264,6 +276,7 @@
                                         (void *)android_hardware_UsbDeviceConnection_request_wait},
     { "native_get_serial",      "()Ljava/lang/String;",
                                         (void*)android_hardware_UsbDeviceConnection_get_serial },
+    {"native_reset_device","()Z", (void *)android_hardware_UsbDeviceConnection_reset_device},
 };
 
 int register_android_hardware_UsbDeviceConnection(JNIEnv *env)
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 0202303..94918f6 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -333,6 +333,7 @@
     return val;                                                         \
 }
 
+DEFINE_PARCEL_WRITER(Bool,jboolean)
 DEFINE_PARCEL_WRITER(Int8,jbyte)
 DEFINE_PARCEL_WRITER(Int16,jshort)
 DEFINE_PARCEL_WRITER(Int32,jint)
@@ -347,6 +348,17 @@
 DEFINE_PARCEL_READER(Float,jfloat)
 DEFINE_PARCEL_READER(Double,jdouble)
 
+static jboolean JHwParcel_native_readBool(JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    bool val;
+    status_t err = parcel->readBool(&val);
+    signalExceptionForError(env, err);
+
+    return (jboolean)val;
+}
+
 static void JHwParcel_native_writeStatus(
         JNIEnv *env, jobject thiz, jint statusCode) {
     using hardware::Status;
@@ -489,6 +501,90 @@
 DEFINE_PARCEL_VECTOR_WRITER(Float,jfloat)
 DEFINE_PARCEL_VECTOR_WRITER(Double,jdouble)
 
+static void JHwParcel_native_writeBoolArray(
+        JNIEnv *env, jobject thiz, jint size, jbooleanArray valObj) {
+    if (valObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    jsize len = env->GetArrayLength(valObj);
+
+    if (len != size) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    jboolean *src = env->GetBooleanArrayElements(valObj, nullptr);
+
+    bool *dst =
+        (bool *)impl->getStorage()->allocTemporaryStorage(size * sizeof(bool));
+
+    for (jint i = 0; i < size; ++i) {
+        dst[i] = src[i];
+    }
+
+    env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */);
+    src = nullptr;
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(
+            dst, size * sizeof(*dst), &parentHandle);
+
+    signalExceptionForError(env, err);
+}
+
+static void JHwParcel_native_writeBoolVector(
+        JNIEnv *env, jobject thiz, jbooleanArray valObj) {
+    if (valObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    hidl_vec<bool> *vec =
+        (hidl_vec<bool> *)impl->getStorage()->allocTemporaryStorage(
+                sizeof(hidl_vec<bool>));
+
+    jsize len = env->GetArrayLength(valObj);
+
+    jboolean *src = env->GetBooleanArrayElements(valObj, nullptr);
+
+    bool *dst =
+        (bool *)impl->getStorage()->allocTemporaryStorage(len * sizeof(bool));
+
+    for (jsize i = 0; i < len; ++i) {
+        dst[i] = src[i];
+    }
+
+    env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */);
+    src = nullptr;
+
+    vec->setToExternal(dst, len);
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle);
+
+    if (err == OK) {
+        size_t childHandle;
+
+        err = vec->writeEmbeddedToParcel(
+                parcel,
+                parentHandle,
+                0 /* parentOffset */,
+                &childHandle);
+    }
+
+    signalExceptionForError(env, err);
+}
+
 static void JHwParcel_native_writeStrongBinder(
         JNIEnv *env, jobject thiz, jobject binderObj) {
     sp<hardware::IBinder> binder;
@@ -616,6 +712,64 @@
 DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float)
 DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double)
 
+static jbooleanArray JHwParcel_native_readBoolArray(
+        JNIEnv *env, jobject thiz, jint size) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+    const bool *val = static_cast<const bool *>(
+            parcel->readBuffer(&parentHandle));
+
+    jbooleanArray valObj = env->NewBooleanArray(size);
+
+    for (jint i = 0; i < size; ++i) {
+        jboolean x = val[i];
+        env->SetBooleanArrayRegion(valObj, i, 1, &x);
+    }
+
+    return valObj;
+}
+
+static jbooleanArray JHwParcel_native_readBoolVector(
+        JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+
+    const hidl_vec<bool> *vec =
+        (const hidl_vec<bool> *)parcel->readBuffer(&parentHandle);
+
+    if (vec == NULL) {
+        signalExceptionForError(env, UNKNOWN_ERROR);
+        return NULL;
+    }
+
+    size_t childHandle;
+
+    status_t err = const_cast<hidl_vec<bool> *>(vec)
+        ->readEmbeddedFromParcel(
+                *parcel,
+                parentHandle,
+                0 /* parentOffset */,
+                &childHandle);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return NULL;
+    }
+
+    jbooleanArray valObj = env->NewBooleanArray(vec->size());
+
+    for (size_t i = 0; i < vec->size(); ++i) {
+        jboolean x = (*vec)[i];
+        env->SetBooleanArrayRegion(valObj, i, 1, &x);
+    }
+
+    return valObj;
+}
+
 static jobjectArray MakeStringArray(
         JNIEnv *env, const hidl_string *array, size_t size) {
     ScopedLocalRef<jclass> stringKlass(
@@ -825,6 +979,7 @@
     { "writeInterfaceToken", "(Ljava/lang/String;)V",
         (void *)JHwParcel_native_writeInterfaceToken },
 
+    { "writeBool", "(Z)V", (void *)JHwParcel_native_writeBool },
     { "writeInt8", "(B)V", (void *)JHwParcel_native_writeInt8 },
     { "writeInt16", "(S)V", (void *)JHwParcel_native_writeInt16 },
     { "writeInt32", "(I)V", (void *)JHwParcel_native_writeInt32 },
@@ -835,6 +990,8 @@
     { "writeString", "(Ljava/lang/String;)V",
         (void *)JHwParcel_native_writeString },
 
+    { "writeBoolArray", "(I[Z)V", (void *)JHwParcel_native_writeBoolArray },
+    { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector },
     { "writeInt8Array", "(I[B)V", (void *)JHwParcel_native_writeInt8Array },
     { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector },
     { "writeInt16Array", "(I[S)V", (void *)JHwParcel_native_writeInt16Array },
@@ -862,6 +1019,7 @@
     { "enforceInterface", "(Ljava/lang/String;)V",
         (void *)JHwParcel_native_enforceInterface },
 
+    { "readBool", "()Z", (void *)JHwParcel_native_readBool },
     { "readInt8", "()B", (void *)JHwParcel_native_readInt8 },
     { "readInt16", "()S", (void *)JHwParcel_native_readInt16 },
     { "readInt32", "()I", (void *)JHwParcel_native_readInt32 },
@@ -872,6 +1030,8 @@
     { "readString", "()Ljava/lang/String;",
         (void *)JHwParcel_native_readString },
 
+    { "readBoolArray", "(I)[Z", (void *)JHwParcel_native_readBoolArray },
+    { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector },
     { "readInt8Array", "(I)[B", (void *)JHwParcel_native_readInt8Array },
     { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector },
     { "readInt16Array", "(I)[S", (void *)JHwParcel_native_readInt16Array },
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index b0028e1..9a91d8e 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -120,12 +120,15 @@
     return reinterpret_cast<jlong>(renderNode);
 }
 
-static void android_view_RenderNode_destroyRenderNode(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
-    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+static void releaseRenderNode(RenderNode* renderNode) {
     renderNode->decStrong(0);
 }
 
+static jlong android_view_RenderNode_getNativeFinalizer(JNIEnv* env,
+        jobject clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseRenderNode));
+}
+
 static void android_view_RenderNode_setDisplayList(JNIEnv* env,
         jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
     class RemovedObserver : public TreeObserver {
@@ -643,79 +646,83 @@
 const char* const kClassPathName = "android/view/RenderNode";
 
 static const JNINativeMethod gMethods[] = {
+// ----------------------------------------------------------------------------
+// Regular JNI
+// ----------------------------------------------------------------------------
     { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
-    { "nDestroyRenderNode",    "(J)V",    (void*) android_view_RenderNode_destroyRenderNode },
+    { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
     { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
     { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
     { "nGetDebugSize",         "(J)I",    (void*) android_view_RenderNode_getDebugSize },
-
-    { "nSetLayerType",         "!(JI)Z",  (void*) android_view_RenderNode_setLayerType },
-    { "nSetLayerPaint",        "!(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
-    { "nSetStaticMatrix",      "!(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
-    { "nSetAnimationMatrix",   "!(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
-    { "nSetClipToBounds",      "!(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
-    { "nSetClipBounds",        "!(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
-    { "nSetClipBoundsEmpty",   "!(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
-    { "nSetProjectBackwards",  "!(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
-    { "nSetProjectionReceiver","!(JZ)Z",  (void*) android_view_RenderNode_setProjectionReceiver },
-
-    { "nSetOutlineRoundRect",  "!(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
-    { "nSetOutlineConvexPath", "!(JJF)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
-    { "nSetOutlineEmpty",      "!(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
-    { "nSetOutlineNone",       "!(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
-    { "nHasShadow",            "!(J)Z",   (void*) android_view_RenderNode_hasShadow },
-    { "nSetClipToOutline",     "!(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
-    { "nSetRevealClip",        "!(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
-
-    { "nSetAlpha",             "!(JF)Z",  (void*) android_view_RenderNode_setAlpha },
-    { "nSetHasOverlappingRendering", "!(JZ)Z",
-            (void*) android_view_RenderNode_setHasOverlappingRendering },
-    { "nSetElevation",         "!(JF)Z",  (void*) android_view_RenderNode_setElevation },
-    { "nSetTranslationX",      "!(JF)Z",  (void*) android_view_RenderNode_setTranslationX },
-    { "nSetTranslationY",      "!(JF)Z",  (void*) android_view_RenderNode_setTranslationY },
-    { "nSetTranslationZ",      "!(JF)Z",  (void*) android_view_RenderNode_setTranslationZ },
-    { "nSetRotation",          "!(JF)Z",  (void*) android_view_RenderNode_setRotation },
-    { "nSetRotationX",         "!(JF)Z",  (void*) android_view_RenderNode_setRotationX },
-    { "nSetRotationY",         "!(JF)Z",  (void*) android_view_RenderNode_setRotationY },
-    { "nSetScaleX",            "!(JF)Z",  (void*) android_view_RenderNode_setScaleX },
-    { "nSetScaleY",            "!(JF)Z",  (void*) android_view_RenderNode_setScaleY },
-    { "nSetPivotX",            "!(JF)Z",  (void*) android_view_RenderNode_setPivotX },
-    { "nSetPivotY",            "!(JF)Z",  (void*) android_view_RenderNode_setPivotY },
-    { "nSetCameraDistance",    "!(JF)Z",  (void*) android_view_RenderNode_setCameraDistance },
-    { "nSetLeft",              "!(JI)Z",  (void*) android_view_RenderNode_setLeft },
-    { "nSetTop",               "!(JI)Z",  (void*) android_view_RenderNode_setTop },
-    { "nSetRight",             "!(JI)Z",  (void*) android_view_RenderNode_setRight },
-    { "nSetBottom",            "!(JI)Z",  (void*) android_view_RenderNode_setBottom },
-    { "nSetLeftTopRightBottom","!(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
-    { "nOffsetLeftAndRight",   "!(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
-    { "nOffsetTopAndBottom",   "!(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
-
-    { "nHasOverlappingRendering", "!(J)Z",  (void*) android_view_RenderNode_hasOverlappingRendering },
-    { "nGetClipToOutline",        "!(J)Z",  (void*) android_view_RenderNode_getClipToOutline },
-    { "nGetAlpha",                "!(J)F",  (void*) android_view_RenderNode_getAlpha },
-    { "nGetCameraDistance",       "!(J)F",  (void*) android_view_RenderNode_getCameraDistance },
-    { "nGetScaleX",               "!(J)F",  (void*) android_view_RenderNode_getScaleX },
-    { "nGetScaleY",               "!(J)F",  (void*) android_view_RenderNode_getScaleY },
-    { "nGetElevation",            "!(J)F",  (void*) android_view_RenderNode_getElevation },
-    { "nGetTranslationX",         "!(J)F",  (void*) android_view_RenderNode_getTranslationX },
-    { "nGetTranslationY",         "!(J)F",  (void*) android_view_RenderNode_getTranslationY },
-    { "nGetTranslationZ",         "!(J)F",  (void*) android_view_RenderNode_getTranslationZ },
-    { "nGetRotation",             "!(J)F",  (void*) android_view_RenderNode_getRotation },
-    { "nGetRotationX",            "!(J)F",  (void*) android_view_RenderNode_getRotationX },
-    { "nGetRotationY",            "!(J)F",  (void*) android_view_RenderNode_getRotationY },
-    { "nIsPivotExplicitlySet",    "!(J)Z",  (void*) android_view_RenderNode_isPivotExplicitlySet },
-    { "nHasIdentityMatrix",       "!(J)Z",  (void*) android_view_RenderNode_hasIdentityMatrix },
-
-    { "nGetTransformMatrix",       "!(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
-    { "nGetInverseTransformMatrix","!(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
-
-    { "nGetPivotX",                "!(J)F",  (void*) android_view_RenderNode_getPivotX },
-    { "nGetPivotY",                "!(J)F",  (void*) android_view_RenderNode_getPivotY },
-
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
     { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
-
     { "nRequestPositionUpdates",   "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+
+// ----------------------------------------------------------------------------
+// Fast JNI via @FastNative annotation in RenderNode.java
+// ----------------------------------------------------------------------------
+    { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
+    { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
+    { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
+    { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
+    { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
+    { "nSetClipBounds",        "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
+    { "nSetClipBoundsEmpty",   "(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
+    { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
+    { "nSetProjectionReceiver","(JZ)Z",  (void*) android_view_RenderNode_setProjectionReceiver },
+
+    { "nSetOutlineRoundRect",  "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
+    { "nSetOutlineConvexPath", "(JJF)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
+    { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
+    { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
+    { "nHasShadow",            "(J)Z",   (void*) android_view_RenderNode_hasShadow },
+    { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
+    { "nSetRevealClip",        "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
+
+    { "nSetAlpha",             "(JF)Z",  (void*) android_view_RenderNode_setAlpha },
+    { "nSetHasOverlappingRendering", "(JZ)Z",
+            (void*) android_view_RenderNode_setHasOverlappingRendering },
+    { "nSetElevation",         "(JF)Z",  (void*) android_view_RenderNode_setElevation },
+    { "nSetTranslationX",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationX },
+    { "nSetTranslationY",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationY },
+    { "nSetTranslationZ",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationZ },
+    { "nSetRotation",          "(JF)Z",  (void*) android_view_RenderNode_setRotation },
+    { "nSetRotationX",         "(JF)Z",  (void*) android_view_RenderNode_setRotationX },
+    { "nSetRotationY",         "(JF)Z",  (void*) android_view_RenderNode_setRotationY },
+    { "nSetScaleX",            "(JF)Z",  (void*) android_view_RenderNode_setScaleX },
+    { "nSetScaleY",            "(JF)Z",  (void*) android_view_RenderNode_setScaleY },
+    { "nSetPivotX",            "(JF)Z",  (void*) android_view_RenderNode_setPivotX },
+    { "nSetPivotY",            "(JF)Z",  (void*) android_view_RenderNode_setPivotY },
+    { "nSetCameraDistance",    "(JF)Z",  (void*) android_view_RenderNode_setCameraDistance },
+    { "nSetLeft",              "(JI)Z",  (void*) android_view_RenderNode_setLeft },
+    { "nSetTop",               "(JI)Z",  (void*) android_view_RenderNode_setTop },
+    { "nSetRight",             "(JI)Z",  (void*) android_view_RenderNode_setRight },
+    { "nSetBottom",            "(JI)Z",  (void*) android_view_RenderNode_setBottom },
+    { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
+    { "nOffsetLeftAndRight",   "(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
+    { "nOffsetTopAndBottom",   "(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
+
+    { "nHasOverlappingRendering", "(J)Z",  (void*) android_view_RenderNode_hasOverlappingRendering },
+    { "nGetClipToOutline",        "(J)Z",  (void*) android_view_RenderNode_getClipToOutline },
+    { "nGetAlpha",                "(J)F",  (void*) android_view_RenderNode_getAlpha },
+    { "nGetCameraDistance",       "(J)F",  (void*) android_view_RenderNode_getCameraDistance },
+    { "nGetScaleX",               "(J)F",  (void*) android_view_RenderNode_getScaleX },
+    { "nGetScaleY",               "(J)F",  (void*) android_view_RenderNode_getScaleY },
+    { "nGetElevation",            "(J)F",  (void*) android_view_RenderNode_getElevation },
+    { "nGetTranslationX",         "(J)F",  (void*) android_view_RenderNode_getTranslationX },
+    { "nGetTranslationY",         "(J)F",  (void*) android_view_RenderNode_getTranslationY },
+    { "nGetTranslationZ",         "(J)F",  (void*) android_view_RenderNode_getTranslationZ },
+    { "nGetRotation",             "(J)F",  (void*) android_view_RenderNode_getRotation },
+    { "nGetRotationX",            "(J)F",  (void*) android_view_RenderNode_getRotationX },
+    { "nGetRotationY",            "(J)F",  (void*) android_view_RenderNode_getRotationY },
+    { "nIsPivotExplicitlySet",    "(J)Z",  (void*) android_view_RenderNode_isPivotExplicitlySet },
+    { "nHasIdentityMatrix",       "(J)Z",  (void*) android_view_RenderNode_hasIdentityMatrix },
+
+    { "nGetTransformMatrix",       "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
+    { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
+
+    { "nGetPivotX",                "(J)F",  (void*) android_view_RenderNode_getPivotX },
+    { "nGetPivotY",                "(J)F",  (void*) android_view_RenderNode_getPivotY },
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 40ffb74..70ea4df 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3339,6 +3339,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_CT_LOGS" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.MasterClearReceiver"
             android:permission="android.permission.MASTER_CLEAR">
             <intent-filter
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ec93e47..15b32c75 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1829,6 +1829,11 @@
          Do not set this to true for production devices. Doing so will cause you to fail CTS. -->
     <bool name="config_disableUsbPermissionDialogs">false</bool>
 
+    <!-- Activity to handle Usb Device connection in USB Host side. Keeping it to null value will
+         lead into handling it inside system using Intent resolution. Non-null contents will have
+         format of package-name/ActivityClassName. -->
+    <string name="config_UsbDeviceConnectionHandling_component" translatable="false">@null</string>
+
     <!-- Minimum span needed to begin a touch scaling gesture.
          If the span is equal to or greater than this size, a scaling gesture
          will begin, where supported. (See android.view.ScaleGestureDetector)
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 166862f..e219b76 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1866,6 +1866,7 @@
   <java-symbol type="string" name="usb_ptp_notification_title" />
   <java-symbol type="string" name="usb_midi_notification_title" />
   <java-symbol type="string" name="usb_supplying_notification_title" />
+  <java-symbol type="string" name="config_UsbDeviceConnectionHandling_component" />
   <java-symbol type="string" name="vpn_text" />
   <java-symbol type="string" name="vpn_text_long" />
   <java-symbol type="string" name="vpn_title" />
diff --git a/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java b/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java
new file mode 100644
index 0000000..ec65f8d
--- /dev/null
+++ b/services/core/java/com/android/server/updates/CertificateTransparencyLogInstallReceiver.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016 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.updates;
+
+import com.android.internal.util.HexDump;
+import android.os.FileUtils;
+import android.system.Os;
+import android.system.ErrnoException;
+import android.util.Base64;
+import android.util.Slog;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringBufferInputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.security.NoSuchAlgorithmException;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class CertificateTransparencyLogInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    private static final String TAG = "CTLogInstallReceiver";
+    private static final String LOGDIR_PREFIX = "logs-";
+
+    public CertificateTransparencyLogInstallReceiver() {
+        super("/data/misc/keychain/trusted_ct_logs/", "ct_logs", "metadata/", "version");
+    }
+
+    @Override
+    protected void install(byte[] content, int version) throws IOException {
+        /* Install is complicated here because we translate the input, which is a JSON file
+         * containing log information to a directory with a file per log. To support atomically
+         * replacing the old configuration directory with the new there's a bunch of steps. We
+         * create a new directory with the logs and then do an atomic update of the current symlink
+         * to point to the new directory.
+         */
+
+        // 1. Ensure that the update dir exists and is readable
+        updateDir.mkdir();
+        if (!updateDir.isDirectory()) {
+            throw new IOException("Unable to make directory " + updateDir.getCanonicalPath());
+        }
+        if (!updateDir.setReadable(true, false)) {
+            throw new IOException("Unable to set permissions on " +
+                    updateDir.getCanonicalPath());
+        }
+        File currentSymlink = new File(updateDir, "current");
+        File newVersion = new File(updateDir, LOGDIR_PREFIX + String.valueOf(version));
+        File oldDirectory;
+        // 2. Handle the corner case where the new directory already exists.
+        if (newVersion.exists()) {
+            // If the symlink has already been updated then the update died between steps 7 and 8
+            // and so we cannot delete the directory since its in use. Instead just bump the version
+            // and return.
+            if (newVersion.getCanonicalPath().equals(currentSymlink.getCanonicalPath())) {
+                writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes());
+                deleteOldLogDirectories();
+                return;
+            } else {
+                FileUtils.deleteContentsAndDir(newVersion);
+            }
+        }
+        try {
+            // 3. Create /data/misc/keychain/trusted_ct_logs/<new_version>/ .
+            newVersion.mkdir();
+            if (!newVersion.isDirectory()) {
+                throw new IOException("Unable to make directory " + newVersion.getCanonicalPath());
+            }
+            if (!newVersion.setReadable(true, false)) {
+                throw new IOException("Failed to set " +newVersion.getCanonicalPath() +
+                        " readable");
+            }
+
+            // 4. For each log in the log file create the corresponding file in <new_version>/ .
+            try {
+                JSONObject json = new JSONObject(new String(content, StandardCharsets.UTF_8));
+                JSONArray logs = json.getJSONArray("logs");
+                for (int i = 0; i < logs.length(); i++) {
+                    JSONObject log = logs.getJSONObject(i);
+                    installLog(newVersion, log);
+                }
+            } catch (JSONException e) {
+                throw new IOException("Failed to parse logs", e);
+            }
+
+            // 5. Create the temp symlink. We'll rename this to the target symlink to get an atomic
+            // update.
+            File tempSymlink = new File(updateDir, "new_symlink");
+            try {
+                Os.symlink(newVersion.getCanonicalPath(), tempSymlink.getCanonicalPath());
+            } catch (ErrnoException e) {
+                throw new IOException("Failed to create symlink", e);
+            }
+
+            // 6. Update the symlink target, this is the actual update step.
+            tempSymlink.renameTo(currentSymlink.getAbsoluteFile());
+        } catch (IOException | RuntimeException e) {
+            FileUtils.deleteContentsAndDir(newVersion);
+            throw e;
+        }
+        Slog.i(TAG, "CT log directory updated to " + newVersion.getAbsolutePath());
+        // 7. Update the current version information
+        writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes());
+        // 8. Cleanup
+        deleteOldLogDirectories();
+    }
+
+    private void installLog(File directory, JSONObject logObject) throws IOException {
+        try {
+            String logFilename = getLogFileName(logObject.getString("key"));
+            File file = new File(directory, logFilename);
+            try (OutputStreamWriter out =
+                    new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
+                writeLogEntry(out, "key", logObject.getString("key"));
+                writeLogEntry(out, "url", logObject.getString("url"));
+                writeLogEntry(out, "description", logObject.getString("description"));
+            }
+            if (!file.setReadable(true, false)) {
+                throw new IOException("Failed to set permissions on " + file.getCanonicalPath());
+            }
+        } catch (JSONException e) {
+            throw new IOException("Failed to parse log", e);
+        }
+
+    }
+
+    /**
+     * Get the filename for a log based on its public key. This must be kept in sync with
+     * org.conscrypt.ct.CTLogStoreImpl.
+     */
+    private String getLogFileName(String base64PublicKey) {
+        byte[] keyBytes = Base64.decode(base64PublicKey, Base64.DEFAULT);
+        try {
+            byte[] id = MessageDigest.getInstance("SHA-256").digest(keyBytes);
+            return HexDump.toHexString(id, false);
+        } catch (NoSuchAlgorithmException e) {
+            // SHA-256 is guaranteed to be available.
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void writeLogEntry(OutputStreamWriter out, String key, String value)
+            throws IOException {
+        out.write(key + ":" + value + "\n");
+    }
+
+    private void deleteOldLogDirectories() throws IOException {
+        if (!updateDir.exists()) {
+            return;
+        }
+        File currentTarget = new File(updateDir, "current").getCanonicalFile();
+        FileFilter filter = new FileFilter() {
+            @Override
+            public boolean accept(File file) {
+                return !currentTarget.equals(file) && file.getName().startsWith(LOGDIR_PREFIX);
+            }
+        };
+        for (File f : updateDir.listFiles(filter)) {
+            FileUtils.deleteContentsAndDir(f);
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b28843a..39e522e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -227,6 +227,7 @@
 
     /**
      *  System property whose value is either "true" or "false", indicating whether
+     *  device owner is present.
      */
     private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.device_owner";
 
@@ -4558,14 +4559,14 @@
         }
     }
 
-    private void wipeDataLocked(boolean wipeExtRequested, String reason) {
+    private void wipeDataLocked(boolean wipeExtRequested, String reason, boolean force) {
         if (wipeExtRequested) {
             StorageManager sm = (StorageManager) mContext.getSystemService(
                     Context.STORAGE_SERVICE);
             sm.wipeAdoptableDisks();
         }
         try {
-            RecoverySystem.rebootWipeUserData(mContext, reason);
+            RecoverySystem.rebootWipeUserData(mContext, false /* shutdown */, reason, force);
         } catch (IOException | SecurityException e) {
             Slog.w(LOG_TAG, "Failed requesting data wipe", e);
         }
@@ -4600,17 +4601,35 @@
                     }
                 }
                 boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0;
+                // If the admin is the only one who has set the restriction: force wipe, even if
+                // {@link UserManager.DISALLOW_FACTORY_RESET} is set. Reason is that the admin
+                // could remove this user restriction anyway.
+                boolean force = (userHandle == UserHandle.USER_SYSTEM)
+                        && isAdminOnlyOneWhoSetRestriction(admin,
+                                UserManager.DISALLOW_FACTORY_RESET, UserHandle.USER_SYSTEM);
                 wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
-                        "DevicePolicyManager.wipeData() from " + source);
+                        "DevicePolicyManager.wipeData() from " + source, force);
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
 
-    private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle, String reason) {
+    private boolean isAdminOnlyOneWhoSetRestriction(ActiveAdmin admin, String userRestriction,
+            int userId) {
+        int source = mUserManager.getUserRestrictionSource(userRestriction, UserHandle.of(userId));
+        if (isDeviceOwner(admin.info.getComponent(), userId)) {
+            return source == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
+        } else if (isProfileOwner(admin.info.getComponent(), userId)) {
+            return source == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+        }
+        return false;
+    }
+
+    private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle,
+            String reason, boolean force) {
         if (userHandle == UserHandle.USER_SYSTEM) {
-            wipeDataLocked(wipeExtRequested, reason);
+            wipeDataLocked(wipeExtRequested, reason, force);
         } else {
             mHandler.post(new Runnable() {
                 @Override
@@ -4786,7 +4805,7 @@
             if (wipeData) {
                 // Call without holding lock.
                 wipeDeviceOrUserLocked(false, identifier,
-                        "reportFailedPasswordAttempt()");
+                        "reportFailedPasswordAttempt()", false);
             }
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 965341e..1d850e1 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.usb;
 
+import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
@@ -24,6 +26,7 @@
 import android.hardware.usb.UsbInterface;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -63,11 +66,20 @@
     @GuardedBy("mLock")
     private UsbSettingsManager mCurrentSettings;
 
+    @GuardedBy("mLock")
+    private ComponentName mUsbDeviceConnectionHandler;
+
     public UsbHostManager(Context context, UsbAlsaManager alsaManager) {
         mContext = context;
         mHostBlacklist = context.getResources().getStringArray(
                 com.android.internal.R.array.config_usbHostBlacklist);
         mUsbAlsaManager = alsaManager;
+        String deviceConnectionHandler = context.getResources().getString(
+                com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
+        if (!TextUtils.isEmpty(deviceConnectionHandler)) {
+            setUsbDeviceConnectionHandler(ComponentName.unflattenFromString(
+                    deviceConnectionHandler));
+        }
     }
 
     public void setCurrentSettings(UsbSettingsManager settings) {
@@ -82,6 +94,18 @@
         }
     }
 
+    public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
+        synchronized (mLock) {
+            mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
+        }
+    }
+
+    private @Nullable ComponentName getUsbDeviceConnectionHandler() {
+        synchronized (mLock) {
+            return mUsbDeviceConnectionHandler;
+        }
+    }
+
     private boolean isBlackListed(String deviceName) {
         int count = mHostBlacklist.length;
         for (int i = 0; i < count; i++) {
@@ -219,10 +243,17 @@
         synchronized (mLock) {
             if (mNewDevice != null) {
                 mNewDevice.setConfigurations(
-                        mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));
+                        mNewConfigurations.toArray(
+                                new UsbConfiguration[mNewConfigurations.size()]));
                 mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
                 Slog.d(TAG, "Added device " + mNewDevice);
-                getCurrentSettings().deviceAttached(mNewDevice);
+                ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+                if (usbDeviceConnectionHandler == null) {
+                    getCurrentSettings().deviceAttached(mNewDevice);
+                } else {
+                    getCurrentSettings().deviceAttachedForFixedHandler(mNewDevice,
+                            usbDeviceConnectionHandler);
+                }
                 mUsbAlsaManager.usbDeviceAdded(mNewDevice);
             } else {
                 Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
@@ -292,6 +323,9 @@
             for (String name : mDevices.keySet()) {
                 pw.println("  " + name + ": " + mDevices.get(name));
             }
+            if (mUsbDeviceConnectionHandler != null) {
+                pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+            }
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index d6dbe90..81ac2dd 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -385,6 +386,14 @@
     }
 
     @Override
+    public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+        if (mHostManager != null) {
+            mHostManager.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler);
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index de9ede3..6b9acf2 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -760,6 +760,31 @@
         resolveActivity(intent, matches, defaultPackage, device, null);
     }
 
+    public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
+        final Intent intent = createDeviceAttachedIntent(device);
+
+        // Send broadcast to running activity with registered intent
+        mUserContext.sendBroadcast(intent);
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName());
+            return;
+        }
+
+        grantDevicePermission(device, appInfo.uid);
+
+        Intent activityIntent = new Intent(intent);
+        activityIntent.setComponent(component);
+        try {
+            mUserContext.startActivityAsUser(activityIntent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "unable to start activity " + activityIntent);
+        }
+    }
+
     public void deviceDetached(UsbDevice device) {
         // clear temporary permissions for the device
         mDevicePermissionMap.remove(device.getDeviceName());
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk
new file mode 100644
index 0000000..3137a73
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 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_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AoapTestDeviceApp
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/AndroidManifest.xml b/tests/UsbHostExternalManagmentTest/AoapTestDevice/AndroidManifest.xml
new file mode 100644
index 0000000..99bb520
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.android.hardware.usb.aoapdevicetest" >
+    <application android:label="UsbAoapDeviceTestApp" >
+        <activity android:name=".UsbAoapDeviceTestActivity"
+            android:configChanges="keyboard|keyboardHidden" >
+            <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>
+</manifest>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/layout/device.xml b/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/layout/device.xml
new file mode 100644
index 0000000..cc71cf9
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/layout/device.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+</LinearLayout>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/xml/accessory_filter.xml b/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/xml/accessory_filter.xml
new file mode 100644
index 0000000..d854a45
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/res/xml/accessory_filter.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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 model="AOAP Test App" manufacturer="Android"/>
+</resources>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/src/com/android/hardware/usb/aoapdevicetest/UsbAoapDeviceTestActivity.java b/tests/UsbHostExternalManagmentTest/AoapTestDevice/src/com/android/hardware/usb/aoapdevicetest/UsbAoapDeviceTestActivity.java
new file mode 100644
index 0000000..aa4f8ca
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/src/com/android/hardware/usb/aoapdevicetest/UsbAoapDeviceTestActivity.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 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.hardware.usb.aoapdevicetest;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import libcore.io.IoUtils;
+
+public class UsbAoapDeviceTestActivity extends Activity {
+    private static final String TAG = UsbAoapDeviceTestActivity.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    private static final String ACTION_USB_ACCESSORY_PERMISSION =
+            "com.android.hardware.usb.aoapdevicetest.ACTION_USB_ACCESSORY_PERMISSION";
+
+    private UsbManager mUsbManager;
+    private AccessoryReceiver mReceiver;
+    private ParcelFileDescriptor mFd;
+    private ReaderThread mReaderThread;
+    private UsbAccessory mAccessory;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.device);
+
+        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+        filter.addAction(ACTION_USB_ACCESSORY_PERMISSION);
+        mReceiver = new AccessoryReceiver();
+        registerReceiver(mReceiver, filter);
+
+        Intent intent = getIntent();
+        if (intent.getAction().equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+            UsbAccessory accessory =
+                    (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+            if (accessory != null) {
+                onAccessoryAttached(accessory);
+            } else {
+                throw new RuntimeException("USB accessory is null.");
+            }
+        } else {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+        IoUtils.closeQuietly(mFd);
+        if (mReaderThread != null) {
+            mReaderThread.requestToQuit();
+            try {
+                mReaderThread.join(1000);
+            } catch (InterruptedException e) {
+            }
+            if (mReaderThread.isAlive()) { // reader thread stuck
+                Log.w(TAG, "ReaderThread still alive");
+            }
+        }
+    }
+
+    private void onAccessoryAttached(UsbAccessory accessory) {
+        Log.i(TAG, "Starting AOAP discovery protocol, accessory attached: " + accessory);
+        // Check whether we have permission to access the accessory.
+        if (!mUsbManager.hasPermission(accessory)) {
+            Log.i(TAG, "Prompting the user for access to the accessory.");
+            Intent intent = new Intent(ACTION_USB_ACCESSORY_PERMISSION);
+            intent.setPackage(getPackageName());
+            PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                    this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+            mUsbManager.requestPermission(accessory, pendingIntent);
+            return;
+        }
+        mFd = mUsbManager.openAccessory(accessory);
+        if (mFd == null) {
+            Log.e(TAG, "UsbManager.openAccessory returned null");
+            finish();
+            return;
+        }
+        mAccessory = accessory;
+        mReaderThread = new ReaderThread(mFd);
+        mReaderThread.start();
+    }
+
+    private void onAccessoryDetached(UsbAccessory accessory) {
+        Log.i(TAG, "Accessory detached: " + accessory);
+        finish();
+    }
+
+    private class ReaderThread extends Thread {
+        private boolean mShouldQuit = false;
+        private final FileInputStream mInputStream;
+        private final FileOutputStream mOutputStream;
+        private final byte[] mBuffer = new byte[16384];
+
+        private ReaderThread(ParcelFileDescriptor fd) {
+            super("AOAP");
+            mInputStream = new FileInputStream(fd.getFileDescriptor());
+            mOutputStream = new FileOutputStream(fd.getFileDescriptor());
+        }
+
+        private synchronized void requestToQuit() {
+            mShouldQuit = true;
+        }
+
+        private synchronized boolean shouldQuit() {
+            return mShouldQuit;
+        }
+
+        @Override
+        public void run() {
+            while (!shouldQuit()) {
+                try {
+                    int read = mInputStream.read(mBuffer);
+                } catch (IOException e) {
+                    Log.i(TAG, "ReaderThread IOException", e);
+                    // AOAP App should release FD when IOException happens.
+                    // If FD is kept, device will not behave nicely on reset and multiple reset
+                    // can be required.
+                    finish();
+                    return;
+                }
+            }
+        }
+    }
+
+    private class AccessoryReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+            if (accessory != null) {
+                String action = intent.getAction();
+                if (action.equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    onAccessoryAttached(accessory);
+                } else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
+                    if (mAccessory != null && mAccessory.equals(accessory)) {
+                        onAccessoryDetached(accessory);
+                    }
+                } else if (action.equals(ACTION_USB_ACCESSORY_PERMISSION)) {
+                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+                        Log.i(TAG, "Accessory permission granted: " + accessory);
+                        onAccessoryAttached(accessory);
+                    } else {
+                        Log.e(TAG, "Accessory permission denied: " + accessory);
+                        finish();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk
new file mode 100644
index 0000000..354e8c9
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 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_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AoapTestHostApp
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/AndroidManifest.xml b/tests/UsbHostExternalManagmentTest/AoapTestHost/AndroidManifest.xml
new file mode 100644
index 0000000..8cc470e
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.android.hardware.usb.aoaphosttest" >
+    <application android:label="UsbAoapHostTestApp" >
+      <activity android:name=".UsbAoapHostTestActivity"
+            android:configChanges="keyboard|keyboardHidden" >
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+            </intent-filter>
+            <meta-data
+                android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+                android:resource="@xml/usb_device_filter"/>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/res/layout/host.xml b/tests/UsbHostExternalManagmentTest/AoapTestHost/res/layout/host.xml
new file mode 100644
index 0000000..cc71cf9
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/res/layout/host.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+</LinearLayout>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/res/xml/usb_device_filter.xml b/tests/UsbHostExternalManagmentTest/AoapTestHost/res/xml/usb_device_filter.xml
new file mode 100644
index 0000000..0509e89
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/res/xml/usb_device_filter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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>
+    <!-- Android USB accessory: accessory -->
+    <usb-device vendor-id="16601" product-id="11520" />
+    <!-- Android USB accessory: accessory + adb -->
+    <usb-device vendor-id="16601" product-id="11521" />
+    <!-- not suppoted by UsbService, but external host management can use this. -->
+    <usb-aoap-device model="AOAP Test App" manufacturer="Android" version="1.0" />
+</resources>
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/src/com/android/hardware/usb/aoaphosttest/UsbAoapHostTestActivity.java b/tests/UsbHostExternalManagmentTest/AoapTestHost/src/com/android/hardware/usb/aoaphosttest/UsbAoapHostTestActivity.java
new file mode 100644
index 0000000..6e2dc5d
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/src/com/android/hardware/usb/aoaphosttest/UsbAoapHostTestActivity.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 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.hardware.usb.aoaphosttest;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+public class UsbAoapHostTestActivity extends Activity {
+
+    private static final String TAG = UsbAoapHostTestActivity.class.getSimpleName();
+
+    private UsbManager mUsbManager;
+    private UsbStateReceiver mReceiver;
+    private UsbDevice mUsbDevice;
+    private UsbDeviceConnection mUsbConnection;
+    private ReaderThread mReaderThread;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.host);
+
+        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        mReceiver = new UsbStateReceiver();
+        registerReceiver(mReceiver, filter);
+
+        Intent intent = getIntent();
+        if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+            mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+            mUsbConnection = mUsbManager.openDevice(mUsbDevice);
+            mReaderThread = new ReaderThread(mUsbDevice, mUsbConnection);
+            mReaderThread.start();
+        } else {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+        if (mUsbConnection != null) {
+            mUsbConnection.close();
+        }
+        if (mReaderThread != null) {
+            mReaderThread.requestToQuit();
+            try {
+                mReaderThread.join(1000);
+            } catch (InterruptedException e) {
+            }
+            if (mReaderThread.isAlive()) { // reader thread stuck
+                throw new RuntimeException("ReaderThread still alive");
+            }
+        }
+    }
+
+    private static boolean isDevicesMatching(UsbDevice l, UsbDevice r) {
+        if (l.getVendorId() == r.getVendorId() && l.getProductId() == r.getProductId() &&
+                TextUtils.equals(l.getSerialNumber(), r.getSerialNumber())) {
+            return true;
+        }
+        return false;
+    }
+
+    private class ReaderThread extends Thread {
+        private boolean mShouldQuit = false;
+        private final UsbDevice mDevice;
+        private final UsbDeviceConnection mUsbConnection;
+        private final UsbEndpoint mBulkIn;
+        private final UsbEndpoint mBulkOut;
+        private final byte[] mBuffer = new byte[16384];
+
+        private ReaderThread(UsbDevice device, UsbDeviceConnection conn) {
+            super("AOAP");
+            mDevice = device;
+            mUsbConnection = conn;
+            UsbInterface iface = mDevice.getInterface(0);
+            // Setup bulk endpoints.
+            UsbEndpoint bulkIn = null;
+            UsbEndpoint bulkOut = null;
+            for (int i = 0; i < iface.getEndpointCount(); i++) {
+                UsbEndpoint ep = iface.getEndpoint(i);
+                if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
+                    if (bulkIn == null) {
+                        bulkIn = ep;
+                    }
+                } else {
+                    if (bulkOut == null) {
+                        bulkOut = ep;
+                    }
+                }
+            }
+            if (bulkIn == null || bulkOut == null) {
+                throw new IllegalStateException("Unable to find bulk endpoints");
+            }
+            mBulkIn = bulkIn;
+            mBulkOut = bulkOut;
+        }
+
+        private synchronized void requestToQuit() {
+            mShouldQuit = true;
+        }
+
+        private synchronized boolean shouldQuit() {
+            return mShouldQuit;
+        }
+
+        @Override
+        public void run() {
+            while (!shouldQuit()) {
+                int read = mUsbConnection.bulkTransfer(mBulkIn, mBuffer, mBuffer.length,
+                        Integer.MAX_VALUE);
+                if (read < 0) {
+                    throw new RuntimeException("bulkTransfer failed, read = " + read);
+                }
+            }
+        }
+    }
+
+    private class UsbStateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
+                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                if (isDevicesMatching(mUsbDevice, device)) {
+                    finish();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk
new file mode 100644
index 0000000..2d6d6ea8
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2016 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_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := UsbHostExternalManagementTestApp
+
+LOCAL_PRIVILEGED_MODULE := true
+# TODO remove tests tag
+#LOCAL_MODULE_TAGS := tests
+#LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/AndroidManifest.xml b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..97bbefb
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.android.hardware.usb.externalmanagementtest" >
+
+    <uses-permission android:name="android.permission.MANAGE_USB" />
+    <application android:label="UsbHostExternalManagementTestApp" >
+        <activity android:name=".UsbHostManagementActivity"
+            android:configChanges="keyboard|keyboardHidden" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/layout/host_management.xml b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/layout/host_management.xml
new file mode 100644
index 0000000..5191184
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/layout/host_management.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+    <TextView android:id="@+id/device_info_text"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/current_device"
+        />
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal" >
+        <Button android:id="@+id/start_aoap_button"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/start_aoap"
+        />
+        <Button android:id="@+id/start_aoap_activity_button"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/start_aoap_activity"
+        />
+        <Button android:id="@+id/reset_button"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/usb_reset"
+            />
+        <Button android:id="@+id/finish_button"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/finish_app"
+            />
+    </LinearLayout>
+    <TextView android:id="@+id/aoap_apps_text"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/aoap_app_msg"
+        />
+
+</LinearLayout>
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/values/strings.xml b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/values/strings.xml
new file mode 100644
index 0000000..79d2c43
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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>
+    <string name="app_title">UsbHostExternalManagementTestApp</string>
+    <string name="current_device">No device</string>
+    <string name="aoap_app_msg">AOAP App message</string>
+    <string name="usb_reset">Reset USB</string>
+    <string name="start_aoap">Start Test AOAP</string>
+    <string name="start_aoap_activity">Start Test AOAP Activity</string>
+    <string name="finish_app">Finish</string>
+</resources>
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/AoapInterface.java b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/AoapInterface.java
new file mode 100644
index 0000000..89dc441
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/AoapInterface.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) 2016 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.hardware.usb.externalmanagementtest;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+public class AoapInterface {
+    /**
+     * Use Google Vendor ID when in accessory mode
+     */
+    public static final int USB_ACCESSORY_VENDOR_ID = 0x18D1;
+
+    /**
+     * Product ID to use when in accessory mode
+     */
+    public static final int USB_ACCESSORY_PRODUCT_ID = 0x2D00;
+
+    /**
+     * Product ID to use when in accessory mode and adb is enabled
+     */
+    public static final int USB_ACCESSORY_ADB_PRODUCT_ID = 0x2D01;
+
+    /**
+     * Indexes for strings sent by the host via ACCESSORY_SEND_STRING
+     */
+    public static final int ACCESSORY_STRING_MANUFACTURER = 0;
+    public static final int ACCESSORY_STRING_MODEL = 1;
+    public static final int ACCESSORY_STRING_DESCRIPTION = 2;
+    public static final int ACCESSORY_STRING_VERSION = 3;
+    public static final int ACCESSORY_STRING_URI = 4;
+    public static final int ACCESSORY_STRING_SERIAL = 5;
+
+    /**
+     * Control request for retrieving device's protocol version
+     *
+     *  requestType:    USB_DIR_IN | USB_TYPE_VENDOR
+     *  request:        ACCESSORY_GET_PROTOCOL
+     *  value:          0
+     *  index:          0
+     *  data            version number (16 bits little endian)
+     *                     1 for original accessory support
+     *                     2 adds HID and device to host audio support
+     */
+    public static final int ACCESSORY_GET_PROTOCOL = 51;
+
+    /**
+     * Control request for host to send a string to the device
+     *
+     *  requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+     *  request:        ACCESSORY_SEND_STRING
+     *  value:          0
+     *  index:          string ID
+     *  data            zero terminated UTF8 string
+     *
+     *  The device can later retrieve these strings via the
+     *  ACCESSORY_GET_STRING_* ioctls
+     */
+    public static final int ACCESSORY_SEND_STRING = 52;
+
+    /**
+     * Control request for starting device in accessory mode.
+     * The host sends this after setting all its strings to the device.
+     *
+     *  requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+     *  request:        ACCESSORY_START
+     *  value:          0
+     *  index:          0
+     *  data            none
+     */
+    public static final int ACCESSORY_START = 53;
+
+    /**
+     * Max payload size for AOAP. Limited by driver.
+     */
+    public static final int MAX_PAYLOAD_SIZE = 16384;
+
+    private static final String TAG = AoapInterface.class.getSimpleName();
+
+    public static int getProtocol(UsbDeviceConnection conn) {
+        byte buffer[] = new byte[2];
+        int len = conn.controlTransfer(
+                UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR,
+                AoapInterface.ACCESSORY_GET_PROTOCOL, 0, 0, buffer, 2, 10000);
+        if (len != 2) {
+            return -1;
+        }
+        return (buffer[1] << 8) | buffer[0];
+    }
+
+    public static void sendString(UsbDeviceConnection conn, int index, String string) {
+        byte[] buffer = (string + "\0").getBytes();
+        int len = conn.controlTransfer(
+                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+                AoapInterface.ACCESSORY_SEND_STRING, 0, index,
+                buffer, buffer.length, 10000);
+        if (len != buffer.length) {
+            throw new RuntimeException("Failed to send string " + index + ": \"" + string + "\"");
+        } else {
+            Log.i(TAG, "Sent string " + index + ": \"" + string + "\"");
+        }
+    }
+
+    public static void sendAoapStart(UsbDeviceConnection conn) {
+        int len = conn.controlTransfer(
+                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+                AoapInterface.ACCESSORY_START, 0, 0, null, 0, 10000);
+        if (len < 0) {
+            throw new RuntimeException("control transfer for accessory start failed:" + len);
+        }
+    }
+
+    public static boolean isDeviceInAoapMode(UsbDevice device) {
+        final int vid = device.getVendorId();
+        final int pid = device.getProductId();
+        return vid == USB_ACCESSORY_VENDOR_ID
+                && (pid == USB_ACCESSORY_PRODUCT_ID
+                        || pid == USB_ACCESSORY_ADB_PRODUCT_ID);
+    }
+}
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbDeviceStateController.java b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbDeviceStateController.java
new file mode 100644
index 0000000..1cb394e
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbDeviceStateController.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2016 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.hardware.usb.externalmanagementtest;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.LinkedList;
+
+import dalvik.system.CloseGuard;
+
+public class UsbDeviceStateController {
+
+    public interface UsbDeviceStateListener {
+        void onDeviceResetComplete(UsbDevice device);
+        void onAoapStartComplete(UsbDevice devie);
+    }
+
+    private static final String TAG = UsbDeviceStateController.class.getSimpleName();
+
+    private static final int MAX_USB_STATE_CHANGE_WAIT = 5;
+    private static final long USB_STATE_CHANGE_WAIT_TIMEOUT_MS = 500;
+
+    private final Context mContext;
+    private final UsbDeviceStateListener mListener;
+    private final UsbManager mUsbManager;
+    private final HandlerThread mHandlerThread;
+    private final UsbStateHandler mHandler;
+    private final UsbDeviceBroadcastReceiver mUsbStateBroadcastReceiver;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private final Object mUsbConnectionChangeWait = new Object();
+    private final LinkedList<UsbDevice> mDevicesRemoved = new LinkedList<>();
+    private final LinkedList<UsbDevice> mDevicesAdded = new LinkedList<>();
+    private boolean mShouldQuit = false;
+
+    public UsbDeviceStateController(Context context, UsbDeviceStateListener listener,
+            UsbManager usbManager) {
+        mContext = context;
+        mListener = listener;
+        mUsbManager = usbManager;
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mCloseGuard.open("release");
+        mHandler = new UsbStateHandler(mHandlerThread.getLooper());
+        mUsbStateBroadcastReceiver = new UsbDeviceBroadcastReceiver();
+    }
+
+    public void init() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        mContext.registerReceiver(mUsbStateBroadcastReceiver, filter);
+    }
+
+    public void release() {
+        mCloseGuard.close();
+        mContext.unregisterReceiver(mUsbStateBroadcastReceiver);
+        synchronized (mUsbConnectionChangeWait) {
+            mShouldQuit = true;
+            mUsbConnectionChangeWait.notifyAll();
+        }
+        mHandlerThread.quit();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            mCloseGuard.warnIfOpen();
+            release();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public void startDeviceReset(UsbDevice device) {
+        mHandler.requestDeviceReset(device);
+    }
+
+    public void startAoap(AoapSwitchRequest request) {
+        mHandler.requestAoap(request);
+    }
+
+    private void doHandleDeviceReset(UsbDevice device) {
+        boolean isInAoap = AoapInterface.isDeviceInAoapMode(device);
+        UsbDevice completedDevice = null;
+        if (isInAoap) {
+            completedDevice = resetUsbDeviceAndConfirmModeChange(device);
+        } else {
+            UsbDeviceConnection conn = openConnection(device);
+            if (conn == null) {
+                throw new RuntimeException("cannot open conneciton for device: " + device);
+            } else {
+                try {
+                    if (!conn.resetDevice()) {
+                        throw new RuntimeException("resetDevice failed for devie: " + device);
+                    } else {
+                        completedDevice = device;
+                    }
+                } finally {
+                    conn.close();
+                }
+            }
+        }
+        mListener.onDeviceResetComplete(completedDevice);
+    }
+
+    private void doHandleAoapStart(AoapSwitchRequest request) {
+        UsbDevice device = request.device;
+        boolean isInAoap = AoapInterface.isDeviceInAoapMode(device);
+        if (isInAoap) {
+            device = resetUsbDeviceAndConfirmModeChange(device);
+            if (device == null) {
+                mListener.onAoapStartComplete(null);
+                return;
+            }
+        }
+        UsbDeviceConnection connection = openConnection(device);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER,
+                request.manufacturer);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL,
+                request.model);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION,
+                request.description);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION,
+                request.version);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI, request.uri);
+        AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, request.serial);
+        AoapInterface.sendAoapStart(connection);
+        device = resetUsbDeviceAndConfirmModeChange(device);
+        if (device == null) {
+            mListener.onAoapStartComplete(null);
+        }
+        if (!AoapInterface.isDeviceInAoapMode(device)) {
+            Log.w(TAG, "Device not in AOAP mode after switching: " + device);
+            mListener.onAoapStartComplete(device);
+        }
+        mListener.onAoapStartComplete(device);
+    }
+
+    private UsbDevice resetUsbDeviceAndConfirmModeChange(UsbDevice device) {
+        int retry = 0;
+        boolean removalDetected = false;
+        while (retry < MAX_USB_STATE_CHANGE_WAIT) {
+            UsbDeviceConnection connNow = openConnection(device);
+            if (connNow == null) {
+                removalDetected = true;
+                break;
+            }
+            connNow.resetDevice();
+            connNow.close();
+            synchronized (mUsbConnectionChangeWait) {
+                try {
+                    mUsbConnectionChangeWait.wait(USB_STATE_CHANGE_WAIT_TIMEOUT_MS);
+                } catch (InterruptedException e) {
+                    break;
+                }
+                if (mShouldQuit) {
+                    return null;
+                }
+                if (isDeviceRemovedLocked(device)) {
+                    removalDetected = true;
+                    break;
+                }
+            }
+            retry++;
+        }
+        if (!removalDetected) {
+            Log.w(TAG, "resetDevice failed for device, device still in the same mode: " + device);
+            return null;
+        }
+        retry = 0;
+        UsbDevice newlyAttached = null;
+        while (retry < MAX_USB_STATE_CHANGE_WAIT) {
+            synchronized (mUsbConnectionChangeWait) {
+                try {
+                    mUsbConnectionChangeWait.wait(USB_STATE_CHANGE_WAIT_TIMEOUT_MS);
+                } catch (InterruptedException e) {
+                    break;
+                }
+                if (mShouldQuit) {
+                    return null;
+                }
+                newlyAttached = checkDeviceAttachedLocked(device);
+            }
+            if (newlyAttached != null) {
+                break;
+            }
+            retry++;
+        }
+        if (newlyAttached == null) {
+            Log.w(TAG, "resetDevice failed for device, device disconnected: " + device);
+            return null;
+        }
+        return newlyAttached;
+    }
+
+    private boolean isDeviceRemovedLocked(UsbDevice device) {
+        for (UsbDevice removed : mDevicesRemoved) {
+            if (UsbUtil.isDevicesMatching(device, removed)) {
+                mDevicesRemoved.clear();
+                return true;
+            }
+        }
+        mDevicesRemoved.clear();
+        return false;
+    }
+
+    private UsbDevice checkDeviceAttachedLocked(UsbDevice device) {
+        for (UsbDevice attached : mDevicesAdded) {
+            if (UsbUtil.isTheSameDevice(device, attached)) {
+                mDevicesAdded.clear();
+                return attached;
+            }
+        }
+        mDevicesAdded.clear();
+        return null;
+    }
+
+    public UsbDeviceConnection openConnection(UsbDevice device) {
+        mUsbManager.grantPermission(device);
+        return mUsbManager.openDevice(device);
+    }
+
+    private void handleUsbDeviceAttached(UsbDevice device) {
+        synchronized (mUsbConnectionChangeWait) {
+            mDevicesAdded.add(device);
+            mUsbConnectionChangeWait.notifyAll();
+        }
+    }
+
+    private void handleUsbDeviceDetached(UsbDevice device) {
+        synchronized (mUsbConnectionChangeWait) {
+            mDevicesRemoved.add(device);
+            mUsbConnectionChangeWait.notifyAll();
+        }
+    }
+
+    private class UsbStateHandler extends Handler {
+        private final int MSG_RESET_DEVICE = 1;
+        private final int MSG_AOAP = 2;
+
+        private UsbStateHandler(Looper looper) {
+            super(looper);
+        }
+
+        private void requestDeviceReset(UsbDevice device) {
+            Message msg = obtainMessage(MSG_RESET_DEVICE, device);
+            sendMessage(msg);
+        }
+
+        private void requestAoap(AoapSwitchRequest request) {
+            Message msg = obtainMessage(MSG_AOAP, request);
+            sendMessage(msg);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_RESET_DEVICE:
+                    doHandleDeviceReset((UsbDevice) msg.obj);
+                    break;
+                case MSG_AOAP:
+                    doHandleAoapStart((AoapSwitchRequest) msg.obj);
+                    break;
+            }
+        }
+    }
+
+    private class UsbDeviceBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
+                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceDetached(device);
+            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceAttached(device);
+            }
+        }
+    }
+
+    public static class AoapSwitchRequest {
+        public final UsbDevice device;
+        public final String manufacturer;
+        public final String model;
+        public final String description;
+        public final String version;
+        public final String uri;
+        public final String serial;
+
+        public AoapSwitchRequest(UsbDevice device, String manufacturer, String model,
+                String description, String version, String uri, String serial) {
+            this.device = device;
+            this.manufacturer = manufacturer;
+            this.model = model;
+            this.description = description;
+            this.version = version;
+            this.uri = uri;
+            this.serial = serial;
+        }
+    }
+}
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbHostManagementActivity.java b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbHostManagementActivity.java
new file mode 100644
index 0000000..2d9226f
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbHostManagementActivity.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2016 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.hardware.usb.externalmanagementtest;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.net.nsd.NsdManager.DiscoveryListener;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.hardware.usb.externalmanagementtest.UsbDeviceStateController.AoapSwitchRequest;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+public class UsbHostManagementActivity extends Activity
+        implements UsbDeviceStateController.UsbDeviceStateListener {
+
+    private static final String TAG = UsbHostManagementActivity.class.getSimpleName();
+
+    private static final String AOAP_APP_PACKAGE_NAME = "com.android.hardware.usb.aoaphosttest";
+    private static final String AOAP_APP_ACTIVITY_NAME =
+            "com.android.hardware.usb.aoaphosttest.UsbAoapHostTestActivity";
+
+    private TextView mDeviceInfoText;
+    private Button mStartAoapButton;
+    private Button mStartAoapActivityButton;
+    private TextView mAoapAppLog;
+    private Button mResetUsbButton;
+    private Button mFinishButton;
+    private UsbDevice mUsbDevice = null;
+    private final UsbDeviceConnectionListener mConnectionListener =
+            new UsbDeviceConnectionListener();
+    private UsbDeviceStateController mUsbDeviceStateController;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.host_management);
+        mDeviceInfoText = (TextView) findViewById(R.id.device_info_text);
+        mStartAoapButton = (Button) findViewById(R.id.start_aoap_button);
+        mStartAoapActivityButton = (Button) findViewById(R.id.start_aoap_activity_button);
+        mAoapAppLog = (TextView) findViewById(R.id.aoap_apps_text);
+        mResetUsbButton = (Button) findViewById(R.id.reset_button);
+        mFinishButton = (Button) findViewById(R.id.finish_button);
+
+        Intent intent = getIntent();
+        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+            mUsbDevice = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+        }
+        UsbManager usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+        if (mUsbDevice == null) {
+            LinkedList<UsbDevice> devices = UsbUtil.findAllPossibleAndroidDevices(usbManager);
+            if (devices.size() > 0) {
+                mUsbDevice = devices.getLast();
+            }
+        }
+        updateDevice(mUsbDevice);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        registerReceiver(mConnectionListener, filter);
+
+        mStartAoapButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mUsbDevice == null) {
+                    return;
+                }
+                startAoap(mUsbDevice);
+            }
+        });
+        mStartAoapActivityButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mUsbDevice == null) {
+                    return;
+                }
+                startAoapActivity(mUsbDevice);
+            }
+        });
+        mResetUsbButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mUsbDevice == null) {
+                    return;
+                }
+                resetDevice(mUsbDevice);
+            }
+        });
+        mFinishButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                finish();
+            }
+        });
+
+        mUsbDeviceStateController = new UsbDeviceStateController(this, this, usbManager);
+        mUsbDeviceStateController.init();
+    }
+
+
+    private void startAoap(UsbDevice device) {
+        AoapSwitchRequest request = new AoapSwitchRequest(device, "Android", "AOAP Test App", "",
+                "1.0", "", "");
+        mUsbDeviceStateController.startAoap(request);
+    }
+
+    private void startAoapActivity(UsbDevice device) {
+        if (!AoapInterface.isDeviceInAoapMode(device)) {
+            Log.w(TAG, "Device not in AOAP mode:" + device);
+            return;
+        }
+        PackageManager pm = getPackageManager();
+        PackageInfo pi = null;
+        try {
+            pi = pm.getPackageInfo(AOAP_APP_PACKAGE_NAME, 0);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "AOAP Test app not found:" + AOAP_APP_PACKAGE_NAME);
+        }
+        int uid = pi.applicationInfo.uid;
+        UsbManager usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+        usbManager.grantPermission(device, uid);
+        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        intent.setComponent(new ComponentName(AOAP_APP_PACKAGE_NAME, AOAP_APP_ACTIVITY_NAME));
+        startActivity(intent);
+    }
+
+    private void resetDevice(UsbDevice device) {
+        Log.i(TAG, "resetDevice");
+        mUsbDeviceStateController.startDeviceReset(device);
+    }
+
+    private void dumpUsbDevices() {
+        UsbManager usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+        HashMap<String, UsbDevice> devices = usbManager.getDeviceList();
+        StringBuilder sb = new StringBuilder();
+        sb.append("Usb devices\n");
+        for (UsbDevice device : devices.values()) {
+            sb.append(device.toString() + "\n");
+        }
+        Log.i(TAG, sb.toString());
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(TAG, "onDestroy");
+        super.onDestroy();
+        unregisterReceiver(mConnectionListener);
+        mUsbDeviceStateController.release();
+    }
+
+    private void handleUsbDeviceAttached(UsbDevice device) {
+        boolean deviceReplaced = false;
+        if (mUsbDevice == null) {
+            deviceReplaced = true;
+        } else {
+            UsbManager usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+            if (!UsbUtil.isDeviceConnected(usbManager, mUsbDevice)) {
+                deviceReplaced = true;
+            }
+        }
+        if (deviceReplaced) {
+            Log.i(TAG, "device attached:" + device);
+            updateDevice(device);
+        }
+    }
+
+    private void handleUsbDeviceDetached(UsbDevice device) {
+        if (mUsbDevice != null && UsbUtil.isDevicesMatching(mUsbDevice, device)) {
+            Log.i(TAG, "device removed ");
+            updateDevice(device);
+        }
+    }
+
+    private void updateDevice(UsbDevice device) {
+        mUsbDevice = device;
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mUsbDevice == null) {
+                    mDeviceInfoText.setText("disconnected");
+                } else {
+                    mDeviceInfoText.setText(mUsbDevice.toString());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onDeviceResetComplete(UsbDevice device) {
+        updateDevice(device);
+    }
+
+
+    @Override
+    public void onAoapStartComplete(UsbDevice device) {
+        updateDevice(device);
+    }
+
+    private class UsbDeviceConnectionListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
+                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceDetached(device);
+            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceAttached(device);
+            }
+        }
+    }
+}
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbUtil.java b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbUtil.java
new file mode 100644
index 0000000..8d0f73e
--- /dev/null
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/src/com/android/hardware/usb/externalmanagementtest/UsbUtil.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 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.hardware.usb.externalmanagementtest;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.text.TextUtils;
+
+public class UsbUtil {
+    public static final String ADB_INTERFACE_NAME = "ADB Interface";
+    public static final String AOAP_INTERFACE_NAME = "Android Accessory Interface";
+    public static final String MTP_INTERFACE_NAME = "MTP";
+
+    public static LinkedList<UsbDevice> findAllPossibleAndroidDevices(UsbManager usbManager) {
+        HashMap<String, UsbDevice> devices = usbManager.getDeviceList();
+        LinkedList<UsbDevice> androidDevices = null;
+        for (UsbDevice device : devices.values()) {
+            if (possiblyAndroid(device)) {
+                if (androidDevices == null) {
+                    androidDevices = new LinkedList<>();
+                }
+                androidDevices.add(device);
+            }
+        }
+        return androidDevices;
+    }
+
+    public static boolean possiblyAndroid(UsbDevice device) {
+        int numInterfaces = device.getInterfaceCount();
+        for (int i = 0; i < numInterfaces; i++) {
+            UsbInterface usbInterface = device.getInterface(i);
+            String interfaceName = usbInterface.getName();
+            // more thorough check can be added, later
+            if (AOAP_INTERFACE_NAME.equals(interfaceName) ||
+                    ADB_INTERFACE_NAME.equals(interfaceName) ||
+                    MTP_INTERFACE_NAME.equals(interfaceName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isTheSameDevice(UsbDevice l, UsbDevice r) {
+        if (TextUtils.equals(l.getManufacturerName(), r.getManufacturerName()) &&
+                TextUtils.equals(l.getProductName(), r.getProductName()) &&
+                TextUtils.equals(l.getSerialNumber(), r.getSerialNumber())) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isDevicesMatching(UsbDevice l, UsbDevice r) {
+        if (l.getVendorId() == r.getVendorId() && l.getProductId() == r.getProductId() &&
+                TextUtils.equals(l.getSerialNumber(), r.getSerialNumber())) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isDeviceConnected(UsbManager usbManager, UsbDevice device) {
+        HashMap<String, UsbDevice> devices = usbManager.getDeviceList();
+        for (UsbDevice dev : devices.values()) {
+            if (isDevicesMatching(dev, device)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}