Merge changes from topic "BackportMockito212"

* changes:
  Allow to share classloader for non-inline dexmaker
  Update dexmaker to latest (master) state
diff --git a/Android.bp b/Android.bp
index 8b0cc40..d61b3aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -38,6 +38,96 @@
     ],
 }
 
+// Build dispatcher for Dexmaker's inline MockMaker
+java_library_static {
+    name: "dexmaker-inline-mockmaker-dispatcher",
+    sdk_version: "25",
+    srcs: ["dexmaker-mockito-inline-dispatcher/src/main/java/**/*.java"],
+}
+
+// Build agent for Dexmaker's inline MockMaker
+cc_library_shared {
+    name: "libdexmakerjvmtiagent",
+    srcs: ["dexmaker-mockito-inline/src/main/jni/**/*.cc"],
+
+    host_supported: false,
+    device_supported: true,
+
+    rtti: true,
+
+    cflags: [
+        "-std=c++11",
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-shift-count-overflow",
+        "-Wno-error=non-virtual-dtor",
+        "-Wno-sign-compare",
+        "-Wno-switch",
+        "-Wno-missing-braces",
+    ],
+
+    static_libs: [
+        "slicer",
+    ],
+
+    shared_libs: [
+        "libz",
+    ],
+
+    header_libs: [
+        "libopenjdkjvmti_headers",
+    ],
+}
+
+// Build agent for Dexmaker's inline tests
+cc_library_shared {
+    name: "libmultiplejvmtiagentsinterferenceagent",
+    srcs: ["dexmaker-mockito-inline-tests/src/main/jni/**/*.cc"],
+
+    host_supported: false,
+    device_supported: true,
+
+    rtti: true,
+
+    cflags: [
+        "-std=c++11",
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-shift-count-overflow",
+        "-Wno-error=non-virtual-dtor",
+        "-Wno-sign-compare",
+        "-Wno-switch",
+        "-Wno-missing-braces",
+    ],
+
+    static_libs: [
+        "slicer",
+    ],
+
+    shared_libs: [
+        "libz",
+    ],
+
+    header_libs: [
+        "libopenjdkjvmti_headers",
+    ],
+}
+
+// Build Dexmaker's inline MockMaker, a plugin to Mockito
+java_library_static {
+    name: "dexmaker-inline-mockmaker",
+    sdk_version: "25",
+    srcs: ["dexmaker-mockito-inline/src/main/java/**/*.java"],
+    java_resource_dirs: ["dexmaker-mockito-inline/src/main/resources"],
+    libs: [
+        "dexmaker",
+        "mockito-api",
+    ],
+    required: ["libdexmakerjvmtiagent"],
+}
+
 java_import {
     name: "dexmaker-dx-target",
     jars: ["lib/dalvik-dx-1.jar"],
diff --git a/README.version b/README.version
index 0fb9575..ce2cdcd 100644
--- a/README.version
+++ b/README.version
@@ -1,5 +1,5 @@
-URL: https://github.com/crittercism/dexmaker/
-Version: 2.2.0
+URL: https://github.com/linkedin/dexmaker/
+Version: master (d4959c215e3e2a92b478ddc72a2692cb40f3efd3)
 License: Apache 2.0
 Description:
 Dexmaker is a Java-language API for doing compile time or runtime code generation targeting the Dalvik VM. Unlike cglib or ASM, this library creates Dalvik .dex files instead of Java .class files.
@@ -9,7 +9,4 @@
 It includes a stock code generator for class proxies. If you just want to do AOP or class mocking, you don't need to mess around with bytecodes.
 
 Local Modifications:
-        Support mocking of package private classes using dexmaker.share_classloader
-        Scan for methods in extra interface hierarchy
-        Update stack trace cleaner to use new dex package name
-        Update stack trace cleaner to filter out java.lang.reflect.Proxy calls
+        Allow to share classloader via dexmaker.share_classloader system property (I8c2490c3ec8e8582dc41c486f8f7a406bd635ebb)
diff --git a/dexmaker-mockito-inline-dispatcher/build.gradle b/dexmaker-mockito-inline-dispatcher/build.gradle
new file mode 100644
index 0000000..c9667c0
--- /dev/null
+++ b/dexmaker-mockito-inline-dispatcher/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 25
+    buildToolsVersion "25.0.0"
+
+    lintOptions {
+        abortOnError false
+    }
+
+    defaultConfig {
+        applicationId "com.android.dexmaker.mockito.inline.dispatcher"
+        minSdkVersion 25
+        targetSdkVersion 25
+        versionName VERSION_NAME
+    }
+}
\ No newline at end of file
diff --git a/dexmaker-mockito-inline-dispatcher/src/main/AndroidManifest.xml b/dexmaker-mockito-inline-dispatcher/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..75024f1
--- /dev/null
+++ b/dexmaker-mockito-inline-dispatcher/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.android.dexmaker.mockito.inline.dispatcher">
+    <application />
+</manifest>
diff --git a/dexmaker-mockito-inline-dispatcher/src/main/java/com/android/dx/mockito/inline/MockMethodDispatcher.java b/dexmaker-mockito-inline-dispatcher/src/main/java/com/android/dx/mockito/inline/MockMethodDispatcher.java
new file mode 100644
index 0000000..c693992
--- /dev/null
+++ b/dexmaker-mockito-inline-dispatcher/src/main/java/com/android/dx/mockito/inline/MockMethodDispatcher.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Called by method entry hooks. Dispatches these hooks to the {@code MockMethodAdvice}.
+ */
+@SuppressWarnings("unused")
+public class MockMethodDispatcher {
+    // An instance of {@code MockMethodAdvice}
+    private Object mAdvice;
+
+    // All dispatchers for various identifiers
+    private static final ConcurrentMap<String, MockMethodDispatcher> INSTANCE =
+            new ConcurrentHashMap<>();
+
+    /**
+     * Get the dispatcher for a identifier.
+     *
+     * @param identifier identifier of the dispatcher
+     * @param instance instance that might be mocked
+     *
+     * @return dispatcher for the identifier
+     */
+    public static MockMethodDispatcher get(String identifier, Object instance) {
+        if (instance == INSTANCE) {
+            // Avoid endless loop if ConcurrentHashMap was redefined to check for being a mock.
+            return null;
+        } else {
+            return INSTANCE.get(identifier);
+        }
+    }
+
+    /**
+     * Create a new dispatcher.
+     *
+     * @param advice Advice the dispatcher should call
+     */
+    private MockMethodDispatcher(Object advice) {
+        mAdvice = advice;
+    }
+
+    /**
+     * Set up a new advice to receive calls for an identifier
+     *
+     * @param identifier a unique identifier
+     * @param advice advice the dispatcher should call
+     */
+    public static void set(String identifier, Object advice) {
+        INSTANCE.putIfAbsent(identifier, new MockMethodDispatcher(advice));
+    }
+
+    /**
+     * Calls {@code MockMethodAdvice#handle}
+     */
+    public Callable<?> handle(Object instance, Method origin, Object[] arguments) throws Throwable {
+        try {
+            return (Callable<?>) mAdvice.getClass().getMethod("handle", Object.class, Method.class,
+                    Object[].class).invoke(mAdvice, instance, origin, arguments);
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
+    }
+
+    /**
+     * Calls {@code MockMethodAdvice#isMock}
+     */
+    public boolean isMock(Object instance) {
+        try {
+            return (Boolean) mAdvice.getClass().getMethod("isMock", Object.class).invoke(mAdvice,
+                    instance);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Calls {@code MockMethodAdvice#isMocked}
+     */
+    public boolean isMocked(Object instance) {
+        try {
+            return (Boolean) mAdvice.getClass().getMethod("isMocked", Object.class).invoke(mAdvice,
+                    instance);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Calls {@code MockMethodAdvice#isOverridden}
+     */
+    public boolean isOverridden(Object instance, Method origin) {
+        try {
+            return (Boolean) mAdvice.getClass().getMethod("isOverridden", Object.class,
+                    Method.class).invoke(mAdvice, instance, origin);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Calls {@code MockMethodAdvice#getOrigin}
+     */
+    public Method getOrigin(Object mock, String instrumentedMethodWithTypeAndSignature)
+            throws Throwable {
+        return (Method) mAdvice.getClass().getMethod("getOrigin", Object.class,
+                String.class).invoke(mAdvice, mock, instrumentedMethodWithTypeAndSignature);
+    }
+}
diff --git a/dexmaker-mockito-inline-tests/AndroidManifest.xml b/dexmaker-mockito-inline-tests/AndroidManifest.xml
new file mode 100644
index 0000000..44afd30
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.android.dexmaker.mockito.inline.tests">
+    <application />
+</manifest>
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java
new file mode 100644
index 0000000..e90145f
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline.tests;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class CleanStackTrace {
+    public abstract static class TestAbstractClass {
+        public abstract String returnA();
+    }
+
+    public static class TestClass {
+        public final String returnA() {
+            return "A";
+        }
+    }
+
+    public interface TestInterface {
+        String returnA();
+    }
+
+    @Test
+    public void cleanStackTraceProxy() {
+        TestAbstractClass t = mock(TestAbstractClass.class);
+
+        try {
+            verify(t).returnA();
+        } catch (Throwable verifyLocation) {
+            try {
+                throw new Exception();
+            } catch (Exception here) {
+                assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+                        .getStackTrace()[0].getMethodName());
+            }
+        }
+    }
+
+    @Test
+    public void cleanStackTraceInline() {
+        TestClass t = mock(TestClass.class);
+
+        try {
+            verify(t).returnA();
+        } catch (Throwable verifyLocation) {
+            try {
+                throw new Exception();
+            } catch (Exception here) {
+                assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+                        .getStackTrace()[1].getMethodName());
+            }
+        }
+    }
+
+    @Test
+    public void cleanStackTraceInterface() {
+        TestInterface t = mock(TestInterface.class);
+
+        try {
+            verify(t).returnA();
+        } catch (Throwable verifyLocation) {
+            try {
+                throw new Exception();
+            } catch (Exception here) {
+                assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+                        .getStackTrace()[0].getMethodName());
+            }
+        }
+    }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java
new file mode 100644
index 0000000..fa02471
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline.tests;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.print.PrintAttributes;
+import android.printservice.PrintService;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class MockFinal {
+    @Test
+    public void mockFinalJavaMethod() throws Exception {
+        ClassLoader fakeParent = mock(ClassLoader.class);
+        ClassLoader mockClassLoader = mock(ClassLoader.class);
+
+        assertNull(mockClassLoader.getParent());
+
+        // ClassLoader#getParent is final
+        when(mockClassLoader.getParent()).thenReturn(fakeParent);
+
+        assertSame(fakeParent, mockClassLoader.getParent());
+    }
+
+    @Test
+    public void mockFinalAndroidFrameworkClass() throws Exception {
+        // PrintAttributes is final
+        PrintAttributes mockAttributes = mock(PrintAttributes.class);
+
+        assertEquals(0, mockAttributes.getColorMode());
+
+        when(mockAttributes.getColorMode()).thenReturn(42);
+
+        assertEquals(42, mockAttributes.getColorMode());
+    }
+
+    @Test
+    public void mockFinalMethodOfAndroidFrameworkClass() throws Exception {
+        IBinder fakeBinder = mock(IBinder.class);
+        PrintService mockService = mock(PrintService.class);
+
+        assertNull(mockService.onBind(new Intent()));
+
+        // PrintService#onBind is final
+        when(mockService.onBind(any(Intent.class))).thenReturn(fakeBinder);
+
+        assertSame(fakeBinder, mockService.onBind(new Intent()));
+    }
+
+    private final class FinalNonDefaultConstructorClass {
+        public FinalNonDefaultConstructorClass(int i) {
+        }
+
+        String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockNonDefaultConstructorClass() throws Exception {
+        FinalNonDefaultConstructorClass mock = mock(FinalNonDefaultConstructorClass.class);
+
+        assertNull(mock.returnA());
+        when(mock.returnA()).thenReturn("fakeA");
+
+        assertEquals("fakeA", mock.returnA());
+    }
+
+    private interface NonDefaultConstructorInterface {
+        String returnA();
+    }
+
+    @Test
+    public void mockNonDefaultConstructorInterface() throws Exception {
+        NonDefaultConstructorInterface mock = mock(NonDefaultConstructorInterface.class);
+
+        assertNull(mock.returnA());
+        when(mock.returnA()).thenReturn("fakeA");
+
+        assertEquals("fakeA", mock.returnA());
+    }
+
+    private static class SuperClass {
+        final String returnA() {
+            return "superA";
+        }
+
+        String returnB() {
+            return "superB";
+        }
+
+        String returnC() {
+            return "superC";
+        }
+    }
+
+    private static final class SubClass extends SuperClass {
+        String returnC() {
+            return "subC";
+        }
+    }
+
+    @Test
+    public void mockSubClass() throws Exception {
+        SubClass mocked = mock(SubClass.class);
+        SuperClass mockedSuper = mock(SuperClass.class);
+        SubClass nonMocked = new SubClass();
+        SuperClass nonMockedSuper = new SuperClass();
+
+        // Mock returns dummy value by default
+        assertNull(mocked.returnA());
+        assertNull(mocked.returnB());
+        assertNull(mocked.returnC());
+        assertNull(mockedSuper.returnA());
+        assertNull(mockedSuper.returnB());
+        assertNull(mockedSuper.returnC());
+
+        // Set fake values for mockedSuper
+        when(mockedSuper.returnA()).thenReturn("fakeA");
+        when(mockedSuper.returnB()).thenReturn("fakeB");
+        when(mockedSuper.returnC()).thenReturn("fakeC");
+
+        // mocked is unaffected
+        assertNull(mocked.returnA());
+        assertNull(mocked.returnB());
+        assertNull(mocked.returnC());
+
+        // Verify fake values of mockedSuper
+        assertEquals("fakeA", mockedSuper.returnA());
+        assertEquals("fakeB", mockedSuper.returnB());
+        assertEquals("fakeC", mockedSuper.returnC());
+
+        // Set fake values for mocked
+        when(mocked.returnA()).thenReturn("fake2A");
+        when(mocked.returnB()).thenReturn("fake2B");
+        when(mocked.returnC()).thenReturn("fake2C");
+
+        // Verify fake values of mocked
+        assertEquals("fake2A", mocked.returnA());
+        assertEquals("fake2B", mocked.returnB());
+        assertEquals("fake2C", mocked.returnC());
+
+        // non mocked instances are unaffected
+        assertEquals("superA", nonMocked.returnA());
+        assertEquals("superB", nonMocked.returnB());
+        assertEquals("subC", nonMocked.returnC());
+        assertEquals("superA", nonMockedSuper.returnA());
+        assertEquals("superB", nonMockedSuper.returnB());
+        assertEquals("superC", nonMockedSuper.returnC());
+    }
+
+    @Test
+    public void spySubClass() throws Exception {
+        SubClass spied = spy(SubClass.class);
+        SuperClass spiedSuper = spy(SuperClass.class);
+        SubClass nonSpied = new SubClass();
+        SuperClass nonSpiedSuper = new SuperClass();
+
+        // Spies call real method by default
+        assertEquals("superA", spied.returnA());
+        assertEquals("superB", spied.returnB());
+        assertEquals("subC", spied.returnC());
+        assertEquals("superA", spiedSuper.returnA());
+        assertEquals("superB", spiedSuper.returnB());
+        assertEquals("superC", spiedSuper.returnC());
+
+        // Set fake values for spiedSuper
+        when(spiedSuper.returnA()).thenReturn("fakeA");
+        when(spiedSuper.returnB()).thenReturn("fakeB");
+        when(spiedSuper.returnC()).thenReturn("fakeC");
+
+        // spied is unaffected
+        assertEquals("superA", spied.returnA());
+        assertEquals("superB", spied.returnB());
+        assertEquals("subC", spied.returnC());
+
+        // Verify fake values of spiedSuper
+        assertEquals("fakeA", spiedSuper.returnA());
+        assertEquals("fakeB", spiedSuper.returnB());
+        assertEquals("fakeC", spiedSuper.returnC());
+
+        // Set fake values for spied
+        when(spied.returnA()).thenReturn("fake2A");
+        when(spied.returnB()).thenReturn("fake2B");
+        when(spied.returnC()).thenReturn("fake2C");
+
+        // Verify fake values of spied
+        assertEquals("fake2A", spied.returnA());
+        assertEquals("fake2B", spied.returnB());
+        assertEquals("fake2C", spied.returnC());
+
+        // non spied instances are unaffected
+        assertEquals("superA", nonSpied.returnA());
+        assertEquals("superB", nonSpied.returnB());
+        assertEquals("subC", nonSpied.returnC());
+        assertEquals("superA", nonSpiedSuper.returnA());
+        assertEquals("superB", nonSpiedSuper.returnB());
+        assertEquals("superC", nonSpiedSuper.returnC());
+    }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java
new file mode 100644
index 0000000..aa828e5
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline.tests;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+public class MockNonPublic {
+    private interface SingleMethodInterface {
+        String returnA();
+    }
+
+    private static <T extends Class> void mockSingleMethod(T clazz) {
+        SingleMethodInterface c = (SingleMethodInterface) mock(clazz);
+        assertNull(c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+    }
+
+    private static <T extends Class> void spySingleMethod(T clazz) {
+        SingleMethodInterface c = (SingleMethodInterface) spy(clazz);
+        assertEquals("A", c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+    }
+
+    private static <T extends SingleMethodInterface> void spyWrappedSingleMethod(T original) {
+        T c = spy(original);
+        assertEquals("A", c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+
+        // original is unaffected
+        assertEquals("A", original.returnA());
+    }
+
+    private interface DualMethodInterface {
+        String returnA();
+        String returnB();
+    }
+
+    private static <T extends Class> void mockDualMethod(T clazz) {
+        DualMethodInterface c = (DualMethodInterface) mock(clazz);
+        assertNull(c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+    }
+
+    private static <T extends Class> void spyDualMethod(T clazz) {
+        DualMethodInterface c = (DualMethodInterface) spy(clazz);
+        assertEquals("A", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+    }
+
+    private static <T extends DualMethodInterface> void spyWrappedDualMethod(T original) {
+        T c = spy(original);
+        assertEquals("A", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+
+        // original is unaffected
+        assertEquals("A", original.returnA());
+        assertEquals("B", original.returnB());
+    }
+
+    private static class PrivateClass implements SingleMethodInterface {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockPrivateClass() {
+        mockSingleMethod(PrivateClass.class);
+    }
+
+    @Test
+    public void spyPrivateClass() {
+        spySingleMethod(PrivateClass.class);
+    }
+
+    @Test
+    public void spyWrappedPrivateClass() {
+        spyWrappedSingleMethod(new PrivateClass());
+    }
+
+    private interface PrivateInterface extends SingleMethodInterface {
+        String returnA();
+    }
+
+    @Test
+    public void mockPrivateInterface() {
+        mockSingleMethod(PrivateInterface.class);
+    }
+
+    private static class SubOfPrivateInterface implements PrivateInterface {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockSubOfPrivateInterface() {
+        mockSingleMethod(SubOfPrivateInterface.class);
+    }
+
+    @Test
+    public void spySubOfPrivateInterface() {
+        spySingleMethod(SubOfPrivateInterface.class);
+    }
+
+    @Test
+    public void spyWrappedSubOfPrivateInterface() {
+        spyWrappedSingleMethod(new SubOfPrivateInterface());
+    }
+
+    private static abstract class PrivateAbstractClass implements DualMethodInterface {
+        public String returnA() {
+            return "A";
+        }
+
+        public abstract String returnB();
+    }
+
+    @Test
+    public void mockPrivateAbstractClass() {
+        mockDualMethod(PrivateAbstractClass.class);
+    }
+
+    private static class SubOfPrivateAbstractClass extends PrivateAbstractClass {
+        public String returnB() {
+            return "B";
+        }
+    }
+
+    @Test
+    public void mockSubOfPrivateAbstractClass() {
+        mockDualMethod(SubOfPrivateAbstractClass.class);
+    }
+
+    @Test
+    public void spySubOfPrivateAbstractClass() {
+        spyDualMethod(SubOfPrivateAbstractClass.class);
+    }
+
+    @Test
+    public void spyWrappedSubOfPrivateAbstractClass() {
+        spyWrappedDualMethod(new SubOfPrivateAbstractClass());
+    }
+
+    static class PackagePrivateClass implements SingleMethodInterface {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockPackagePrivateClass() {
+        mockSingleMethod(PackagePrivateClass.class);
+    }
+
+    static abstract class PackagePrivateAbstractClass implements DualMethodInterface {
+        public String returnA() {
+            return "A";
+        }
+
+        public abstract String returnB();
+    }
+
+    @Test
+    public void mockPackagePrivateAbstractClass() {
+        mockDualMethod(PackagePrivateAbstractClass.class);
+    }
+
+    static class SubOfPackagePrivateAbstractClass extends PackagePrivateAbstractClass {
+        public String returnB() {
+            return "B";
+        }
+    }
+
+    @Test
+    public void mockSubOfPackagePrivateAbstractClass() {
+        mockDualMethod(SubOfPackagePrivateAbstractClass.class);
+    }
+
+    @Test
+    public void spySubOfPackagePrivateAbstractClass() {
+        spyDualMethod(SubOfPackagePrivateAbstractClass.class);
+    }
+
+    @Test
+    public void spyWrappedSubOfPackagePrivateAbstractClass() {
+        spyWrappedDualMethod(new SubOfPackagePrivateAbstractClass());
+    }
+
+    interface PackagePrivateInterface extends SingleMethodInterface {
+        String returnA();
+    }
+
+    @Test
+    public void mockPackagePrivateInterface() {
+        mockSingleMethod(PackagePrivateInterface.class);
+    }
+
+    static class SubOfPackagePrivateInterface implements PackagePrivateInterface {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockSubOfPackagePrivateInterface() {
+        mockSingleMethod(SubOfPackagePrivateInterface.class);
+    }
+
+    @Test
+    public void spySubOfPackagePrivateInterface() {
+        spySingleMethod(SubOfPackagePrivateInterface.class);
+    }
+
+    @Test
+    public void spyWrappedSubOfPackagePrivateInterface() {
+        spyWrappedSingleMethod(new SubOfPackagePrivateInterface());
+    }
+
+    // Cannot implement SingleMethodInterface as returnA would have to be public
+    public static class ClassWithPackagePrivateMethod {
+        String returnA() {
+            return "A";
+        }
+    }
+
+    @Test
+    public void mockClassWithPackagePrivateMethod() {
+        ClassWithPackagePrivateMethod c = mock(ClassWithPackagePrivateMethod.class);
+        assertNull(c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+    }
+
+    @Test
+    public void spyClassWithPackagePrivateMethod() {
+        ClassWithPackagePrivateMethod c = spy(ClassWithPackagePrivateMethod.class);
+        assertEquals("A", c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+    }
+
+    @Test
+    public void spyWrappedClassWithPackagePrivateMethod() {
+        ClassWithPackagePrivateMethod original = new ClassWithPackagePrivateMethod();
+        ClassWithPackagePrivateMethod c = spy(original);
+        assertEquals("A", c.returnA());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+
+        // original is unaffected
+        assertEquals("A", original.returnA());
+    }
+
+    // Cannot implement DualMethodInterface as returnA/returnB would have to be public
+    public static abstract class AbstractClassWithPackagePrivateMethod {
+        String returnA() {
+            return "A";
+        }
+
+        abstract String returnB();
+    }
+
+    @Test
+    public void mockAbstractClassWithPackagePrivateMethod() {
+        AbstractClassWithPackagePrivateMethod c = mock(AbstractClassWithPackagePrivateMethod.class);
+        assertNull(c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+    }
+
+    public static class SubOfAbstractClassWithPackagePrivateMethod extends
+            AbstractClassWithPackagePrivateMethod {
+        String returnB() {
+            return "B";
+        }
+    }
+
+    @Test
+    public void mockSubOfAbstractClassWithPackagePrivateMethod() {
+        SubOfAbstractClassWithPackagePrivateMethod c = mock
+                (SubOfAbstractClassWithPackagePrivateMethod.class);
+        assertNull(c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertNull(c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+    }
+
+    @Test
+    public void spySubOfAbstractClassWithPackagePrivateMethod() {
+        SubOfAbstractClassWithPackagePrivateMethod c = spy
+                (SubOfAbstractClassWithPackagePrivateMethod.class);
+        assertEquals("A", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+    }
+
+    @Test
+    public void spyWrappedSubOfAbstractClassWithPackagePrivateMethod() {
+        SubOfAbstractClassWithPackagePrivateMethod original = new
+                SubOfAbstractClassWithPackagePrivateMethod();
+        SubOfAbstractClassWithPackagePrivateMethod c = spy(original);
+        assertEquals("A", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnA()).thenReturn("fakeA");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("B", c.returnB());
+
+        when(c.returnB()).thenReturn("fakeB");
+        assertEquals("fakeA", c.returnA());
+        assertEquals("fakeB", c.returnB());
+
+        // original is unaffected
+        assertEquals("A", original.returnA());
+        assertEquals("B", original.returnB());
+    }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java
new file mode 100644
index 0000000..d66b128
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline.tests;
+
+import android.os.Build;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import dalvik.system.BaseDexClassLoader;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.mock;
+
+public class MultipleJvmtiAgentsInterference {
+    private static final String AGENT_LIB_NAME = "multiplejvmtiagentsinterferenceagent";
+
+    public class TestClass {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    @BeforeClass
+    public static void installTestAgent() throws Exception {
+        // TODO (moltmann@google.com): Replace with proper check for >= P
+        assumeTrue(Build.VERSION.CODENAME.equals("P"));
+
+        // Currently Debug.attachJvmtiAgent requires a file in the right directory
+        File copiedAgent = File.createTempFile("testagent", ".so");
+        copiedAgent.deleteOnExit();
+
+        try (InputStream is = new FileInputStream(((BaseDexClassLoader)
+                MultipleJvmtiAgentsInterference.class.getClassLoader()).findLibrary
+                (AGENT_LIB_NAME))) {
+            try (OutputStream os = new FileOutputStream(copiedAgent)) {
+                byte[] buffer = new byte[64 * 1024];
+
+                while (true) {
+                    int numRead = is.read(buffer);
+                    if (numRead == -1) {
+                        break;
+                    }
+                    os.write(buffer, 0, numRead);
+                }
+            }
+        }
+
+        // TODO (moltmann@google.com): Replace with regular method call once the API becomes public
+        Class.forName("android.os.Debug").getMethod("attachJvmtiAgent", String.class, String
+                .class).invoke(null, copiedAgent.getAbsolutePath(), null);
+    }
+
+    @Test
+    public void otherAgentTransformsWhileMocking() {
+        TestClass t = mock(TestClass.class);
+
+        assertNull(t.returnA());
+
+        // Unrelated class re-transform does not affect mocking
+        nativeRetransformClasses(new Class<?>[]{MultipleJvmtiAgentsInterference.class});
+        assertNull(t.returnA());
+
+        // Re-transform of classes that are mocked does not affect mocking
+        nativeRetransformClasses(new Class<?>[]{TestClass.class});
+        assertNull(t.returnA());
+    }
+
+    private native int nativeRetransformClasses(Class<?>[] classes);
+}
diff --git a/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc b/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc
new file mode 100644
index 0000000..a293fe7
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#include <cstring>
+#include <cstdlib>
+#include <sstream>
+
+#include "jvmti.h"
+
+#include <dex_ir.h>
+#include <writer.h>
+#include <reader.h>
+
+using namespace dex;
+
+namespace com_android_dx_mockito_inline_tests {
+    static jvmtiEnv *localJvmtiEnv;
+
+    // Converts a class name to a type descriptor
+    // (ex. "java.lang.String" to "Ljava/lang/String;")
+    static std::string
+    ClassNameToDescriptor(const char* class_name) {
+        std::stringstream ss;
+        ss << "L";
+        for (auto p = class_name; *p != '\0'; ++p) {
+            ss << (*p == '.' ? '/' : *p);
+        }
+        ss << ";";
+        return ss.str();
+    }
+
+    static void
+    Transform(jvmtiEnv *jvmti_env,
+              JNIEnv *env,
+              jclass classBeingRedefined,
+              jobject loader,
+              const char *name,
+              jobject protectionDomain,
+              jint classDataLen,
+              const unsigned char *classData,
+              jint *newClassDataLen,
+              unsigned char **newClassData) {
+        // Isolate byte code of class class. This is needed as Android usually gives us more
+        // than the class we need.
+        // Then just return the isolated byte code without modification.
+        Reader reader(classData, (size_t) classDataLen);
+
+        u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
+        reader.CreateClassIr(index);
+        std::shared_ptr<ir::DexFile> ir = reader.GetIr();
+
+        class Allocator : public Writer::Allocator {
+            jvmtiEnv *jvmti_env;
+
+        public:
+            Allocator(jvmtiEnv *jvmti_env) : Writer::Allocator(), jvmti_env(jvmti_env) {
+            }
+
+            virtual void *Allocate(size_t size) {
+                unsigned char *mem;
+                jvmti_env->Allocate(size, &mem);
+                return mem;
+            }
+
+            virtual void Free(void *ptr) { ::free(ptr); }
+        };
+
+        Allocator allocator(jvmti_env);
+        Writer writer(ir);
+        size_t newClassLen;
+        *newClassData = writer.CreateImage(&allocator, &newClassLen);
+        *newClassDataLen = (jint) newClassLen;
+    }
+
+    // Initializes the agent
+    extern "C" jint Agent_OnAttach(JavaVM *vm,
+                                   char *options,
+                                   void *reserved) {
+        jint jvmError = vm->GetEnv(reinterpret_cast<void **>(&localJvmtiEnv), JVMTI_VERSION_1_2);
+        if (jvmError != JNI_OK) {
+            return jvmError;
+        }
+
+        jvmtiCapabilities caps;
+        memset(&caps, 0, sizeof(caps));
+        caps.can_retransform_classes = 1;
+
+        jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
+        if (error != JVMTI_ERROR_NONE) {
+            return error;
+        }
+
+        jvmtiEventCallbacks cb;
+        memset(&cb, 0, sizeof(cb));
+        cb.ClassFileLoadHook = Transform;
+
+        error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
+        if (error != JVMTI_ERROR_NONE) {
+            return error;
+        }
+
+        error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,
+                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+                                                        NULL);
+        if (error != JVMTI_ERROR_NONE) {
+            return error;
+        }
+
+        return JVMTI_ERROR_NONE;
+    }
+
+
+    // Triggers retransformation of classes via this file's Transform method
+    extern "C" JNIEXPORT jint JNICALL
+    Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(
+            JNIEnv *env,
+            jobject thiz,
+            jobjectArray classes) {
+        jsize numTransformedClasses = env->GetArrayLength(classes);
+        jclass *transformedClasses = (jclass *) malloc(numTransformedClasses * sizeof(jclass));
+        for (int i = 0; i < numTransformedClasses; i++) {
+            transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes,
+                                                                                          i));
+        }
+
+        jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
+                                                             transformedClasses);
+
+        for (int i = 0; i < numTransformedClasses; i++) {
+            env->DeleteGlobalRef(transformedClasses[i]);
+        }
+        free(transformedClasses);
+
+        return error;
+    }
+}
\ No newline at end of file
diff --git a/dexmaker-mockito-inline/CMakeLists.txt b/dexmaker-mockito-inline/CMakeLists.txt
new file mode 100644
index 0000000..cd26d58
--- /dev/null
+++ b/dexmaker-mockito-inline/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.4.1)
+
+set(slicer_sources
+    external/slicer/bytecode_encoder.cc
+    external/slicer/code_ir.cc
+    external/slicer/common.cc
+    external/slicer/control_flow_graph.cc
+    external/slicer/debuginfo_encoder.cc
+    external/slicer/dex_bytecode.cc
+    external/slicer/dex_format.cc
+    external/slicer/dex_ir_builder.cc
+    external/slicer/dex_ir.cc
+    external/slicer/dex_utf8.cc
+    external/slicer/instrumentation.cc
+    external/slicer/reader.cc
+    external/slicer/tryblocks_encoder.cc
+    external/slicer/writer.cc)
+
+add_library(slicer
+            STATIC
+            ${slicer_sources})
+
+include_directories(external/jdk external/slicer/)
+
+target_link_libraries(slicer z)
+
+add_library(dexmakerjvmtiagent
+            SHARED
+            src/main/jni/dexmakerjvmtiagent/agent.cc)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -frtti -Wall -Werror -Wno-unused-parameter -Wno-shift-count-overflow -Wno-error=non-virtual-dtor -Wno-sign-compare -Wno-switch -Wno-missing-braces")
+
+target_link_libraries(dexmakerjvmtiagent slicer)
diff --git a/dexmaker-mockito-inline/build.gradle b/dexmaker-mockito-inline/build.gradle
new file mode 100644
index 0000000..54e85ec
--- /dev/null
+++ b/dexmaker-mockito-inline/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 25
+    buildToolsVersion "25.0.0"
+
+    lintOptions {
+        abortOnError false
+    }
+
+    defaultConfig {
+        minSdkVersion 25
+        targetSdkVersion 25
+        versionName VERSION_NAME
+    }
+
+    externalNativeBuild {
+        cmake {
+            path 'CMakeLists.txt'
+        }
+    }
+
+}
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile project(':dexmaker')
+    compile 'org.mockito:mockito-core:2.12.0'
+}
+
diff --git a/dexmaker-mockito-inline/src/main/AndroidManifest.xml b/dexmaker-mockito-inline/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9295bf2
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.android.dx.mockito.inline">
+    <application />
+</manifest>
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/ClassTransformer.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/ClassTransformer.java
new file mode 100644
index 0000000..f719304
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/ClassTransformer.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.concurrent.WeakConcurrentMap;
+import org.mockito.internal.util.concurrent.WeakConcurrentSet;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Adds entry hooks (that eventually call into
+ * {@link MockMethodAdvice#handle(Object, Method, Object[])} to all non-abstract methods of the
+ * supplied classes.
+ *
+ * <p></p>Transforming a class to adding entry hooks follow the following simple steps:
+ * <ol>
+ * <li>{@link #mockClass(MockFeatures)}</li>
+ * <li>{@link JvmtiAgent#requestTransformClasses(Class[])}</li>
+ * <li>{@link JvmtiAgent#nativeRetransformClasses(Class[])}</li>
+ * <li>agent.cc::Transform</li>
+ * <li>{@link JvmtiAgent#runTransformers(ClassLoader, String, Class, ProtectionDomain, byte[])}</li>
+ * <li>{@link #transform(Class, byte[])}</li>
+ * <li>{@link #nativeRedefine(String, byte[])}</li>
+ * </ol>
+ *
+ */
+class ClassTransformer {
+    // Some classes are so deeply optimized inside the runtime that they cannot be transformed
+    private static final Set<Class<? extends java.io.Serializable>> EXCLUDES = new HashSet<>(
+            Arrays.asList(Class.class,
+                    Boolean.class,
+                    Byte.class,
+                    Short.class,
+                    Character.class,
+                    Integer.class,
+                    Long.class,
+                    Float.class,
+                    Double.class,
+                    String.class));
+    private final static Random random = new Random();
+
+    /** Jvmti agent responsible for triggering transformation s*/
+    private final JvmtiAgent agent;
+
+    /** Types that have already be transformed */
+    private final WeakConcurrentSet<Class<?>> mockedTypes;
+
+    /**
+     * A unique identifier that is baked into the transformed classes. The entry hooks will then
+     * pass this identifier to
+     * {@code com.android.dx.mockito.inline.MockMethodDispatcher#get(String, Object)} to
+     * find the advice responsible for handling the method call interceptions.
+     */
+    private final String identifier;
+
+    /**
+     * We can only have a single transformation going on at a time, hence synchronize the
+     * transformation process via this lock.
+     *
+     * @see #mockClass(MockFeatures)
+     */
+    private final static Object lock = new Object();
+
+    /**
+     * Create a new generator.
+     *
+     * Creating more than one generator might cause transformations to overwrite each other.
+     *
+     * @param agent agent used to trigger transformations
+     * @param dispatcherClass {@code com.android.dx.mockito.inline.MockMethodDispatcher}
+     *                        that will dispatch method calls that might need to get intercepted.
+     * @param mocks list of mocked objects. As all objects of a class use the same transformed
+     *              bytecode the {@link MockMethodAdvice} needs to check this list if a object is
+     *              mocked or not.
+     */
+    ClassTransformer(JvmtiAgent agent, Class dispatcherClass,
+                     WeakConcurrentMap<Object, InvocationHandlerAdapter> mocks) {
+        this.agent = agent;
+        mockedTypes = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.INLINE);
+        identifier = Long.toString(random.nextLong());
+        MockMethodAdvice advice = new MockMethodAdvice(mocks);
+
+        try {
+            dispatcherClass.getMethod("set", String.class, Object.class).invoke(null, identifier,
+                    advice);
+        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+
+        agent.addTransformer(this);
+    }
+
+    /**
+     * Trigger the process to add entry hooks to a class (and all its parents).
+     *
+     * @param features specification what to mock
+     */
+    <T> void mockClass(MockFeatures<T> features) {
+        boolean subclassingRequired = !features.interfaces.isEmpty()
+                || Modifier.isAbstract(features.mockedType.getModifiers());
+
+        if (subclassingRequired
+                && !features.mockedType.isArray()
+                && !features.mockedType.isPrimitive()
+                && Modifier.isFinal(features.mockedType.getModifiers())) {
+            throw new MockitoException("Unsupported settings with this type '"
+                    + features.mockedType.getName() + "'");
+        }
+
+        synchronized (lock) {
+            Set<Class<?>> types = new HashSet<>();
+            Class<?> type = features.mockedType;
+
+            do {
+                boolean wasAdded = mockedTypes.add(type);
+
+                if (wasAdded) {
+                    if (!EXCLUDES.contains(type)) {
+                        types.add(type);
+                    }
+
+                    type = type.getSuperclass();
+                } else {
+                    break;
+                }
+            } while (type != null && !type.isInterface());
+
+            if (!types.isEmpty()) {
+                try {
+                    agent.requestTransformClasses(types.toArray(new Class<?>[types.size()]));
+                } catch (UnmodifiableClassException exception) {
+                    for (Class<?> failed : types) {
+                        mockedTypes.remove(failed);
+                    }
+
+                    throw new MockitoException("Could not modify all classes " + types, exception);
+                }
+            }
+        }
+    }
+
+    /**
+     * Add entry hooks to all methods of a class.
+     *
+     * <p>Called by the agent after triggering the transformation via
+     * {@link #mockClass(MockFeatures)}.
+     *
+     * @param classBeingRedefined class the hooks should be added to
+     * @param classfileBuffer original byte code of the class
+     *
+     * @return transformed class
+     */
+    byte[] transform(Class<?> classBeingRedefined, byte[] classfileBuffer) throws
+            IllegalClassFormatException {
+        if (classBeingRedefined == null
+                || !mockedTypes.contains(classBeingRedefined)) {
+            return null;
+        } else {
+            try {
+                return nativeRedefine(identifier, classfileBuffer);
+            } catch (Throwable throwable) {
+                throw new IllegalClassFormatException();
+            }
+        }
+    }
+
+    private native byte[] nativeRedefine(String identifier, byte[] original);
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/DexmakerStackTraceCleaner.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/DexmakerStackTraceCleaner.java
new file mode 100644
index 0000000..2757a9e
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/DexmakerStackTraceCleaner.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+import org.mockito.exceptions.stacktrace.StackTraceCleaner;
+import org.mockito.plugins.StackTraceCleanerProvider;
+
+/**
+ * Cleans out mockito internal elements out of stack traces. This creates stack traces as if mockito
+ * would have not intercepted any calls.
+ */
+public final class DexmakerStackTraceCleaner implements StackTraceCleanerProvider {
+    @Override
+    public StackTraceCleaner getStackTraceCleaner(final StackTraceCleaner defaultCleaner) {
+        return new StackTraceCleaner() {
+            @Override
+            public boolean isIn(StackTraceElement candidate) {
+                String className = candidate.getClassName();
+
+                return defaultCleaner.isIn(candidate)
+                        // dexmaker class proxies
+                        && !className.endsWith("_Proxy")
+
+                        && !className.startsWith("java.lang.reflect.Method")
+                        && !className.startsWith("java.lang.reflect.Proxy")
+                        && !(className.startsWith("com.android.dx.mockito.")
+                             // Do not clean unit tests
+                             && !className.startsWith("com.android.dx.mockito.inline.tests"))
+
+                        // dalvik interface proxies
+                        && !className.startsWith("$Proxy")
+                        && !className.matches(".*\\.\\$Proxy[\\d]+");
+            }
+        };
+    }
+
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/IllegalClassFormatException.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/IllegalClassFormatException.java
new file mode 100644
index 0000000..8da7b2b
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/IllegalClassFormatException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+/**
+ * Exception thrown if a class cannot be transformed by
+ * {@link ClassTransformer#transform(Class, byte[])}
+ */
+class IllegalClassFormatException extends Exception {
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java
new file mode 100644
index 0000000..d5be235
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+import com.android.dx.stock.ProxyBuilder;
+import com.android.dx.stock.ProxyBuilder.MethodSetEntry;
+
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.creation.instance.Instantiator;
+import org.mockito.internal.util.Platform;
+import org.mockito.internal.util.concurrent.WeakConcurrentMap;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.plugins.MockMaker;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Generates mock instances on Android's runtime that can mock final methods.
+ *
+ * <p>This is done by transforming the byte code of the classes to add method entry hooks.
+ */
+public final class InlineDexmakerMockMaker implements MockMaker {
+    private static final String DISPATCHER_CLASS_NAME =
+            "com.android.dx.mockito.inline.MockMethodDispatcher";
+    private static final String DISPATCHER_JAR = "dispatcher.jar";
+
+    /** {@link com.android.dx.mockito.inline.JvmtiAgent} set up during one time init */
+    private static final JvmtiAgent AGENT;
+
+    /** Error  during one time init or {@code null} if init was successful*/
+    private static final Throwable INITIALIZATION_ERROR;
+
+    /**
+     * Class injected into the bootstrap classloader. All entry hooks added to methods will call
+     * this class.
+     */
+    private static final Class DISPATCHER_CLASS;
+
+    /*
+     * One time setup to allow the system to mocking via this mock maker.
+     */
+    static {
+        JvmtiAgent agent;
+        Throwable initializationError = null;
+        Class dispatcherClass = null;
+        try {
+            try {
+                agent = new JvmtiAgent();
+
+                try (InputStream is = InlineDexmakerMockMaker.class.getClassLoader()
+                        .getResource(DISPATCHER_JAR).openStream()) {
+                    agent.appendToBootstrapClassLoaderSearch(is);
+                }
+
+                try {
+                    dispatcherClass = Class.forName(DISPATCHER_CLASS_NAME, true,
+                            Object.class.getClassLoader());
+
+                    if (dispatcherClass == null) {
+                        throw new IllegalStateException(DISPATCHER_CLASS_NAME
+                                + " could not be loaded");
+                    }
+                } catch (ClassNotFoundException cnfe) {
+                    throw new IllegalStateException(
+                            "Mockito failed to inject the MockMethodDispatcher class into the "
+                            + "bootstrap class loader\n\nIt seems like your current VM does not "
+                            + "support the jvmti API correctly.", cnfe);
+                }
+            } catch (IOException ioe) {
+                throw new IllegalStateException(
+                        "Mockito could not self-attach a jvmti agent to the current VM. This "
+                        + "feature is required for inline mocking.\nThis error occured due to an "
+                        + "I/O error during the creation of this agent: " + ioe + "\n\n"
+                        + "Potentially, the current VM does not support the jvmti API correctly",
+                        ioe);
+            }
+        } catch (Throwable throwable) {
+            agent = null;
+            initializationError = throwable;
+        }
+
+        AGENT = agent;
+        INITIALIZATION_ERROR = initializationError;
+        DISPATCHER_CLASS = dispatcherClass;
+    }
+
+    /**
+     * All currently active mocks. We modify the class's byte code. Some objects of the class are
+     * modified, some are not. This list helps the {@link MockMethodAdvice} help figure out if a
+     * object's method calls should be intercepted.
+     */
+    private final WeakConcurrentMap<Object, InvocationHandlerAdapter> mocks;
+
+    /**
+     * Class doing the actual byte code transformation.
+     */
+    private final ClassTransformer classTransformer;
+
+    /**
+     * Create a new mock maker.
+     */
+    public InlineDexmakerMockMaker() {
+        if (INITIALIZATION_ERROR != null) {
+            throw new RuntimeException(
+                    "Could not initialize inline mock maker.\n"
+                    + "\n"
+                    + Platform.describe(), INITIALIZATION_ERROR);
+        }
+
+        mocks = new WeakConcurrentMap.WithInlinedExpunction<>();
+        classTransformer = new ClassTransformer(AGENT, DISPATCHER_CLASS, mocks);
+    }
+
+    /**
+     * Get methods to proxy.
+     *
+     * <p>Only abstract methods will need to get proxied as all other methods will get an entry
+     * hook.
+     *
+     * @param settings description of the current mocking process.
+     *
+     * @return methods to proxy.
+     */
+    private <T> Method[] getMethodsToProxy(MockCreationSettings<T> settings) {
+        Set<MethodSetEntry> abstractMethods = new HashSet<>();
+        Set<MethodSetEntry> nonAbstractMethods = new HashSet<>();
+
+        Class<?> superClass = settings.getTypeToMock();
+        while (superClass != null) {
+            for (Method method : superClass.getDeclaredMethods()) {
+                if (Modifier.isAbstract(method.getModifiers())
+                        && !nonAbstractMethods.contains(new MethodSetEntry(method))) {
+                    abstractMethods.add(new MethodSetEntry(method));
+                } else {
+                    nonAbstractMethods.add(new MethodSetEntry(method));
+                }
+            }
+
+            superClass = superClass.getSuperclass();
+        }
+
+        for (Class<?> i : settings.getTypeToMock().getInterfaces()) {
+            for (Method method : i.getMethods()) {
+                if (!nonAbstractMethods.contains(new MethodSetEntry(method))) {
+                    abstractMethods.add(new MethodSetEntry(method));
+                }
+            }
+        }
+
+        for (Class<?> i : settings.getExtraInterfaces()) {
+            for (Method method : i.getMethods()) {
+                if (!nonAbstractMethods.contains(new MethodSetEntry(method))) {
+                    abstractMethods.add(new MethodSetEntry(method));
+                }
+            }
+        }
+
+        Method[] methodsToProxy = new Method[abstractMethods.size()];
+        int i = 0;
+        for (MethodSetEntry entry : abstractMethods) {
+            methodsToProxy[i++] = entry.originalMethod;
+        }
+
+        return methodsToProxy;
+    }
+
+    @Override
+    public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
+        Class<T> typeToMock = settings.getTypeToMock();
+        Set<Class<?>> interfacesSet = settings.getExtraInterfaces();
+        Class<?>[] extraInterfaces = interfacesSet.toArray(new Class[interfacesSet.size()]);
+        InvocationHandlerAdapter handlerAdapter = new InvocationHandlerAdapter(handler);
+
+        T mock;
+        if (typeToMock.isInterface()) {
+            // support interfaces via java.lang.reflect.Proxy
+            Class[] classesToMock = new Class[extraInterfaces.length + 1];
+            classesToMock[0] = typeToMock;
+            System.arraycopy(extraInterfaces, 0, classesToMock, 1, extraInterfaces.length);
+
+            // newProxyInstance returns the type of typeToMock
+            mock = (T) Proxy.newProxyInstance(typeToMock.getClassLoader(), classesToMock,
+                    handlerAdapter);
+        } else {
+            boolean subclassingRequired = !interfacesSet.isEmpty()
+                    || Modifier.isAbstract(typeToMock.getModifiers());
+
+            // Add entry hooks to non-abstract methods.
+            classTransformer.mockClass(MockFeatures.withMockFeatures(typeToMock, interfacesSet));
+
+            Class<? extends T> proxyClass;
+
+            Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
+
+            if (subclassingRequired) {
+                try {
+                    // support abstract methods via dexmaker's ProxyBuilder
+                    proxyClass = ProxyBuilder.forClass(typeToMock).implementing(extraInterfaces)
+                            .onlyMethods(getMethodsToProxy(settings)).withSharedClassLoader()
+                            .buildProxyClass();
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new MockitoException("Failed to mock " + typeToMock, e);
+                }
+
+                try {
+                    mock = instantiator.newInstance(proxyClass);
+                } catch (org.mockito.internal.creation.instance.InstantiationException e) {
+                    throw new MockitoException("Unable to create mock instance of type '"
+                            + proxyClass.getSuperclass().getSimpleName() + "'", e);
+                }
+
+                ProxyBuilder.setInvocationHandler(mock, handlerAdapter);
+            } else {
+                try {
+                    mock = instantiator.newInstance(typeToMock);
+                } catch (org.mockito.internal.creation.instance.InstantiationException e) {
+                    throw new MockitoException("Unable to create mock instance of type '"
+                            + typeToMock.getSimpleName() + "'", e);
+                }
+            }
+        }
+
+        mocks.put(mock, handlerAdapter);
+        return mock;
+    }
+
+    @Override
+    public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) {
+        InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
+        if (adapter != null) {
+            adapter.setHandler(newHandler);
+        }
+    }
+
+    @Override
+    public TypeMockability isTypeMockable(final Class<?> type) {
+        return new TypeMockability() {
+            @Override
+            public boolean mockable() {
+                return !type.isPrimitive() && type != String.class;
+            }
+
+            @Override
+            public String nonMockableReason() {
+                if (type.isPrimitive()) {
+                    return "primitive type";
+                }
+
+                if (type == String.class) {
+                    return "string";
+                }
+
+                return "not handled type";
+            }
+        };
+    }
+
+    @Override
+    public MockHandler getHandler(Object mock) {
+        InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
+        return adapter != null ? adapter.getHandler() : null;
+    }
+
+    /**
+     * Get the {@link InvocationHandlerAdapter} registered for a mock.
+     *
+     * @param instance instance that might be mocked
+     *
+     * @return adapter for this mock, or {@code null} if instance is not mocked
+     */
+    private InvocationHandlerAdapter getInvocationHandlerAdapter(Object instance) {
+        if (instance == null) {
+            return null;
+        }
+
+        return mocks.get(instance);
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InterceptedInvocation.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InterceptedInvocation.java
new file mode 100644
index 0000000..a6d11b5
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InterceptedInvocation.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package com.android.dx.mockito.inline;
+
+import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.exceptions.VerificationAwareInvocation;
+import org.mockito.internal.invocation.ArgumentsProcessor;
+import org.mockito.internal.invocation.MockitoMethod;
+import org.mockito.internal.reporting.PrintSettings;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.Location;
+import org.mockito.invocation.StubInfo;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import static org.mockito.internal.exceptions.Reporter.cannotCallAbstractRealMethod;
+
+/**
+ * {@link Invocation} used when intercepting methods from an method entry hook.
+ */
+class InterceptedInvocation implements Invocation, VerificationAwareInvocation {
+    /** The mocked instance */
+    private final Object mock;
+
+    /** The method invoked */
+    private final MockitoMethod method;
+
+    /** expanded arguments to the method */
+    private final Object[] arguments;
+
+    /** raw arguments to the method */
+    private final Object[] rawArguments;
+
+    /** The super method */
+    private final SuperMethod superMethod;
+
+    /** sequence number of the invocation (different for each invocation) */
+    private final int sequenceNumber;
+
+    /** the location of the invocation (i.e. the stack trace) */
+    private final Location location;
+
+    /** Was this invocation {@link #markVerified() marked as verified} */
+    private boolean verified;
+
+    /** Should this be {@link #ignoreForVerification()} ignored for verification?} */
+    private boolean isIgnoredForVerification;
+
+    /** The stubinfo is this was {@link #markStubbed(StubInfo) markes as stubbed}*/
+    private StubInfo stubInfo;
+
+    /**
+     * Create a new invocation.
+     *
+     * @param mock mocked instance
+     * @param method method invoked
+     * @param arguments arguments to the method
+     * @param superMethod super method
+     * @param sequenceNumber sequence number of the invocation
+     */
+    InterceptedInvocation(Object mock, MockitoMethod method, Object[] arguments,
+                          SuperMethod superMethod, int sequenceNumber) {
+        this.mock = mock;
+        this.method = method;
+        this.arguments = ArgumentsProcessor.expandArgs(method, arguments);
+        this.rawArguments = arguments;
+        this.superMethod = superMethod;
+        this.sequenceNumber = sequenceNumber;
+        location = new LocationImpl();
+    }
+
+    @Override
+    public boolean isVerified() {
+        return verified || isIgnoredForVerification;
+    }
+
+    @Override
+    public int getSequenceNumber() {
+        return sequenceNumber;
+    }
+
+    @Override
+    public Location getLocation() {
+        return location;
+    }
+
+    @Override
+    public Object[] getRawArguments() {
+        return rawArguments;
+    }
+
+    @Override
+    public Class<?> getRawReturnType() {
+        return method.getReturnType();
+    }
+
+    @Override
+    public void markVerified() {
+        verified = true;
+    }
+
+    @Override
+    public StubInfo stubInfo() {
+        return stubInfo;
+    }
+
+    @Override
+    public void markStubbed(StubInfo stubInfo) {
+        this.stubInfo = stubInfo;
+    }
+
+    @Override
+    public boolean isIgnoredForVerification() {
+        return isIgnoredForVerification;
+    }
+
+    @Override
+    public void ignoreForVerification() {
+        isIgnoredForVerification = true;
+    }
+
+    @Override
+    public Object getMock() {
+        return mock;
+    }
+
+    @Override
+    public Method getMethod() {
+        return method.getJavaMethod();
+    }
+
+    @Override
+    public Object[] getArguments() {
+        return arguments;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getArgument(int index) {
+        return (T) arguments[index];
+    }
+
+    @Override
+    public Object callRealMethod() throws Throwable {
+        if (!superMethod.isInvokable()) {
+            throw cannotCallAbstractRealMethod();
+        }
+        return superMethod.invoke();
+    }
+
+    @Override
+    public int hashCode() {
+        // TODO SF we need to provide hash code implementation so that there are no unexpected,
+        //         slight perf issues
+        return 1;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !o.getClass().equals(this.getClass())) {
+            return false;
+        }
+        InterceptedInvocation other = (InterceptedInvocation) o;
+        return this.mock.equals(other.mock)
+                && this.method.equals(other.method)
+                && this.equalArguments(other.arguments);
+    }
+
+    private boolean equalArguments(Object[] arguments) {
+        return Arrays.equals(arguments, this.arguments);
+    }
+
+    @Override
+    public String toString() {
+        return new PrintSettings().print(ArgumentsProcessor.argumentsToMatchers(getArguments()),
+                this);
+    }
+
+    interface SuperMethod extends Serializable {
+        boolean isInvokable();
+
+        Object invoke() throws Throwable;
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InvocationHandlerAdapter.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InvocationHandlerAdapter.java
new file mode 100644
index 0000000..0c9af71
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InvocationHandlerAdapter.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2012 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.dx.mockito.inline;
+
+import org.mockito.internal.creation.DelegatingMethod;
+import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.invocation.ArgumentsProcessor;
+import org.mockito.internal.progress.SequenceNumber;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.Location;
+import org.mockito.invocation.MockHandler;
+import org.mockito.invocation.StubInfo;
+import org.mockito.mock.MockCreationSettings;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import static org.mockito.internal.exceptions.Reporter.cannotCallAbstractRealMethod;
+
+/**
+ * Handles proxy and entry hook method invocations added by
+ * {@link InlineDexmakerMockMaker#createMock(MockCreationSettings, MockHandler)}
+ */
+final class InvocationHandlerAdapter implements InvocationHandler {
+    private MockHandler handler;
+
+    InvocationHandlerAdapter(MockHandler handler) {
+        this.handler = handler;
+    }
+
+    private static boolean isEqualsMethod(Method method) {
+        return method.getName().equals("equals")
+                && method.getParameterTypes().length == 1
+                && method.getParameterTypes()[0] == Object.class;
+    }
+
+    private static boolean isHashCodeMethod(Method method) {
+        return method.getName().equals("hashCode")
+                && method.getParameterTypes().length == 0;
+    }
+
+    /**
+     * Intercept a method call. Called <u>before</u> a method is called by the method entry hook.
+     *
+     * <p>This does the same as {@link #invoke(Object, Method, Object[])} but this handles methods
+     * that got and entry hook.
+     *
+     * @param mock mocked object
+     * @param method method that was called
+     * @param args arguments to the method
+     * @param superMethod The super method
+     *
+     * @return mocked result
+     * @throws Throwable An exception if thrown
+     */
+    Object interceptEntryHook(Object mock, Method method, Object[] args,
+                              InterceptedInvocation.SuperMethod superMethod) throws Throwable {
+        return handler.handle(new InterceptedInvocation(mock, new DelegatingMethod(method), args,
+                superMethod, SequenceNumber.next()));
+    }
+
+    /**
+     * Intercept a method call. Called <u>before</u> a method is called by the proxied method.
+     *
+     * <p>This does the same as {@link #interceptEntryHook(Object, Method, Object[],
+     * InterceptedInvocation.SuperMethod)} but this handles proxied methods. We only proxy abstract
+     * methods.
+     *
+     * @param proxy proxies object
+     * @param method method that was called
+     * @param argsIn arguments to the method
+     *
+     * @return mocked result
+     * @throws Throwable An exception if thrown
+     */
+    @Override
+    public Object invoke(final Object proxy, final Method method, Object[] argsIn) throws
+            Throwable {
+        // args can be null if the method invoked has no arguments, but Mockito expects a non-null
+        // array
+        final Object[] args = argsIn != null ? argsIn : new Object[0];
+        if (isEqualsMethod(method)) {
+            return proxy == args[0];
+        } else if (isHashCodeMethod(method)) {
+            return System.identityHashCode(proxy);
+        }
+
+        return handler.handle(new ProxyInvocation(proxy, method, args, new DelegatingMethod
+                (method), SequenceNumber.next(), new LocationImpl()));
+    }
+
+    /**
+     * Get the handler registered with this adapter.
+     *
+     * @return handler
+     */
+    MockHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Set a new handler for this adapter.
+     */
+    void setHandler(MockHandler handler) {
+        this.handler = handler;
+    }
+
+    /**
+     * Invocation on a proxy
+     */
+    private class ProxyInvocation implements Invocation {
+        private final Object proxy;
+        private final Method method;
+        private final Object[] rawArgs;
+        private final int sequenceNumber;
+        private final Location location;
+        private final Object[] args;
+
+        private StubInfo stubInfo;
+        private boolean isIgnoredForVerification;
+        private boolean verified;
+
+        private ProxyInvocation(Object proxy, Method method, Object[] rawArgs, DelegatingMethod
+                                mockitoMethod, int sequenceNumber, Location location) {
+            this.rawArgs = rawArgs;
+            this.proxy = proxy;
+            this.method = method;
+            this.sequenceNumber = sequenceNumber;
+            this.location = location;
+            args = ArgumentsProcessor.expandArgs(mockitoMethod, rawArgs);
+        }
+
+        @Override
+        public Object getMock() {
+            return proxy;
+        }
+
+        @Override
+        public Method getMethod() {
+            return method;
+        }
+
+        @Override
+        public Object[] getArguments() {
+            return args;
+        }
+
+        @Override
+        public <T> T getArgument(int index) {
+            return (T)args[index];
+        }
+
+        @Override
+        public Object callRealMethod() throws Throwable {
+            if (Modifier.isAbstract(method.getModifiers())) {
+                throw cannotCallAbstractRealMethod();
+            }
+            return method.invoke(proxy, rawArgs);
+        }
+
+        @Override
+        public boolean isVerified() {
+            return verified || isIgnoredForVerification;
+        }
+
+        @Override
+        public int getSequenceNumber() {
+            return sequenceNumber;
+        }
+
+        @Override
+        public Location getLocation() {
+            return location;
+        }
+
+        @Override
+        public Object[] getRawArguments() {
+            return rawArgs;
+        }
+
+        @Override
+        public Class<?> getRawReturnType() {
+            return method.getReturnType();
+        }
+
+        @Override
+        public void markVerified() {
+            verified = true;
+        }
+
+        @Override
+        public StubInfo stubInfo() {
+            return stubInfo;
+        }
+
+        @Override
+        public void markStubbed(StubInfo stubInfo) {
+            this.stubInfo = stubInfo;
+        }
+
+        @Override
+        public boolean isIgnoredForVerification() {
+            return isIgnoredForVerification;
+        }
+
+        @Override
+        public void ignoreForVerification() {
+            isIgnoredForVerification = true;
+        }
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/JvmtiAgent.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/JvmtiAgent.java
new file mode 100644
index 0000000..1b4a550
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/JvmtiAgent.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+import android.os.Build;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+
+import dalvik.system.BaseDexClassLoader;
+
+/**
+ * Interface to the native jvmti agent in agent.cc
+ */
+class JvmtiAgent {
+    private static final String AGENT_LIB_NAME = "dexmakerjvmtiagent";
+
+    private static final Object lock = new Object();
+
+    /** Registered byte code transformers */
+    private final ArrayList<ClassTransformer> transformers = new ArrayList<>();
+
+    private native void nativeRegisterTransformerHook();
+
+    /**
+     * Enable jvmti and load agent.
+     *
+     * <p><b>If there are more than agent transforming classes the other agent might remove
+     * transformations added by this agent.</b>
+     *
+     * @throws IOException If jvmti could not be enabled or agent could not be loaded
+     */
+    JvmtiAgent() throws IOException {
+        // TODO (moltmann@google.com): Replace with proper check for >= P
+        if (!Build.VERSION.CODENAME.equals("P")) {
+            throw new IOException("Requires Android P. Build is " + Build.VERSION.CODENAME);
+        }
+
+        Throwable loadJvmtiException = null;
+
+        ClassLoader cl = JvmtiAgent.class.getClassLoader();
+        if (!(cl instanceof BaseDexClassLoader)) {
+            throw new IOException("Could not load jvmti plugin as JvmtiAgent class was not loaded "
+                    + "by a BaseDexClassLoader");
+        }
+
+        // Currently Debug.attachJvmtiAgent requires a file in the right directory
+        File copiedAgent = File.createTempFile("agent", ".so");
+        copiedAgent.deleteOnExit();
+
+        try (InputStream is = new FileInputStream(
+                ((BaseDexClassLoader) cl).findLibrary(AGENT_LIB_NAME))) {
+            try (OutputStream os = new FileOutputStream(copiedAgent)) {
+                byte[] buffer = new byte[64 * 1024];
+
+                while (true) {
+                    int numRead = is.read(buffer);
+                    if (numRead == -1) {
+                        break;
+                    }
+                    os.write(buffer, 0, numRead);
+                }
+            }
+        }
+
+        try {
+            /*
+             * TODO (moltmann@google.com): Replace with regular method call once the API becomes
+             *                             public
+             */
+            Class.forName("android.os.Debug").getMethod("attachJvmtiAgent", String.class,
+                    String.class).invoke(null, copiedAgent.getAbsolutePath(), null);
+        } catch (InvocationTargetException e) {
+            loadJvmtiException = e.getCause();
+        } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) {
+            loadJvmtiException = e;
+        }
+
+        if (loadJvmtiException != null) {
+            if (loadJvmtiException instanceof IOException) {
+                throw (IOException)loadJvmtiException;
+            } else {
+                throw new IOException("Could not load jvmti plugin",
+                        loadJvmtiException);
+            }
+        }
+
+        nativeRegisterTransformerHook();
+    }
+
+    private native void nativeUnregisterTransformerHook();
+
+    @Override
+    protected void finalize() throws Throwable {
+        nativeUnregisterTransformerHook();
+    }
+
+    private native static void nativeAppendToBootstrapClassLoaderSearch(String absolutePath);
+
+    /**
+     * Append the jar to be bootstrap class load. This makes the classes in the jar behave as if
+     * they are loaded from the BCL. E.g. classes from java.lang can now call the classes in the
+     * jar.
+     *
+     * @param jarStream stream of jar to be added
+     */
+    void appendToBootstrapClassLoaderSearch(InputStream jarStream) throws IOException {
+        File jarFile = File.createTempFile("mockito-boot", ".jar");
+        jarFile.deleteOnExit();
+
+        byte[] buffer = new byte[64 * 1024];
+        try (OutputStream os = new FileOutputStream(jarFile)) {
+            while (true) {
+                int numRead = jarStream.read(buffer);
+                if (numRead == -1) {
+                    break;
+                }
+
+                os.write(buffer, 0, numRead);
+            }
+        }
+
+        nativeAppendToBootstrapClassLoaderSearch(jarFile.getAbsolutePath());
+    }
+
+    /**
+     * Ask the agent to trigger transformation of some classes. This will extract the byte code of
+     * the classes and the call back the {@link #addTransformer(ClassTransformer) transformers} for
+     * each individual class.
+     *
+     * @param classes The classes to transform
+     *
+     * @throws UnmodifiableClassException If one of the classes can not be transformed
+     */
+    void requestTransformClasses(Class<?>[] classes) throws UnmodifiableClassException {
+        synchronized (lock) {
+            try {
+                nativeRetransformClasses(classes);
+            } catch (RuntimeException e) {
+                throw new UnmodifiableClassException(e);
+            }
+        }
+    }
+
+    /**
+     * Register a transformer. These are called for each class when a transformation was triggered
+     * via {@link #requestTransformClasses(Class[])}.
+     *
+     * @param transformer the transformer to add.
+     */
+    void addTransformer(ClassTransformer transformer) {
+        transformers.add(transformer);
+    }
+
+    // called by JNI
+    @SuppressWarnings("unused")
+    public byte[] runTransformers(ClassLoader loader, String className,
+                                  Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+                                  byte[] classfileBuffer) throws IllegalClassFormatException {
+        byte[] transformedByteCode = classfileBuffer;
+        for (ClassTransformer transformer : transformers) {
+            transformedByteCode = transformer.transform(classBeingRedefined, transformedByteCode);
+        }
+
+        return transformedByteCode;
+    }
+
+    private native void nativeRetransformClasses(Class<?>[] classes);
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockFeatures.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockFeatures.java
new file mode 100644
index 0000000..01d01bf
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockFeatures.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package com.android.dx.mockito.inline;
+
+import java.util.Collections;
+import java.util.Set;
+
+class MockFeatures<T> {
+    final Class<T> mockedType;
+    final Set<Class<?>> interfaces;
+
+    private MockFeatures(Class<T> mockedType, Set<Class<?>> interfaces) {
+        this.mockedType = mockedType;
+        this.interfaces = Collections.unmodifiableSet(interfaces);
+    }
+
+    static <T> MockFeatures<T> withMockFeatures(Class<T> mockedType, Set<Class<?>> interfaces) {
+        return new MockFeatures<>(mockedType, interfaces);
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockMethodAdvice.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockMethodAdvice.java
new file mode 100644
index 0000000..4cf2ac8
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/MockMethodAdvice.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2016 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package com.android.dx.mockito.inline;
+
+import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter;
+import org.mockito.internal.util.concurrent.WeakConcurrentMap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Backend for the method entry hooks. Checks if the hooks should cause an interception or should
+ * be ignored.
+ */
+class MockMethodAdvice {
+    private final WeakConcurrentMap<Object, InvocationHandlerAdapter> interceptors;
+
+    /** Pattern to decompose a instrumentedMethodWithTypeAndSignature */
+    private final Pattern methodPattern = Pattern.compile("(.*)#(.*)\\((.*)\\)");
+
+    private final SelfCallInfo selfCallInfo = new SelfCallInfo();
+
+    MockMethodAdvice(WeakConcurrentMap<Object, InvocationHandlerAdapter> interceptors) {
+        this.interceptors = interceptors;
+    }
+
+    /**
+     * Try to invoke the method {@code origin} on {@code instance}.
+     *
+     * @param origin method to invoke
+     * @param instance instance to invoke the method on.
+     * @param arguments arguments to the method
+     *
+     * @return result of the method
+     *
+     * @throws Throwable Exception if thrown by the method
+     */
+    private static Object tryInvoke(Method origin, Object instance, Object[] arguments)
+            throws Throwable {
+        try {
+            return origin.invoke(instance, arguments);
+        } catch (InvocationTargetException exception) {
+            Throwable cause = exception.getCause();
+
+            new ConditionalStackTraceFilter().filter(hideRecursiveCall(cause,
+                    new Throwable().getStackTrace().length, origin.getDeclaringClass()));
+
+            throw cause;
+        }
+    }
+
+    /**
+     * Remove calls to a class from a throwable's stack.
+     *
+     * @param throwable throwable to clean
+     * @param current stack frame number to start cleaning from (upwards)
+     * @param targetType class to remove from the stack
+     *
+     * @return throwable with the cleaned stack
+     */
+    private static Throwable hideRecursiveCall(Throwable throwable, int current,
+                                               Class<?> targetType) {
+        try {
+            StackTraceElement[] stack = throwable.getStackTrace();
+            int skip = 0;
+            StackTraceElement next;
+
+            do {
+                next = stack[stack.length - current - ++skip];
+            } while (!next.getClassName().equals(targetType.getName()));
+
+            int top = stack.length - current - skip;
+            StackTraceElement[] cleared = new StackTraceElement[stack.length - skip];
+            System.arraycopy(stack, 0, cleared, 0, top);
+            System.arraycopy(stack, top + skip, cleared, top, current);
+            throwable.setStackTrace(cleared);
+
+            return throwable;
+        } catch (RuntimeException ignored) {
+            // This should not happen unless someone instrumented or manipulated exception stack
+            // traces.
+            return throwable;
+        }
+    }
+
+    /**
+     * Get the method of {@code instance} specified by {@code methodWithTypeAndSignature}.
+     *
+     * @param instance instance the method belongs to
+     * @param methodWithTypeAndSignature the description of the method
+     *
+     * @return method {@code methodWithTypeAndSignature} refer to
+     */
+    @SuppressWarnings("unused")
+    public Method getOrigin(Object instance, String methodWithTypeAndSignature) throws Throwable {
+        if (!isMocked(instance)) {
+            return null;
+        }
+
+        Matcher methodComponents = methodPattern.matcher(methodWithTypeAndSignature);
+        boolean wasFound = methodComponents.find();
+        if (!wasFound) {
+            throw new IllegalArgumentException();
+        }
+        String argTypeNames[] = methodComponents.group(3).split(",");
+
+        ArrayList<Class<?>> argTypes = new ArrayList<>(argTypeNames.length);
+        for (String argTypeName : argTypeNames) {
+            if (!argTypeName.equals("")) {
+                switch (argTypeName) {
+                    case "byte":
+                        argTypes.add(Byte.TYPE);
+                        break;
+                    case "short":
+                        argTypes.add(Short.TYPE);
+                        break;
+                    case "int":
+                        argTypes.add(Integer.TYPE);
+                        break;
+                    case "long":
+                        argTypes.add(Long.TYPE);
+                        break;
+                    case "char":
+                        argTypes.add(Character.TYPE);
+                        break;
+                    case "float":
+                        argTypes.add(Float.TYPE);
+                        break;
+                    case "double":
+                        argTypes.add(Double.TYPE);
+                        break;
+                    case "boolean":
+                        argTypes.add(Boolean.TYPE);
+                        break;
+                    case "byte[]":
+                        argTypes.add(byte[].class);
+                        break;
+                    case "short[]":
+                        argTypes.add(short[].class);
+                        break;
+                    case "int[]":
+                        argTypes.add(int[].class);
+                        break;
+                    case "long[]":
+                        argTypes.add(long[].class);
+                        break;
+                    case "char[]":
+                        argTypes.add(char[].class);
+                        break;
+                    case "float[]":
+                        argTypes.add(float[].class);
+                        break;
+                    case "double[]":
+                        argTypes.add(double[].class);
+                        break;
+                    case "boolean[]":
+                        argTypes.add(boolean[].class);
+                        break;
+                    default:
+                        if (argTypeName.endsWith("[]")) {
+                            argTypes.add(Class.forName("[L" + argTypeName.substring(0,
+                                    argTypeName.length() - 2) + ";"));
+                        } else {
+                            argTypes.add(Class.forName(argTypeName));
+                        }
+                        break;
+                }
+            }
+        }
+
+        Method origin = Class.forName(methodComponents.group(1)).getDeclaredMethod(
+                methodComponents.group(2), argTypes.toArray(new Class<?>[]{}));
+
+        if (isOverridden(instance, origin)) {
+            return null;
+        } else {
+            return origin;
+        }
+    }
+
+    /**
+     * Handle a method entry hook.
+     *
+     * @param instance instance that is mocked
+     * @param origin method that contains the hook
+     * @param arguments arguments to the method
+     *
+     * @return A callable that can be called to get the mocked result or null if the method is not
+     *         mocked.
+     */
+    @SuppressWarnings("unused")
+    public Callable<?> handle(Object instance, Method origin, Object[] arguments) throws Throwable {
+        InvocationHandlerAdapter interceptor = interceptors.get(instance);
+        if (interceptor == null) {
+            return null;
+        }
+
+        return new ReturnValueWrapper(interceptor.interceptEntryHook(instance, origin, arguments,
+                new SuperMethodCall(selfCallInfo, origin, instance, arguments)));
+    }
+
+    /**
+     * Checks if an {@code instance} is a mock.
+     *
+     * @param instance instance that might be a mock
+     *
+     * @return {@code true} iff the instance is a mock
+     */
+    public boolean isMock(Object instance) {
+        return interceptors.containsKey(instance);
+    }
+
+    /**
+     * Check if this method call should be mocked. Usually the same as {@link #isMock(Object)} but
+     * takes into account the state of {@link #selfCallInfo} that allows to temporary disable
+     * mocking for a single method call.
+     *
+     * @param instance instance that might be mocked
+     *
+     * @return {@code true} iff the a method call should be mocked
+     *
+     * @see SelfCallInfo
+     */
+    public boolean isMocked(Object instance) {
+        return selfCallInfo.shouldMockMethod(instance) && isMock(instance);
+    }
+
+    /**
+     * Check if a method is overridden.
+     *
+     * @param instance mocked instance
+     * @param origin method that might be overridden
+     *
+     * @return {@code true} iff the method is overridden
+     */
+    public boolean isOverridden(Object instance, Method origin) {
+        Class<?> currentType = instance.getClass();
+
+        do {
+            try {
+                return !origin.equals(currentType.getDeclaredMethod(origin.getName(),
+                        origin.getParameterTypes()));
+            } catch (NoSuchMethodException ignored) {
+                currentType = currentType.getSuperclass();
+            }
+        } while (currentType != null);
+
+        return true;
+    }
+
+    /**
+     * Used to call the read (non mocked) method.
+     */
+    private static class SuperMethodCall implements InterceptedInvocation.SuperMethod {
+        private final SelfCallInfo selfCallInfo;
+        private final Method origin;
+        private final Object instance;
+        private final Object[] arguments;
+
+        private SuperMethodCall(SelfCallInfo selfCallInfo, Method origin, Object instance,
+                                Object[] arguments) {
+            this.selfCallInfo = selfCallInfo;
+            this.origin = origin;
+            this.instance = instance;
+            this.arguments = arguments;
+        }
+
+        @Override
+        public boolean isInvokable() {
+            return true;
+        }
+
+        /**
+         * Call the read (non mocked) method.
+         *
+         * @return Result of read method
+         * @throws Throwable thrown by the read method
+         */
+        @Override
+        public Object invoke() throws Throwable {
+            if (!Modifier.isPublic(origin.getDeclaringClass().getModifiers()
+                    & origin.getModifiers())) {
+                origin.setAccessible(true);
+            }
+
+            // By setting instance in the the selfCallInfo, once single method call on this instance
+            // and thread will call the read method as isMocked will return false.
+            selfCallInfo.set(instance);
+            return tryInvoke(origin, instance, arguments);
+        }
+
+    }
+
+    /**
+     * Stores a return value of {@link #handle(Object, Method, Object[])} and returns in on
+     * {@link #call()}.
+     */
+    private static class ReturnValueWrapper implements Callable<Object> {
+        private final Object returned;
+
+        private ReturnValueWrapper(Object returned) {
+            this.returned = returned;
+        }
+
+        @Override
+        public Object call() {
+            return returned;
+        }
+    }
+
+    /**
+     * Used to call the original method. If a instance is {@link #set(Object)}
+     * {@link #shouldMockMethod(Object)} returns false for this instance once.
+     *
+     * <p>This is {@link ThreadLocal}, so a thread can {@link #set(Object)} and instance and then
+     * call {@link #shouldMockMethod(Object)} without interference.
+     *
+     * @see SuperMethodCall#invoke()
+     * @see #isMocked(Object)
+     */
+    private static class SelfCallInfo extends ThreadLocal<Object> {
+        boolean shouldMockMethod(Object value) {
+            Object current = get();
+
+            if (current == value) {
+                set(null);
+                return false;
+            } else {
+                return true;
+            }
+        }
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/UnmodifiableClassException.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/UnmodifiableClassException.java
new file mode 100644
index 0000000..96d451f
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/UnmodifiableClassException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.inline;
+
+/**
+ * Exception thrown by {@link JvmtiAgent#requestTransformClasses(Class[])} if any of the supplied
+ * classed cannot be modified.
+ */
+class UnmodifiableClassException extends Exception {
+    UnmodifiableClassException(RuntimeException e) {
+        super(e);
+    }
+}
diff --git a/dexmaker-mockito-inline/src/main/jni/dexmakerjvmtiagent/agent.cc b/dexmaker-mockito-inline/src/main/jni/dexmakerjvmtiagent/agent.cc
new file mode 100644
index 0000000..e00ada8
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/jni/dexmakerjvmtiagent/agent.cc
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdlib>
+#include <sstream>
+#include <cstring>
+#include <cassert>
+#include <cstdarg>
+#include <algorithm>
+
+#include <jni.h>
+
+#include "jvmti.h"
+
+#include <dex_ir.h>
+#include <code_ir.h>
+#include <dex_ir_builder.h>
+#include <dex_utf8.h>
+#include <writer.h>
+#include <reader.h>
+#include <instrumentation.h>
+
+using namespace dex;
+using namespace lir;
+
+namespace com_android_dx_mockito_inline {
+static jvmtiEnv* localJvmtiEnv;
+
+static jobject sTransformer;
+
+// Converts a class name to a type descriptor
+// (ex. "java.lang.String" to "Ljava/lang/String;")
+static std::string
+ClassNameToDescriptor(const char* class_name) {
+    std::stringstream ss;
+    ss << "L";
+    for (auto p = class_name; *p != '\0'; ++p) {
+         ss << (*p == '.' ? '/' : *p);
+    }
+    ss << ";";
+    return ss.str();
+}
+
+// Takes the full dex file for class 'classBeingRedefined'
+// - isolates the dex code for the class out of the dex file
+// - calls sTransformer.runTransformers on the isolated dex code
+// - send the transformed code back to the runtime
+static void
+Transform(jvmtiEnv* jvmti_env,
+          JNIEnv* env,
+          jclass classBeingRedefined,
+          jobject loader,
+          const char* name,
+          jobject protectionDomain,
+          jint classDataLen,
+          const unsigned char* classData,
+          jint* newClassDataLen,
+          unsigned char** newClassData) {
+    if (sTransformer != NULL) {
+        // Isolate byte code of class class. This is needed as Android usually gives us more
+        // than the class we need.
+        Reader reader(classData, classDataLen);
+
+        u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
+        reader.CreateClassIr(index);
+        std::shared_ptr<ir::DexFile> ir = reader.GetIr();
+
+        struct Allocator : public Writer::Allocator {
+            virtual void* Allocate(size_t size) {return ::malloc(size);}
+            virtual void Free(void* ptr) {::free(ptr);}
+        };
+
+        Allocator allocator;
+        Writer writer(ir);
+        size_t isolatedClassLen = 0;
+        std::shared_ptr<jbyte> isolatedClass((jbyte*)writer.CreateImage(&allocator,
+                                                                        &isolatedClassLen));
+
+        // Create jbyteArray with isolated byte code of class
+        jbyteArray isolatedClassArr = env->NewByteArray(isolatedClassLen);
+        env->SetByteArrayRegion(isolatedClassArr, 0, isolatedClassLen,
+                                isolatedClass.get());
+
+        jstring nameStr = env->NewStringUTF(name);
+
+        // Call JvmtiAgent#runTransformers
+        jclass cls = env->GetObjectClass(sTransformer);
+        jmethodID runTransformers = env->GetMethodID(cls, "runTransformers",
+                                                     "(Ljava/lang/ClassLoader;"
+                                                     "Ljava/lang/String;"
+                                                     "Ljava/lang/Class;"
+                                                     "Ljava/security/ProtectionDomain;"
+                                                     "[B)[B");
+
+        jbyteArray transformedArr = (jbyteArray) env->CallObjectMethod(sTransformer,
+                                                                       runTransformers,
+                                                                       loader, nameStr,
+                                                                       classBeingRedefined,
+                                                                       protectionDomain,
+                                                                       isolatedClassArr);
+
+        // Set transformed byte code
+        if (!env->ExceptionOccurred() && transformedArr != NULL) {
+            *newClassDataLen = env->GetArrayLength(transformedArr);
+
+            jbyte* transformed = env->GetByteArrayElements(transformedArr, 0);
+
+            jvmti_env->Allocate(*newClassDataLen, newClassData);
+            std::memcpy(*newClassData, transformed, *newClassDataLen);
+
+            env->ReleaseByteArrayElements(transformedArr, transformed, 0);
+        }
+    }
+}
+
+// Add a label before instructionAfter
+static void
+addLabel(CodeIr& c,
+         lir::Instruction* instructionAfter,
+         Label* returnTrueLabel) {
+    c.instructions.InsertBefore(instructionAfter, returnTrueLabel);
+}
+
+// Add a byte code before instructionAfter
+static void
+addInstr(CodeIr& c,
+         lir::Instruction* instructionAfter,
+         Opcode opcode,
+         const std::list<Operand*>& operands) {
+    auto instruction = c.Alloc<Bytecode>();
+
+    instruction->opcode = opcode;
+
+    for (auto it = operands.begin(); it != operands.end(); it++) {
+        instruction->operands.push_back(*it);
+    }
+
+    c.instructions.InsertBefore(instructionAfter, instruction);
+}
+
+// Add a method call byte code before instructionAfter
+static void
+addCall(ir::Builder& b,
+        CodeIr& c,
+        lir::Instruction* instructionAfter,
+        Opcode opcode,
+        ir::Type* type,
+        const char* methodName,
+        ir::Type* returnType,
+        const std::vector<ir::Type*>& types,
+        const std::list<int>& regs) {
+    auto proto = b.GetProto(returnType, b.GetTypeList(types));
+    auto method = b.GetMethodDecl(b.GetAsciiString(methodName), proto, type);
+
+    VRegList* param_regs = c.Alloc<VRegList>();
+    for (auto it = regs.begin(); it != regs.end(); it++) {
+        param_regs->registers.push_back(*it);
+    }
+
+    addInstr(c, instructionAfter, opcode, {param_regs, c.Alloc<Method>(method,
+                                                                       method->orig_index)});
+}
+
+typedef struct {
+    ir::Type* boxedType;
+    ir::Type* scalarType;
+    std::string unboxMethod;
+} BoxingInfo;
+
+// Get boxing / unboxing info for a type
+static BoxingInfo
+getBoxingInfo(ir::Builder &b,
+              char typeCode) {
+    BoxingInfo boxingInfo;
+
+    if (typeCode != 'L' && typeCode !=  '[') {
+        std::stringstream tmp;
+        tmp << typeCode;
+        boxingInfo.scalarType = b.GetType(tmp.str().c_str());
+    }
+
+    switch (typeCode) {
+        case 'B':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Byte;");
+            boxingInfo.unboxMethod = "byteValue";
+            break;
+        case 'S':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Short;");
+            boxingInfo.unboxMethod = "shortValue";
+            break;
+        case 'I':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Integer;");
+            boxingInfo.unboxMethod = "intValue";
+            break;
+        case 'C':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Character;");
+            boxingInfo.unboxMethod = "charValue";
+            break;
+        case 'F':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Float;");
+            boxingInfo.unboxMethod = "floatValue";
+            break;
+        case 'Z':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Boolean;");
+            boxingInfo.unboxMethod = "booleanValue";
+            break;
+        case 'J':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Long;");
+            boxingInfo.unboxMethod = "longValue";
+            break;
+        case 'D':
+            boxingInfo.boxedType = b.GetType("Ljava/lang/Double;");
+            boxingInfo.unboxMethod = "doubleValue";
+            break;
+        default:
+            // real object
+            break;
+    }
+
+    return boxingInfo;
+}
+
+static size_t
+getNumParams(ir::EncodedMethod *method) {
+    if (method->decl->prototype->param_types == NULL) {
+        return 0;
+    }
+
+    return method->decl->prototype->param_types->types.size();
+}
+
+static bool
+canBeTransformed(ir::EncodedMethod *method) {
+    std::string type = method->decl->parent->Decl();
+    ir::String* methodName = method->decl->name;
+
+    return !(((method->access_flags & (kAccAbstract | kAccPrivate | kAccBridge | kAccNative
+                                       | kAccStatic)) != 0)
+             || (Utf8Cmp(methodName->c_str(), "<init>") == 0)
+             || (Utf8Cmp(methodName->c_str(), "<clinit>") == 0)
+             || (Utf8Cmp(type.c_str(), "java.lang.Object") == 0
+                 && Utf8Cmp(methodName->c_str(), "finalize") == 0
+                 && getNumParams(method) == 0)
+             || (strncmp(type.c_str(), "java.", 5) == 0
+                 && (method->access_flags & (kAccPrivate | kAccPublic | kAccProtected)) == 0)
+             // getClass is used by MockMethodAdvice.isOverridden
+             || (Utf8Cmp(methodName->c_str(), "getClass") == 0));
+}
+
+static bool
+isHashCode(ir::EncodedMethod *method) {
+    return Utf8Cmp(method->decl->name->c_str(), "hashCode") == 0
+           && getNumParams(method) == 0;
+}
+
+static bool
+isEquals(ir::EncodedMethod *method) {
+    return Utf8Cmp(method->decl->name->c_str(), "equals") == 0
+           && getNumParams(method) == 1
+           && Utf8Cmp(method->decl->prototype->param_types->types[0]->Decl().c_str(),
+                      "java.lang.Object") == 0;
+}
+
+// Transforms the classes to add the mockito hooks
+// - equals and hashcode are handled in a special way
+extern "C" JNIEXPORT jbyteArray JNICALL
+Java_com_android_dx_mockito_inline_ClassTransformer_nativeRedefine(JNIEnv* env,
+                                                                   jobject generator,
+                                                                   jstring idStr,
+                                                                   jbyteArray originalArr) {
+    unsigned char* original = (unsigned char*)env->GetByteArrayElements(originalArr, 0);
+
+    Reader reader(original, env->GetArrayLength(originalArr));
+    reader.CreateClassIr(0);
+    std::shared_ptr<ir::DexFile> dex_ir = reader.GetIr();
+    ir::Builder b(dex_ir);
+
+    ir::Type* booleanScalarT = b.GetType("Z");
+    ir::Type* intScalarT = b.GetType("I");
+    ir::Type* objectT = b.GetType("Ljava/lang/Object;");
+    ir::Type* objectArrayT = b.GetType("[Ljava/lang/Object;");
+    ir::Type* stringT = b.GetType("Ljava/lang/String;");
+    ir::Type* methodT = b.GetType("Ljava/lang/reflect/Method;");
+    ir::Type* systemT = b.GetType("Ljava/lang/System;");
+    ir::Type* callableT = b.GetType("Ljava/util/concurrent/Callable;");
+    ir::Type* dispatcherT = b.GetType("Lcom/android/dx/mockito/inline/MockMethodDispatcher;");
+
+    // Add id to dex file
+    const char* idNative = env->GetStringUTFChars(idStr, 0);
+    ir::String* id = b.GetAsciiString(idNative);
+    env->ReleaseStringUTFChars(idStr, idNative);
+
+    for (auto& method : dex_ir->encoded_methods) {
+        if (!canBeTransformed(method.get())) {
+            continue;
+        }
+
+        if (isEquals(method.get())) {
+            /*
+            equals_original(Object other) {
+                T t = foo(other);
+                return bar(t);
+            }
+
+            equals_transformed(params) {
+                // MockMethodDispatcher dispatcher = MockMethodDispatcher.get(idStr, this);
+                const-string v0, "65463hg34t"
+                move-objectfrom16 v1, THIS
+                invoke-static {v0, v1}, MockMethodDispatcher.get(String, Object):MockMethodDispatcher
+                move-result-object v2
+
+                // if (dispatcher == null || ) {
+                //     goto original_method;
+                // }
+                if-eqz v2, original_method
+
+                // if (!dispatcher.isMock(this)) {
+                //     goto original_method;
+                // }
+                invoke-virtual {v2, v1}, MockMethodDispatcher.isMock(Object):Method
+                move-result v2
+                if-eqz v2, original_method
+
+                // return self == other
+                move-objectfrom16 v0, ARG1
+                if-eq v0, v1, return_true
+
+                const v0, 0
+                return v0
+
+            return true:
+                const v0, 1
+                return v0
+
+            original_method:
+                // Move all method arguments down so that they match what the original code expects.
+                move-object16 v4, v5      # THIS
+                move-object16 v5, v6      # ARG1
+
+                T t = foo(other);
+                return bar(t);
+            }
+            */
+
+            CodeIr c(method.get(), dex_ir);
+
+            // Make sure there are at least 5 local registers to use
+            int originalNumRegisters = method->code->registers - method->code->ins_count;
+            int numAdditionalRegs = std::max(0, 3 - originalNumRegisters);
+            int thisReg = numAdditionalRegs + method->code->registers
+                          - method->code->ins_count;
+
+            if (numAdditionalRegs > 0) {
+                c.ir_method->code->registers += numAdditionalRegs;
+            }
+
+            lir::Instruction* fi = *(c.instructions.begin());
+
+            Label* originalMethodLabel = c.Alloc<Label>(0);
+            Label* returnTrueLabel = c.Alloc<Label>(0);
+            CodeLocation* originalMethod = c.Alloc<CodeLocation>(originalMethodLabel);
+            VReg* v0 = c.Alloc<VReg>(0);
+            VReg* v1 = c.Alloc<VReg>(1);
+            VReg* v2 = c.Alloc<VReg>(2);
+            VReg* thiz = c.Alloc<VReg>(thisReg);
+
+            addInstr(c, fi, OP_CONST_STRING, {v0, c.Alloc<String>(id, id->orig_index)});
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v1, thiz});
+            addCall(b, c, fi, OP_INVOKE_STATIC, dispatcherT, "get", dispatcherT,
+                    {stringT, objectT}, {0, 1});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v2});
+            addInstr(c, fi, OP_IF_EQZ, {v2, originalMethod});
+            addCall(b, c, fi, OP_INVOKE_VIRTUAL, dispatcherT, "isMock", booleanScalarT, {objectT},
+                    {2, 1});
+            addInstr(c, fi, OP_MOVE_RESULT, {v2});
+            addInstr(c, fi, OP_IF_EQZ, {v2, originalMethod});
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v0, c.Alloc<VReg>(thisReg + 1)});
+            addInstr(c, fi, OP_IF_EQ, {v0, v1, c.Alloc<CodeLocation>(returnTrueLabel)});
+            addInstr(c, fi, OP_CONST, {v0, c.Alloc<Const32>(0)});
+            addInstr(c, fi, OP_RETURN, {v0});
+            addLabel(c, fi, returnTrueLabel);
+            addInstr(c, fi, OP_CONST, {v0, c.Alloc<Const32>(1)});
+            addInstr(c, fi, OP_RETURN, {v0});
+            addLabel(c, fi, originalMethodLabel);
+            addInstr(c, fi, OP_MOVE_OBJECT_16, {c.Alloc<VReg>(thisReg - numAdditionalRegs), thiz});
+            addInstr(c, fi, OP_MOVE_OBJECT_16, {c.Alloc<VReg>(thisReg - numAdditionalRegs + 1),
+                     c.Alloc<VReg>(thisReg + 1)});
+
+            c.Assemble();
+        } else if (isHashCode(method.get())) {
+            /*
+            hashCode_original(Object other) {
+                T t = foo(other);
+                return bar(t);
+            }
+
+            hashCode_transformed(params) {
+                // MockMethodDispatcher dispatcher = MockMethodDispatcher.get(idStr, this);
+                const-string v0, "65463hg34t"
+                move-objectfrom16 v1, THIS
+                invoke-static {v0, v1}, MockMethodDispatcher.get(String, Object):MockMethodDispatcher
+                move-result-object v2
+
+                // if (dispatcher == null || ) {
+                //     goto original_method;
+                // }
+                if-eqz v2, original_method
+
+                // if (!dispatcher.isMock(this)) {
+                //     goto original_method;
+                // }
+                invoke-interface {v2, v1}, MockMethodDispatcher.isMock(Object):Method
+                move-result v2
+                if-eqz v2, original_method
+
+                // return System.identityHashCode(this);
+                invoke-static {v1}, System.identityHashCode(Object):int
+                move-result v2
+                return v2
+
+            original_method:
+                // Move all method arguments down so that they match what the original code expects.
+                move-object16 v4, v5      # THIS
+
+                T t = foo(other);
+                return bar(t);
+            }
+            */
+
+            CodeIr c(method.get(), dex_ir);
+
+            // Make sure there are at least 5 local registers to use
+            int originalNumRegisters = method->code->registers - method->code->ins_count;
+            int numAdditionalRegs = std::max(0, 3 - originalNumRegisters);
+            int thisReg = numAdditionalRegs + method->code->registers - method->code->ins_count;
+
+            if (numAdditionalRegs > 0) {
+                c.ir_method->code->registers += numAdditionalRegs;
+            }
+
+            lir::Instruction* fi = *(c.instructions.begin());
+
+            Label* originalMethodLabel = c.Alloc<Label>(0);
+            CodeLocation* originalMethod = c.Alloc<CodeLocation>(originalMethodLabel);
+            VReg* v0 = c.Alloc<VReg>(0);
+            VReg* v1 = c.Alloc<VReg>(1);
+            VReg* v2 = c.Alloc<VReg>(2);
+            VReg* thiz = c.Alloc<VReg>(thisReg);
+
+            addInstr(c, fi, OP_CONST_STRING, {v0, c.Alloc<String>(id, id->orig_index)});
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v1, thiz});
+            addCall(b, c, fi, OP_INVOKE_STATIC, dispatcherT, "get", dispatcherT,
+                    {stringT, objectT}, {0, 1});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v2});
+            addInstr(c, fi, OP_IF_EQZ, {v2, originalMethod});
+            addCall(b, c, fi, OP_INVOKE_VIRTUAL, dispatcherT, "isMock", booleanScalarT, {objectT},
+                    {2, 1});
+            addInstr(c, fi, OP_MOVE_RESULT, {v2});
+            addInstr(c, fi, OP_IF_EQZ, {v2, originalMethod});
+            addCall(b, c, fi, OP_INVOKE_STATIC, systemT, "identityHashCode", intScalarT, {objectT},
+                    {1});
+            addInstr(c, fi, OP_MOVE_RESULT, {v2});
+            addInstr(c, fi, OP_RETURN, {v2});
+            addLabel(c, fi, originalMethodLabel);
+            addInstr(c, fi, OP_MOVE_OBJECT_16, {c.Alloc<VReg>(thisReg - numAdditionalRegs), thiz});
+
+            c.Assemble();
+        } else {
+            /*
+            long method_original(int param1, long param2, String param3) {
+                foo();
+                return bar();
+            }
+
+            long method_transformed(int param1, long param2, String param3) {
+                // MockMethodDispatcher dispatcher = MockMethodDispatcher.get(idStr, this);
+                const-string v0, "65463hg34t"
+                move-objectfrom16 v1, THIS       # this is necessary as invoke-static cannot deal
+                                                 # with medium or high registers and THIS might not
+                                                 # be low
+                invoke-static {v0, v1}, MockMethodDispatcher.get(String, Object):MockMethodDispatcher
+                move-result-object v0
+
+                // if (dispatcher == null) {
+                //    goto original_method;
+                // }
+                if-eqz v0, original_method
+
+                // Method origin = dispatcher.getOrigin(this, methodDesc);
+                const-string v1 "fully.qualified.ClassName#original_method(int, long, String)"
+                move-objectfrom16 v2, THIS       # this is necessary as invoke-static cannot deal
+                                                 # with medium or high registers and THIS might not
+                                                 # be low
+                invoke-virtual {v0, v2, v1}, MockMethodDispatcher.getOrigin(Object, String):Method
+                move-result-object v1
+
+                // if (origin == null) {
+                //     goto original_method;
+                // }
+                if-eqz v1, original_method
+
+                // Create an array with Objects of all parameters.
+
+                //     Object[] arguments = new Object[3]
+                const v3, 3
+                new-array v2, v3, Object[]
+
+                //     Integer param1Integer = Integer.valueOf(param1)
+                move-from16 v3, ARG1     # this is necessary as invoke-static cannot deal with high
+                                         # registers and ARG1 might be high
+                invoke-static {v3}, Integer.valueOf(int):Integer
+                move-result-object v3
+
+                //     arguments[0] = param1Integer
+                const v4, 0
+                aput-object v3, v2, v4
+
+                //     Long param2Long = Long.valueOf(param2)
+                move-widefrom16 v3:v4, ARG2.1:ARG2.2 # this is necessary as invoke-static cannot
+                                                     # deal with high registers and ARG2 might be
+                                                     # high
+                invoke-static {v3, v4}, Long.valueOf(long):Long
+                move-result-object v3
+
+                //     arguments[1] = param2Long
+                const v4, 1
+                aput-object v3, v2, v4
+
+                //     arguments[2] = param3
+                const v4, 2
+                move-objectfrom16 v3, ARG3     # this is necessary as aput-object cannot deal with
+                                               # high registers and ARG3 might be high
+                aput-object v3, v2, v4
+
+                // Callable<?> mocked = dispatcher.handle(this, origin, arguments);
+                move-objectfrom16 v3, THIS       # this is necessary as invoke-virtual cannot deal
+                                                 # with medium or high registers and THIS might not
+                                                 # be low
+                invoke-virtual {v0,v3,v1,v2}, MockMethodDispatcher.handle(Object, Method,
+                                                                          Object[]):Callable
+                move-result-object v0
+
+                //  if (mocked != null) {
+                if-eqz v0, original_method
+
+                //      Object ret = mocked.call();
+                invoke-interface {v0}, Callable.call():Object
+                move-result-object v0
+
+                //      Long retLong = (Long)ret
+                check-cast v0, Long
+
+                //      long retlong = retLong.longValue();
+                invoke-virtual {v0}, Long.longValue():long
+                move-result-wide v0:v1
+
+                //      return retlong;
+                return-wide v0:v1
+
+                //  }
+
+            original_method:
+                // Move all method arguments down so that they match what the original code expects.
+                // Let's assume three arguments, one int, one long, one String and the and used to
+                // use 4 registers
+                move-object16 v4, v5      # THIS
+                move16 v5, v6             # ARG1
+                move-wide16 v6:v7, v7:v8  # ARG2 (overlapping moves are allowed)
+                move-object16 v8, v9      # ARG3
+
+                // foo();
+                // return bar();
+                unmodified original byte code
+            }
+            */
+
+            CodeIr c(method.get(), dex_ir);
+
+            // Make sure there are at least 5 local registers to use
+            int originalNumRegisters = method->code->registers - method->code->ins_count;
+            int numAdditionalRegs = std::max(0, 5 - originalNumRegisters);
+            int thisReg = originalNumRegisters + numAdditionalRegs;
+
+            if (numAdditionalRegs > 0) {
+                c.ir_method->code->registers += numAdditionalRegs;
+            }
+
+            lir::Instruction* fi = *(c.instructions.begin());
+
+            // Add methodDesc to dex file
+            std::stringstream ss;
+            ss << method->decl->parent->Decl() << "#" << method->decl->name->c_str() << "(" ;
+            bool first = true;
+            if (method->decl->prototype->param_types != NULL) {
+                 for (const auto& type : method->decl->prototype->param_types->types) {
+                     if (first) {
+                         first = false;
+                     } else {
+                         ss << ",";
+                     }
+
+                     ss << type->Decl().c_str();
+                 }
+            }
+            ss << ")";
+            std::string methodDescStr = ss.str();
+            ir::String* methodDesc = b.GetAsciiString(methodDescStr.c_str());
+
+            size_t numParams = getNumParams(method.get());
+
+            Label* originalMethodLabel = c.Alloc<Label>(0);
+            CodeLocation* originalMethod = c.Alloc<CodeLocation>(originalMethodLabel);
+            VReg* v0 = c.Alloc<VReg>(0);
+            VReg* v1 = c.Alloc<VReg>(1);
+            VReg* v2 = c.Alloc<VReg>(2);
+            VReg* v3 = c.Alloc<VReg>(3);
+            VReg* v4 = c.Alloc<VReg>(4);
+            VReg* thiz = c.Alloc<VReg>(thisReg);
+
+            addInstr(c, fi, OP_CONST_STRING, {v0, c.Alloc<String>(id, id->orig_index)});
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v1, thiz});
+            addCall(b, c, fi, OP_INVOKE_STATIC, dispatcherT, "get", dispatcherT, {stringT, objectT},
+                    {0, 1});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v0});
+            addInstr(c, fi, OP_IF_EQZ, {v0, originalMethod});
+            addInstr(c, fi, OP_CONST_STRING,
+                     {v1, c.Alloc<String>(methodDesc, methodDesc->orig_index)});
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v2, thiz});
+            addCall(b, c, fi, OP_INVOKE_VIRTUAL, dispatcherT, "getOrigin", methodT,
+                    {objectT, stringT}, {0, 2, 1});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v1});
+            addInstr(c, fi, OP_IF_EQZ, {v1, originalMethod});
+            addInstr(c, fi, OP_CONST, {v3, c.Alloc<Const32>(numParams)});
+            addInstr(c, fi, OP_NEW_ARRAY, {v2, v3, c.Alloc<Type>(objectArrayT,
+                                                                 objectArrayT->orig_index)});
+
+            if (numParams > 0) {
+                int argReg = thisReg + 1;
+
+                for (int argNum = 0; argNum < numParams; argNum++) {
+                    const auto& type = method->decl->prototype->param_types->types[argNum];
+                    BoxingInfo boxingInfo = getBoxingInfo(b, type->descriptor->c_str()[0]);
+
+                    switch (type->GetCategory()) {
+                        case ir::Type::Category::Scalar:
+                            addInstr(c, fi, OP_MOVE_FROM16, {v3, c.Alloc<VReg>(argReg)});
+                            addCall(b, c, fi, OP_INVOKE_STATIC, boxingInfo.boxedType, "valueOf",
+                                    boxingInfo.boxedType, {type}, {3});
+                            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v3});
+
+                            argReg++;
+                            break;
+                        case ir::Type::Category::WideScalar: {
+                            VRegPair* v3v4 = c.Alloc<VRegPair>(3);
+                            VRegPair* argRegPair = c.Alloc<VRegPair>(argReg);
+
+                            addInstr(c, fi, OP_MOVE_WIDE_FROM16, {v3v4, argRegPair});
+                            addCall(b, c, fi, OP_INVOKE_STATIC, boxingInfo.boxedType, "valueOf",
+                                    boxingInfo.boxedType, {type}, {3, 4});
+                            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v3});
+
+                            argReg += 2;
+                            break;
+                        }
+                        case ir::Type::Category::Reference:
+                            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v3, c.Alloc<VReg>(argReg)});
+
+                            argReg++;
+                            break;
+                        case ir::Type::Category::Void:
+                            assert(false);
+                    }
+
+                    addInstr(c, fi, OP_CONST, {v4, c.Alloc<Const32>(argNum)});
+                    addInstr(c, fi, OP_APUT_OBJECT, {v3, v2, v4});
+                }
+            }
+
+            addInstr(c, fi, OP_MOVE_OBJECT_FROM16, {v3, thiz});
+            addCall(b, c, fi, OP_INVOKE_VIRTUAL, dispatcherT, "handle", callableT,
+                    {objectT, methodT, objectArrayT}, {0, 3, 1, 2});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v0});
+            addInstr(c, fi, OP_IF_EQZ, {v0, originalMethod});
+            addCall(b, c, fi, OP_INVOKE_INTERFACE, callableT, "call", objectT, {}, {0});
+            addInstr(c, fi, OP_MOVE_RESULT_OBJECT, {v0});
+
+            ir::Type *returnType = method->decl->prototype->return_type;
+            BoxingInfo boxingInfo = getBoxingInfo(b, returnType->descriptor->c_str()[0]);
+
+            switch (returnType->GetCategory()) {
+                case ir::Type::Category::Scalar:
+                    addInstr(c, fi, OP_CHECK_CAST, {v0,
+                            c.Alloc<Type>(boxingInfo.boxedType, boxingInfo.boxedType->orig_index)});
+                    addCall(b, c, fi, OP_INVOKE_VIRTUAL, boxingInfo.boxedType,
+                            boxingInfo.unboxMethod.c_str(), returnType, {}, {0});
+                    addInstr(c, fi, OP_MOVE_RESULT, {v0});
+                    addInstr(c, fi, OP_RETURN, {v0});
+                    break;
+                case ir::Type::Category::WideScalar: {
+                    VRegPair* v0v1 = c.Alloc<VRegPair>(0);
+
+                    addInstr(c, fi, OP_CHECK_CAST, {v0,
+                            c.Alloc<Type>(boxingInfo.boxedType, boxingInfo.boxedType->orig_index)});
+                    addCall(b, c, fi, OP_INVOKE_VIRTUAL, boxingInfo.boxedType,
+                            boxingInfo.unboxMethod.c_str(), returnType, {}, {0});
+                    addInstr(c, fi, OP_MOVE_RESULT_WIDE, {v0v1});
+                    addInstr(c, fi, OP_RETURN_WIDE, {v0v1});
+                    break;
+                }
+                case ir::Type::Category::Reference:
+                    addInstr(c, fi, OP_CHECK_CAST, {v0, c.Alloc<Type>(returnType,
+                                                                      returnType->orig_index)});
+                    addInstr(c, fi, OP_RETURN_OBJECT, {v0});
+                    break;
+                case ir::Type::Category::Void:
+                    addInstr(c, fi, OP_RETURN_VOID, {});
+                    break;
+            }
+
+            addLabel(c, fi, originalMethodLabel);
+            addInstr(c, fi, OP_MOVE_OBJECT_16, {c.Alloc<VReg>(thisReg - numAdditionalRegs), thiz});
+
+            if (numParams > 0) {
+                int argReg = thisReg + 1;
+
+                for (int argNum = 0; argNum < numParams; argNum++) {
+                    const auto& type = method->decl->prototype->param_types->types[argNum];
+                    int origReg = argReg - numAdditionalRegs;
+                    switch (type->GetCategory()) {
+                        case ir::Type::Category::Scalar:
+                            addInstr(c, fi, OP_MOVE_16, {c.Alloc<VReg>(origReg),
+                                     c.Alloc<VReg>(argReg)});
+                            argReg++;
+                            break;
+                        case ir::Type::Category::WideScalar:
+                            addInstr(c, fi, OP_MOVE_WIDE_16,{c.Alloc<VRegPair>(origReg),
+                                     c.Alloc<VRegPair>(argReg)});
+                            argReg +=2;
+                            break;
+                        case ir::Type::Category::Reference:
+                            addInstr(c, fi, OP_MOVE_OBJECT_16, {c.Alloc<VReg>(origReg),
+                                     c.Alloc<VReg>(argReg)});
+                            argReg++;
+                            break;
+                    }
+                }
+            }
+
+            c.Assemble();
+        }
+    }
+
+    struct Allocator : public Writer::Allocator {
+        virtual void* Allocate(size_t size) {return ::malloc(size);}
+        virtual void Free(void* ptr) {::free(ptr);}
+    };
+
+    Allocator allocator;
+    Writer writer(dex_ir);
+    size_t transformedLen = 0;
+    std::shared_ptr<jbyte> transformed((jbyte*)writer.CreateImage(&allocator, &transformedLen));
+
+    jbyteArray transformedArr = env->NewByteArray(transformedLen);
+    env->SetByteArrayRegion(transformedArr, 0, transformedLen, transformed.get());
+
+    return transformedArr;
+}
+
+// Initializes the agent
+extern "C" jint Agent_OnAttach(JavaVM* vm,
+                               char* options,
+                               void* reserved) {
+    jint jvmError = vm->GetEnv(reinterpret_cast<void**>(&localJvmtiEnv), JVMTI_VERSION_1_2);
+    if (jvmError != JNI_OK) {
+        return jvmError;
+    }
+
+    jvmtiCapabilities caps;
+    memset(&caps, 0, sizeof(caps));
+    caps.can_retransform_classes = 1;
+
+    jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
+    if (error != JVMTI_ERROR_NONE) {
+        return error;
+    }
+
+    jvmtiEventCallbacks cb;
+    memset(&cb, 0, sizeof(cb));
+    cb.ClassFileLoadHook = Transform;
+
+    error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
+    if (error != JVMTI_ERROR_NONE) {
+        return error;
+    }
+
+    error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+                                                    NULL);
+    if (error != JVMTI_ERROR_NONE) {
+        return error;
+    }
+
+    return JVMTI_ERROR_NONE;
+}
+
+// Throw runtime exception
+static void throwRuntimeExpection(JNIEnv* env, const char* fmt, ...) {
+    char msgBuf[512];
+
+    va_list args;
+    va_start (args, fmt);
+    vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+    va_end (args);
+
+    jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
+    env->ThrowNew(exceptionClass, msgBuf);
+}
+
+// Register transformer hook
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_dx_mockito_inline_JvmtiAgent_nativeRegisterTransformerHook(JNIEnv* env,
+                                                                            jobject thiz) {
+    sTransformer = env->NewGlobalRef(thiz);
+}
+
+// Unregister transformer hook
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_dx_mockito_inline_JvmtiAgent_nativeUnregisterTransformerHook(JNIEnv* env,
+                                                                              jobject thiz) {
+    env->DeleteGlobalRef(sTransformer);
+    sTransformer = NULL;
+}
+
+// Triggers retransformation of classes via this file's Transform method
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_dx_mockito_inline_JvmtiAgent_nativeRetransformClasses(JNIEnv* env,
+                                                                       jobject thiz,
+                                                                       jobjectArray classes) {
+    jsize numTransformedClasses = env->GetArrayLength(classes);
+    jclass *transformedClasses = (jclass*) malloc(numTransformedClasses * sizeof(jclass));
+    for (int i = 0; i < numTransformedClasses; i++) {
+        transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes, i));
+    }
+
+    jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
+                                                         transformedClasses);
+
+    for (int i = 0; i < numTransformedClasses; i++) {
+        env->DeleteGlobalRef(transformedClasses[i]);
+    }
+    free(transformedClasses);
+
+    if (error != JVMTI_ERROR_NONE) {
+        throwRuntimeExpection(env, "Could not retransform classes: %d", error);
+    }
+}
+
+// Adds a jar file to the bootstrap class loader
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_dx_mockito_inline_JvmtiAgent_nativeAppendToBootstrapClassLoaderSearch(JNIEnv* env,
+                                                                                  jclass klass,
+                                                                                  jstring jarFile) {
+    const char *jarFileNative = env->GetStringUTFChars(jarFile, 0);
+    jvmtiError error = localJvmtiEnv->AddToBootstrapClassLoaderSearch(jarFileNative);
+
+    if (error != JVMTI_ERROR_NONE) {
+        throwRuntimeExpection(env, "Could not add %s to bootstrap class path: %d", jarFileNative,
+                              error);
+    }
+
+    env->ReleaseStringUTFChars(jarFile, jarFileNative);
+}
+}  // namespace com_android_dx_mockito_inline
+
diff --git a/dexmaker-mockito-inline/src/main/resources/README.txt b/dexmaker-mockito-inline/src/main/resources/README.txt
new file mode 100644
index 0000000..b94264b
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/resources/README.txt
@@ -0,0 +1,2 @@
+dispatcher.jar is the classes.dex of the apk created by dexmaker-mockito-inline-dispatcher
+repackaged into a jar. We should automate this.
\ No newline at end of file
diff --git a/dexmaker-mockito-inline/src/main/resources/dispatcher.jar b/dexmaker-mockito-inline/src/main/resources/dispatcher.jar
new file mode 100644
index 0000000..6a5cf5f
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/resources/dispatcher.jar
Binary files differ
diff --git a/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..4f2b284
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+com.android.dx.mockito.inline.InlineDexmakerMockMaker
\ No newline at end of file
diff --git a/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider b/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider
new file mode 100644
index 0000000..1767c97
--- /dev/null
+++ b/dexmaker-mockito-inline/src/main/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider
@@ -0,0 +1 @@
+com.android.dx.mockito.inline.DexmakerStackTraceCleaner
\ No newline at end of file
diff --git a/dexmaker-mockito-tests/AndroidManifest.xml b/dexmaker-mockito-tests/AndroidManifest.xml
new file mode 100644
index 0000000..45201f9
--- /dev/null
+++ b/dexmaker-mockito-tests/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.android.dexmaker.mockito.tests">
+    <application />
+</manifest>
diff --git a/dexmaker-mockito-tests/build.gradle b/dexmaker-mockito-tests/build.gradle
new file mode 100644
index 0000000..f0befe0
--- /dev/null
+++ b/dexmaker-mockito-tests/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 25
+    buildToolsVersion "25.0.0"
+
+    lintOptions {
+        abortOnError false
+    }
+
+    defaultConfig {
+        applicationId "com.android.dexmaker.mockito.tests"
+        minSdkVersion 25
+        targetSdkVersion 25
+        versionName VERSION_NAME
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+}
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile project(':dexmaker')
+    compile project(':dexmaker-mockito')
+
+    androidTestCompile 'com.android.support.test:runner:0.5'
+    androidTestCompile 'junit:junit:4.12'
+}
diff --git a/dexmaker-mockito-tests/src/androidTest/java/com/android/dx/mockito/tests/MockTests.java b/dexmaker-mockito-tests/src/androidTest/java/com/android/dx/mockito/tests/MockTests.java
new file mode 100644
index 0000000..1102b71
--- /dev/null
+++ b/dexmaker-mockito-tests/src/androidTest/java/com/android/dx/mockito/tests/MockTests.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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.dx.mockito.tests;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.exceptions.base.MockitoException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class MockTests {
+    public static class TestClass {
+        public String returnA() {
+            return "A";
+        }
+    }
+
+    public interface TestInterface {
+        String returnA();
+    }
+
+    @Test
+    public void mockClass() throws Exception {
+        TestClass t = mock(TestClass.class);
+
+        assertNull(t.returnA());
+
+        when(t.returnA()).thenReturn("B");
+        assertEquals("B", t.returnA());
+    }
+
+    @Test
+    public void mockInterface() throws Exception {
+        TestInterface t = mock(TestInterface.class);
+
+        assertNull(t.returnA());
+
+        when(t.returnA()).thenReturn("B");
+        assertEquals("B", t.returnA());
+    }
+
+    @Test
+    public void spyClass() throws Exception {
+        TestClass originalT = new TestClass();
+        TestClass t = spy(originalT);
+
+        assertEquals("A", t.returnA());
+
+        when(t.returnA()).thenReturn("B");
+        assertEquals("B", t.returnA());
+
+        // Wrapped object is not affected by mocking
+        assertEquals("A", originalT.returnA());
+    }
+
+    @Test
+    public void spyNewClass() throws Exception {
+        TestClass t = spy(TestClass.class);
+
+        assertEquals("A", t.returnA());
+
+        when(t.returnA()).thenReturn("B");
+        assertEquals("B", t.returnA());
+    }
+
+    @Test
+    public void cleanStackTraceProxy() {
+        TestClass t = mock(TestClass.class);
+
+        try {
+            verify(t).returnA();
+        } catch (Throwable verifyLocation) {
+            try {
+                throw new Exception();
+            } catch (Exception here) {
+                assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+                        .getStackTrace()[0].getMethodName());
+            }
+        }
+    }
+
+    @Test
+    public void cleanStackTraceInterface() {
+        TestInterface t = mock(TestInterface.class);
+
+        try {
+            verify(t).returnA();
+        } catch (Throwable verifyLocation) {
+            try {
+                throw new Exception();
+            } catch (Exception here) {
+                assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+                        .getStackTrace()[0].getMethodName());
+            }
+        }
+    }
+}
diff --git a/dexmaker-mockito/build.gradle b/dexmaker-mockito/build.gradle
index 36fa92f..e1f5133 100644
--- a/dexmaker-mockito/build.gradle
+++ b/dexmaker-mockito/build.gradle
@@ -14,5 +14,5 @@
 dependencies {
     compile project(":dexmaker")
 
-    compile 'org.mockito:mockito-core:2.2.29'
+    compile 'org.mockito:mockito-core:2.12.0'
 }
diff --git a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
index 4015059..b19f7bd 100644
--- a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
+++ b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
@@ -55,9 +55,15 @@
         } else {
             // support concrete classes via dexmaker's ProxyBuilder
             try {
-                Class<? extends T> proxyClass = ProxyBuilder.forClass(typeToMock)
-                        .implementing(extraInterfaces)
-                        .buildProxyClass();
+                ProxyBuilder b = ProxyBuilder.forClass(typeToMock)
+                        .implementing(extraInterfaces);
+
+                if (Boolean.parseBoolean(
+                        System.getProperty("dexmaker.share_classloader", "false"))) {
+                    b.withSharedClassLoader();
+                }
+
+                Class<? extends T> proxyClass = b.buildProxyClass();
                 T mock = unsafeAllocator.newInstance(proxyClass);
                 ProxyBuilder.setInvocationHandler(mock, invocationHandler);
                 return mock;
@@ -109,12 +115,15 @@
         return new StackTraceCleaner() {
             @Override
             public boolean isIn(StackTraceElement candidate) {
+                String className = candidate.getClassName();
+
                 return defaultCleaner.isIn(candidate)
-                        && !candidate.getClassName().endsWith("_Proxy") // dexmaker class proxies
-                        && !candidate.getClassName().startsWith("$Proxy") // dalvik interface proxies
-                        && !candidate.getClassName().startsWith("java.lang.reflect.Proxy")
-                        && !candidate.getClassName().startsWith("com.google.dexmaker.mockito.")
-                        && !candidate.getClassName().startsWith("com.android.dx.mockito.");
+                        && !className.endsWith("_Proxy") // dexmaker class proxies
+                        && !className.startsWith("$Proxy") // dalvik interface proxies
+                        && !className.startsWith("java.lang.reflect.Proxy")
+                        && !(className.startsWith("com.android.dx.mockito.")
+                             // Do not clean unit tests
+                             && !className.startsWith("com.android.dx.mockito.tests"));
             }
         };
     }
diff --git a/dexmaker-mockito/src/main/java/com/android/dx/mockito/InterceptedInvocation.java b/dexmaker-mockito/src/main/java/com/android/dx/mockito/InterceptedInvocation.java
new file mode 100644
index 0000000..b0b42e9
--- /dev/null
+++ b/dexmaker-mockito/src/main/java/com/android/dx/mockito/InterceptedInvocation.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package com.android.dx.mockito;
+
+import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.exceptions.VerificationAwareInvocation;
+import org.mockito.internal.invocation.ArgumentsProcessor;
+import org.mockito.internal.invocation.MockitoMethod;
+import org.mockito.internal.reporting.PrintSettings;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.Location;
+import org.mockito.invocation.StubInfo;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import static org.mockito.internal.exceptions.Reporter.cannotCallAbstractRealMethod;
+
+/**
+ * {@link Invocation} used when intercepting methods from an method entry hook.
+ */
+class InterceptedInvocation implements Invocation, VerificationAwareInvocation {
+    /** The mocked instance */
+    private final Object mock;
+
+    /** The method invoked */
+    private final MockitoMethod method;
+
+    /** expanded arguments to the method */
+    private final Object[] arguments;
+
+    /** raw arguments to the method */
+    private final Object[] rawArguments;
+
+    /** The super method */
+    private final SuperMethod superMethod;
+
+    /** sequence number of the invocation (different for each invocation) */
+    private final int sequenceNumber;
+
+    /** the location of the invocation (i.e. the stack trace) */
+    private final Location location;
+
+    /** Was this invocation {@link #markVerified() marked as verified} */
+    private boolean verified;
+
+    /** Should this be {@link #ignoreForVerification()} ignored for verification?} */
+    private boolean isIgnoredForVerification;
+
+    /** The stubinfo is this was {@link #markStubbed(StubInfo) markes as stubbed}*/
+    private StubInfo stubInfo;
+
+    /**
+     * Create a new invocation.
+     *
+     * @param mock mocked instance
+     * @param method method invoked
+     * @param arguments arguments to the method
+     * @param superMethod super method
+     * @param sequenceNumber sequence number of the invocation
+     */
+    InterceptedInvocation(Object mock, MockitoMethod method, Object[] arguments,
+                          SuperMethod superMethod, int sequenceNumber) {
+        this.mock = mock;
+        this.method = method;
+        this.arguments = ArgumentsProcessor.expandArgs(method, arguments);
+        this.rawArguments = arguments;
+        this.superMethod = superMethod;
+        this.sequenceNumber = sequenceNumber;
+        location = new LocationImpl();
+    }
+
+    @Override
+    public boolean isVerified() {
+        return verified || isIgnoredForVerification;
+    }
+
+    @Override
+    public int getSequenceNumber() {
+        return sequenceNumber;
+    }
+
+    @Override
+    public Location getLocation() {
+        return location;
+    }
+
+    @Override
+    public Object[] getRawArguments() {
+        return rawArguments;
+    }
+
+    @Override
+    public Class<?> getRawReturnType() {
+        return method.getReturnType();
+    }
+
+    @Override
+    public void markVerified() {
+        verified = true;
+    }
+
+    @Override
+    public StubInfo stubInfo() {
+        return stubInfo;
+    }
+
+    @Override
+    public void markStubbed(StubInfo stubInfo) {
+        this.stubInfo = stubInfo;
+    }
+
+    @Override
+    public boolean isIgnoredForVerification() {
+        return isIgnoredForVerification;
+    }
+
+    @Override
+    public void ignoreForVerification() {
+        isIgnoredForVerification = true;
+    }
+
+    @Override
+    public Object getMock() {
+        return mock;
+    }
+
+    @Override
+    public Method getMethod() {
+        return method.getJavaMethod();
+    }
+
+    @Override
+    public Object[] getArguments() {
+        return arguments;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getArgument(int index) {
+        return (T) arguments[index];
+    }
+
+    @Override
+    public Object callRealMethod() throws Throwable {
+        if (!superMethod.isInvokable()) {
+            throw cannotCallAbstractRealMethod();
+        }
+        return superMethod.invoke();
+    }
+
+    @Override
+    public int hashCode() {
+        // TODO SF we need to provide hash code implementation so that there are no unexpected,
+        //         slight perf issues
+        return 1;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !o.getClass().equals(this.getClass())) {
+            return false;
+        }
+        InterceptedInvocation other = (InterceptedInvocation) o;
+        return this.mock.equals(other.mock)
+                && this.method.equals(other.method)
+                && this.equalArguments(other.arguments);
+    }
+
+    private boolean equalArguments(Object[] arguments) {
+        return Arrays.equals(arguments, this.arguments);
+    }
+
+    @Override
+    public String toString() {
+        return new PrintSettings().print(ArgumentsProcessor.argumentsToMatchers(getArguments()),
+                this);
+    }
+
+    interface SuperMethod extends Serializable {
+        boolean isInvokable();
+
+        Object invoke() throws Throwable;
+    }
+}
diff --git a/dexmaker-mockito/src/main/java/com/android/dx/mockito/InvocationHandlerAdapter.java b/dexmaker-mockito/src/main/java/com/android/dx/mockito/InvocationHandlerAdapter.java
index e2a5957..6a6dc72 100644
--- a/dexmaker-mockito/src/main/java/com/android/dx/mockito/InvocationHandlerAdapter.java
+++ b/dexmaker-mockito/src/main/java/com/android/dx/mockito/InvocationHandlerAdapter.java
@@ -17,17 +17,22 @@
 package com.android.dx.mockito;
 
 import com.android.dx.stock.ProxyBuilder;
+
+import org.mockito.internal.creation.DelegatingMethod;
 import org.mockito.internal.debugging.LocationImpl;
-import org.mockito.internal.invocation.InvocationImpl;
-import org.mockito.internal.invocation.MockitoMethod;
-import org.mockito.internal.invocation.realmethod.RealMethod;
+import org.mockito.internal.invocation.ArgumentsProcessor;
 import org.mockito.internal.progress.SequenceNumber;
+import org.mockito.invocation.Invocation;
+import org.mockito.invocation.Location;
 import org.mockito.invocation.MockHandler;
+import org.mockito.invocation.StubInfo;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
+import static org.mockito.internal.exceptions.Reporter.cannotCallAbstractRealMethod;
+
 /**
  * Handles proxy method invocations to dexmaker's InvocationHandler by calling
  * a MockitoInvocationHandler.
@@ -49,9 +54,8 @@
             return System.identityHashCode(proxy);
         }
 
-        ProxiedMethod proxiedMethod = new ProxiedMethod(method);
-        return handler.handle(new InvocationImpl(proxy, proxiedMethod, args, SequenceNumber.next(),
-                proxiedMethod, new LocationImpl()));
+        return handler.handle(new ProxyInvocation(proxy, method, args, new DelegatingMethod
+                (method), SequenceNumber.next(), new LocationImpl()));
     }
 
     public MockHandler getHandler() {
@@ -73,51 +77,107 @@
                 && method.getParameterTypes().length == 0;
     }
 
-    private static class ProxiedMethod implements MockitoMethod, RealMethod {
+    /**
+     * Invocation on a proxy
+     */
+    private class ProxyInvocation implements Invocation {
+        private final Object proxy;
         private final Method method;
+        private final Object[] rawArgs;
+        private final int sequenceNumber;
+        private final Location location;
+        private final Object[] args;
 
-        ProxiedMethod(Method method) {
+        private StubInfo stubInfo;
+        private boolean isIgnoredForVerification;
+        private boolean verified;
+
+        private ProxyInvocation(Object proxy, Method method, Object[] rawArgs, DelegatingMethod
+                mockitoMethod, int sequenceNumber, Location location) {
+            this.rawArgs = rawArgs;
+            this.proxy = proxy;
             this.method = method;
+            this.sequenceNumber = sequenceNumber;
+            this.location = location;
+            args = ArgumentsProcessor.expandArgs(mockitoMethod, rawArgs);
         }
 
         @Override
-        public String getName() {
-            return method.getName();
+        public Object getMock() {
+            return proxy;
         }
 
         @Override
-        public Class<?> getReturnType() {
-            return method.getReturnType();
-        }
-
-        @Override
-        public Class<?>[] getParameterTypes() {
-            return method.getParameterTypes();
-        }
-
-        @Override
-        public Class<?>[] getExceptionTypes() {
-            return method.getExceptionTypes();
-        }
-
-        @Override
-        public boolean isVarArgs() {
-            return method.isVarArgs();
-        }
-
-        @Override
-        public Method getJavaMethod() {
+        public Method getMethod() {
             return method;
         }
 
         @Override
-        public Object invoke(Object target, Object[] arguments) throws Throwable {
-            return ProxyBuilder.callSuper(target, method, arguments);
+        public Object[] getArguments() {
+            return args;
         }
 
         @Override
-        public boolean isAbstract() {
-            return Modifier.isAbstract(method.getModifiers());
+        public <T> T getArgument(int index) {
+            return (T)args[index];
+        }
+
+        @Override
+        public Object callRealMethod() throws Throwable {
+            if (Modifier.isAbstract(method.getModifiers())) {
+                throw cannotCallAbstractRealMethod();
+            }
+            return ProxyBuilder.callSuper(proxy, method, rawArgs);
+        }
+
+        @Override
+        public boolean isVerified() {
+            return verified || isIgnoredForVerification;
+        }
+
+        @Override
+        public int getSequenceNumber() {
+            return sequenceNumber;
+        }
+
+        @Override
+        public Location getLocation() {
+            return location;
+        }
+
+        @Override
+        public Object[] getRawArguments() {
+            return rawArgs;
+        }
+
+        @Override
+        public Class<?> getRawReturnType() {
+            return method.getReturnType();
+        }
+
+        @Override
+        public void markVerified() {
+            verified = true;
+        }
+
+        @Override
+        public StubInfo stubInfo() {
+            return stubInfo;
+        }
+
+        @Override
+        public void markStubbed(StubInfo stubInfo) {
+            this.stubInfo = stubInfo;
+        }
+
+        @Override
+        public boolean isIgnoredForVerification() {
+            return isIgnoredForVerification;
+        }
+
+        @Override
+        public void ignoreForVerification() {
+            isIgnoredForVerification = true;
         }
     }
 }
diff --git a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
index e955d82..f2b457f 100644
--- a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
+++ b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
@@ -572,6 +572,69 @@
     }
 
     @Test
+    public void testBranchingZ() throws Exception {
+        Method lt = branchingZMethod(Comparison.LT);
+        assertEquals(Boolean.TRUE, lt.invoke(null, -1));
+        assertEquals(Boolean.FALSE, lt.invoke(null, 0));
+        assertEquals(Boolean.FALSE, lt.invoke(null, 1));
+
+        Method le = branchingZMethod(Comparison.LE);
+        assertEquals(Boolean.TRUE, le.invoke(null, -1));
+        assertEquals(Boolean.TRUE, le.invoke(null, 0));
+        assertEquals(Boolean.FALSE, le.invoke(null, 1));
+
+        Method eq = branchingZMethod(Comparison.EQ);
+        assertEquals(Boolean.FALSE, eq.invoke(null, -1));
+        assertEquals(Boolean.TRUE, eq.invoke(null, 0));
+        assertEquals(Boolean.FALSE, eq.invoke(null, 1));
+
+        Method ge = branchingZMethod(Comparison.GE);
+        assertEquals(Boolean.FALSE, ge.invoke(null, -1));
+        assertEquals(Boolean.TRUE, ge.invoke(null, 0));
+        assertEquals(Boolean.TRUE, ge.invoke(null, 1));
+
+        Method gt = branchingZMethod(Comparison.GT);
+        assertEquals(Boolean.FALSE, gt.invoke(null, -1));
+        assertEquals(Boolean.FALSE, gt.invoke(null, 0));
+        assertEquals(Boolean.TRUE, gt.invoke(null, 1));
+
+        Method ne = branchingZMethod(Comparison.NE);
+        assertEquals(Boolean.TRUE, ne.invoke(null, -1));
+        assertEquals(Boolean.FALSE, ne.invoke(null, 0));
+        assertEquals(Boolean.TRUE, ne.invoke(null, 1));
+    }
+
+    private Method branchingZMethod(Comparison comparison) throws Exception {
+        /*
+         * public static boolean call(int localA) {
+         *   if (a comparison 0) {
+         *     return true;
+         *   }
+         *   return false;
+         * }
+         */
+        reset();
+        MethodId<?, Boolean> methodId = GENERATED.getMethod(
+            TypeId.BOOLEAN, "call", TypeId.INT);
+        Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
+        Local<Integer> localA = code.getParameter(0, TypeId.INT);
+        Local<Boolean> result = code.newLocal(TypeId.get(boolean.class));
+        Label afterIf = new Label();
+        Label ifBody = new Label();
+        code.compareZ(comparison, ifBody, localA);
+        code.jump(afterIf);
+
+        code.mark(ifBody);
+        code.loadConstant(result, true);
+        code.returnValue(result);
+
+        code.mark(afterIf);
+        code.loadConstant(result, false);
+        code.returnValue(result);
+        return getMethod();
+    }
+
+    @Test
     public void testCastIntegerToInteger() throws Exception {
         Method intToLong = numericCastingMethod(int.class, long.class);
         assertEquals(0x0000000000000000L, intToLong.invoke(null, 0x00000000));
@@ -1103,6 +1166,74 @@
     }
 
     @Test
+    public void testStaticInitializer() throws Exception {
+        reset();
+
+        StaticFieldSpec<?>[] fields = new StaticFieldSpec[] {
+                new StaticFieldSpec<>(boolean.class, "booleanValue", true),
+                new StaticFieldSpec<>(byte.class, "byteValue", Byte.MIN_VALUE),
+                new StaticFieldSpec<>(short.class, "shortValue", Short.MAX_VALUE),
+                new StaticFieldSpec<>(int.class, "intValue", Integer.MIN_VALUE),
+                new StaticFieldSpec<>(long.class, "longValue", Long.MAX_VALUE),
+                new StaticFieldSpec<>(float.class, "floatValue", Float.MIN_VALUE),
+                new StaticFieldSpec<>(double.class, "doubleValue", Double.MAX_VALUE),
+                new StaticFieldSpec<>(String.class, "stringValue", "qwerty"),
+        };
+
+        MethodId<?, Void> clinit = GENERATED.getStaticInitializer();
+        assertTrue(clinit.isStaticInitializer());
+
+        Code code = dexMaker.declare(clinit, Modifier.STATIC);
+
+        for (StaticFieldSpec<?> field : fields) {
+            field.createLocal(code);
+        }
+
+        for (StaticFieldSpec<?> field : fields) {
+            field.initializeField(code);
+        }
+
+        code.returnVoid();
+
+        Class<?> generated = generateAndLoad();
+        for (StaticFieldSpec<?> fieldSpec : fields) {
+            Field field = generated.getDeclaredField(fieldSpec.name);
+            assertEquals(StaticFieldSpec.MODIFIERS, field.getModifiers());
+            assertEquals(fieldSpec.value, field.get(null));
+        }
+    }
+
+    private class StaticFieldSpec<T> {
+        Class<T> type;
+        TypeId<T> typeId;
+        String name;
+        T value;
+        FieldId<?, T> fieldId;
+        Local<T> local;
+
+        static final int MODIFIERS = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
+
+        public StaticFieldSpec(Class<T> type, String name, T value) {
+            this.type = type;
+            this.name = name;
+            this.value = value;
+
+            typeId = TypeId.get(type);
+            fieldId = GENERATED.getField(typeId, name);
+            dexMaker.declare(fieldId, MODIFIERS, null);
+        }
+
+        public void createLocal(Code code) {
+            local = code.newLocal(typeId);
+        }
+
+        public void initializeField(Code code) {
+            code.loadConstant(local, value);
+            code.sput(fieldId, local);
+        }
+    }
+
+    @Test
     public void testTypeCast() throws Exception {
         /*
          * public static String call(Object o) {
@@ -1881,8 +2012,9 @@
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         generateAndLoad();
+        int numFiles = getDataDirectory().listFiles().length;
         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
-        assertEquals(origSize + 2, getDataDirectory().listFiles().length);
+        assertTrue(origSize < numFiles);
 
         long lastModified  = getJarFiles()[0].lastModified();
 
@@ -1891,7 +2023,7 @@
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 2, getDataDirectory().listFiles().length);
+        assertEquals(numFiles, getDataDirectory().listFiles().length);
         assertEquals(lastModified, getJarFiles()[0].lastModified());
 
         // Create new dexmaker generators with different params.
@@ -1899,20 +2031,23 @@
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.DOUBLE);
         generateAndLoad();
-        assertEquals(origSize + 4, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.DOUBLE);
         generateAndLoad();
-        assertEquals(origSize + 6, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         // Create new dexmaker generator with different return types.
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.DOUBLE, defaultMethodName, TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 8, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         // Create new dexmaker generators with multiple methods.
         dexMaker = new DexMaker();
@@ -1920,35 +2055,38 @@
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN); // new method
         generateAndLoad();
-        assertEquals(origSize + 10, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 10, getDataDirectory().listFiles().length); // should already be cached.
+        assertEquals(numFiles, getDataDirectory().listFiles().length); // should already be cached.
 
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.INT, TypeId.BOOLEAN); // new method
         generateAndLoad();
-        assertEquals(origSize + 12, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.INT); // new method
         generateAndLoad();
-        assertEquals(origSize + 14, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addMethodToDexMakerGenerator(TypeId.INT, "differentName", TypeId.INT); // new method
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN);
         generateAndLoad();
-        assertEquals(origSize + 16, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
     }
 
     public static class BlankClassA {}
@@ -1966,15 +2104,15 @@
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         generateAndLoad();
         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
-        assertEquals(origSize + 2, getDataDirectory().listFiles().length);
+        int numFiles = getDataDirectory().listFiles().length;
+        assertTrue(origSize < numFiles);
 
         // Create new dexmaker generator with BlankClassB as supertype.
         dexMaker = new DexMaker();
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.get(BlankClassB.class));
         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 4, getDataDirectory().listFiles().length);
-
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
     }
 
     private void addMethodToDexMakerGenerator(TypeId<?> typeId, String methodName, TypeId<?>... params) throws Exception {
@@ -2004,7 +2142,8 @@
         addConstructorToDexMakerGenerator(TypeId.INT);
         generateAndLoad();
         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
-        assertEquals(origSize + 2, getDataDirectory().listFiles().length);
+        int numFiles = getDataDirectory().listFiles().length;
+        assertTrue(origSize < numFiles);
 
         long lastModified  = getJarFiles()[0].lastModified();
 
@@ -2012,7 +2151,7 @@
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addConstructorToDexMakerGenerator(TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 2, getDataDirectory().listFiles().length);
+        assertEquals(numFiles, getDataDirectory().listFiles().length);
         assertEquals(lastModified, getJarFiles()[0].lastModified());
 
         // Create new dexmaker generator with Generated(boolean) constructor.
@@ -2020,7 +2159,8 @@
         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
         generateAndLoad();
-        assertEquals(origSize + 4, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         // Create new dexmaker generator with multiple constructors.
         dexMaker = new DexMaker();
@@ -2028,7 +2168,8 @@
         addConstructorToDexMakerGenerator(TypeId.INT);
         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
         generateAndLoad();
-        assertEquals(origSize + 6, getDataDirectory().listFiles().length);
+        assertTrue(numFiles < getDataDirectory().listFiles().length);
+        numFiles = getDataDirectory().listFiles().length;
 
         // Ensure that order of constructors does not affect caching decision.
         dexMaker = new DexMaker();
@@ -2036,7 +2177,7 @@
         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
         addConstructorToDexMakerGenerator(TypeId.INT);
         generateAndLoad();
-        assertEquals(origSize + 6, getDataDirectory().listFiles().length);
+        assertEquals(numFiles, getDataDirectory().listFiles().length);
     }
 
     private void addConstructorToDexMakerGenerator(TypeId<?>... params) throws Exception {
diff --git a/dexmaker-tests/src/androidTest/java/com/android/dx/stock/ProxyBuilderTest.java b/dexmaker-tests/src/androidTest/java/com/android/dx/stock/ProxyBuilderTest.java
index 193a6d1..7127ec5 100644
--- a/dexmaker-tests/src/androidTest/java/com/android/dx/stock/ProxyBuilderTest.java
+++ b/dexmaker-tests/src/androidTest/java/com/android/dx/stock/ProxyBuilderTest.java
@@ -27,7 +27,9 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Random;
@@ -1106,7 +1108,8 @@
                 .build();
         assertEquals(1, proxyA.foo());
         assertEquals("bar", proxyA.bar());
-        assertEquals(2, versionedDxDir.listFiles().length);
+        int numFiles = versionedDxDir.listFiles().length;
+        assertTrue(numFiles > 0);
 
         ConcreteClassB proxyB = ProxyBuilder.forClass(ConcreteClassB.class)
                 .handler(new InvokeSuperHandler())
@@ -1114,6 +1117,61 @@
                 .build();
         assertEquals(0, proxyB.foo());
         assertEquals("bahhr", proxyB.bar());
-        assertEquals(4, versionedDxDir.listFiles().length);
+        assertTrue(numFiles < versionedDxDir.listFiles().length);
+    }
+
+    public static abstract class PartiallyFinalClass {
+        public String returnA() {
+            return "A";
+        }
+
+        public String returnB() {
+            return "B";
+        }
+
+        public String returnC() {
+            return "C";
+        }
+
+        public final String returnD() {
+            return "D";
+        }
+
+        public abstract String returnE();
+    }
+
+    @Test
+    public void testProxyingSomeMethods() throws Throwable {
+        ArrayList<Method> methodsToOverride = new ArrayList<>();
+        for (Method method : PartiallyFinalClass.class.getDeclaredMethods()) {
+            if (!Modifier.isFinal(method.getModifiers()) && !method.getName().equals("returnC")) {
+                methodsToOverride.add(method);
+            }
+        }
+
+        InvocationHandler handler = new InvokeSuperHandler() {
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                if (method.getName().equals("returnA")) {
+                    return "fake A";
+                } else if (method.getName().equals("returnC")) {
+                    // This will never trigger as "returnC" is not overridden
+                    return "fake C";
+                } else if (method.getName().equals("returnE")) {
+                    return "fake E";
+                } else {
+                    return super.invoke(proxy, method, args);
+                }
+            }
+        };
+
+        PartiallyFinalClass proxy = ProxyBuilder.forClass(PartiallyFinalClass.class)
+                .handler(handler).onlyMethods(methodsToOverride.toArray(new Method[]{})).build();
+
+        assertEquals("fake A", proxy.returnA());
+        assertEquals("B", proxy.returnB());
+        assertEquals("C", proxy.returnC());
+        assertEquals("D", proxy.returnD());
+        assertEquals("fake E", proxy.returnE());
+
     }
 }
diff --git a/dexmaker/build.gradle b/dexmaker/build.gradle
index eb337ec..73c9344 100644
--- a/dexmaker/build.gradle
+++ b/dexmaker/build.gradle
@@ -13,6 +13,4 @@
 
 dependencies {
     compile 'com.jakewharton.android.repackaged:dalvik-dx:7.1.0_r7'
-
-    testCompile 'junit:junit:4.12'
 }
diff --git a/dexmaker/src/main/java/com/android/dx/Code.java b/dexmaker/src/main/java/com/android/dx/Code.java
index 721d659..715d2b4 100644
--- a/dexmaker/src/main/java/com/android/dx/Code.java
+++ b/dexmaker/src/main/java/com/android/dx/Code.java
@@ -534,13 +534,24 @@
      */
     public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) {
         adopt(trueLabel);
-        // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make()
         Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
         addInstruction(new PlainInsn(rop, sourcePosition, null,
                 RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
     }
 
     /**
+     * Check if an int or reference equals to zero. If the comparison is true,
+     * execution jumps to {@code trueLabel}. If it is false, execution continues to
+     * the next instruction.
+     */
+    public <T> void compareZ(Comparison comparison, Label trueLabel, Local<?> a) {
+        adopt(trueLabel);
+        Rop rop = comparison.rop(StdTypeList.make(a.type.ropType));
+        addInstruction(new PlainInsn(rop, sourcePosition, null,
+                RegisterSpecList.make(a.spec())), trueLabel);
+    }
+
+    /**
      * Compare floats or doubles. This stores -1 in {@code target} if {@code
      * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
      * a > b}. This stores {@code nanValue} in {@code target} if either value
@@ -576,7 +587,7 @@
      * Copies the value in instance field {@code fieldId} of {@code instance} to
      * {@code target}.
      */
-    public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) {
+    public <D, V> void iget(FieldId<D, ? extends V> fieldId, Local<V> target, Local<D> instance) {
         addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
                 RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
         moveResult(target, true);
@@ -586,7 +597,7 @@
      * Copies the value in {@code source} to the instance field {@code fieldId}
      * of {@code instance}.
      */
-   public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
+   public <D, V> void iput(FieldId<D, V> fieldId, Local<? extends D> instance, Local<? extends V> source) {
         addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
                 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
     }
@@ -594,7 +605,7 @@
     /**
      * Copies the value in the static field {@code fieldId} to {@code target}.
      */
-    public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
+    public <V> void sget(FieldId<?, ? extends V> fieldId, Local<V> target) {
         addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
                 RegisterSpecList.EMPTY, catches, fieldId.constant));
         moveResult(target, true);
@@ -603,7 +614,7 @@
     /**
      * Copies the value in {@code source} to the static field {@code fieldId}.
      */
-    public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
+    public <V> void sput(FieldId<?, V> fieldId, Local<? extends V> source) {
         addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
                 RegisterSpecList.make(source.spec()), catches, fieldId.constant));
     }
diff --git a/dexmaker/src/main/java/com/android/dx/DexMaker.java b/dexmaker/src/main/java/com/android/dx/DexMaker.java
index ee8d722..f10ad8e 100644
--- a/dexmaker/src/main/java/com/android/dx/DexMaker.java
+++ b/dexmaker/src/main/java/com/android/dx/DexMaker.java
@@ -31,14 +31,12 @@
 import com.android.dx.rop.cst.CstString;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.StdTypeList;
-import com.android.dx.stock.ProxyBuilder;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
-import dalvik.system.DexClassLoader;
-
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -199,6 +197,7 @@
  */
 public final class DexMaker {
     private final Map<TypeId<?>, TypeDeclaration> types = new LinkedHashMap<>();
+    private ClassLoader sharedClassLoader;
 
     /**
      * Creates a new {@code DexMaker} instance, which can be used to create a
@@ -269,7 +268,7 @@
             flags = (flags & ~Modifier.SYNCHRONIZED) | AccessFlags.ACC_DECLARED_SYNCHRONIZED;
         }
 
-        if (method.isConstructor()) {
+        if (method.isConstructor() || method.isStaticInitializer()) {
             flags |= ACC_CONSTRUCTOR;
         }
 
@@ -357,15 +356,16 @@
         return "Generated_" + checksum +".jar";
     }
 
-    private ClassLoader generateClassLoader(ClassLoader classLoader, File result, File dexCache,
-            ClassLoader parent) {
+    public void setSharedClassLoader(ClassLoader classLoader) {
+        this.sharedClassLoader = classLoader;
+    }
+
+    private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader parent) {
         try {
-            boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
-                        "dexmaker.share_classloader", "false"));
-            if (shareClassLoader) {
-                ClassLoader loader = parent != null ? parent : classLoader;
+            if (sharedClassLoader != null) {
+                ClassLoader loader = parent != null ? parent : sharedClassLoader;
                 loader.getClass().getMethod("addDexPath", String.class).invoke(loader,
-                         result.getPath());
+                        result.getPath());
                 return loader;
             } else {
                 return (ClassLoader) Class.forName("dalvik.system.DexClassLoader")
@@ -411,11 +411,6 @@
      *     application's private data dir.
      */
     public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException {
-        return generateAndLoad(parent, parent, dexCache);
-    }
-
-    public ClassLoader generateAndLoad(ClassLoader classLoader, ClassLoader parent, File dexCache)
-            throws IOException {
         if (dexCache == null) {
             String property = System.getProperty("dexmaker.dexcache");
             if (property != null) {
@@ -433,7 +428,7 @@
         // Check that the file exists. If it does, return a DexClassLoader and skip all
         // the dex bytecode generation.
         if (result.exists()) {
-            return generateClassLoader(classLoader, result, dexCache, parent);
+            return generateClassLoader(result, dexCache, parent);
         }
 
         byte[] dex = generate();
@@ -453,7 +448,7 @@
         jarOut.write(dex);
         jarOut.closeEntry();
         jarOut.close();
-        return generateClassLoader(classLoader, result, dexCache, parent);
+        return generateClassLoader(result, dexCache, parent);
     }
 
     private static class TypeDeclaration {
diff --git a/dexmaker/src/main/java/com/android/dx/MethodId.java b/dexmaker/src/main/java/com/android/dx/MethodId.java
index 746d73e..d891b97 100644
--- a/dexmaker/src/main/java/com/android/dx/MethodId.java
+++ b/dexmaker/src/main/java/com/android/dx/MethodId.java
@@ -66,7 +66,15 @@
     }
 
     /**
-     * Returns the method's name. This is "&lt;init&gt;" if this is a constructor.
+     * Returns true if this method is the static initializer for its declaring class.
+     */
+    public boolean isStaticInitializer() {
+        return name.equals("<clinit>");
+    }
+
+    /**
+     * Returns the method's name. This is "&lt;init&gt;" if this is a constructor
+     * or "&lt;clinit&gt;" if a static initializer
      */
     public String getName() {
         return name;
diff --git a/dexmaker/src/main/java/com/android/dx/TypeId.java b/dexmaker/src/main/java/com/android/dx/TypeId.java
index de96028..7131f33 100644
--- a/dexmaker/src/main/java/com/android/dx/TypeId.java
+++ b/dexmaker/src/main/java/com/android/dx/TypeId.java
@@ -123,6 +123,10 @@
         return new MethodId<>(this, VOID, "<init>", new TypeList(parameters));
     }
 
+    public MethodId<T, Void> getStaticInitializer() {
+        return new MethodId<>(this, VOID, "<clinit>", new TypeList(new TypeId[0]));
+    }
+
     public <R> MethodId<T, R> getMethod(TypeId<R> returnType, String name, TypeId<?>... parameters) {
         return new MethodId<>(this, returnType, name, new TypeList(parameters));
     }
diff --git a/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java b/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
index b618c84..1363894 100644
--- a/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
+++ b/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
@@ -140,6 +140,8 @@
     private Class<?>[] constructorArgTypes = new Class[0];
     private Object[] constructorArgValues = new Object[0];
     private Set<Class<?>> interfaces = new HashSet<>();
+    private Method[] methods;
+    private boolean sharedClassLoader;
 
     private ProxyBuilder(Class<T> clazz) {
         baseClass = clazz;
@@ -195,6 +197,16 @@
         return this;
     }
 
+    public ProxyBuilder<T> onlyMethods(Method[] methods) {
+        this.methods = methods;
+        return this;
+    }
+
+    public ProxyBuilder<T> withSharedClassLoader() {
+        this.sharedClassLoader = true;
+        return this;
+    }
+
     /**
      * Create a new instance of the class to proxy.
      *
@@ -249,12 +261,10 @@
         @SuppressWarnings("unchecked")
         Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(baseClass);
         if (proxyClass != null) {
-            boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
-                        "dexmaker.share_classloader", "false"));
             boolean validClassLoader;
-            if (shareClassLoader) {
-                ClassLoader parent = parentClassLoader != null ? parentClassLoader
-                        : baseClass.getClassLoader();
+            if (sharedClassLoader) {
+                ClassLoader parent = parentClassLoader != null ? parentClassLoader : baseClass
+                        .getClassLoader();
                 validClassLoader = proxyClass.getClassLoader() == parent;
             } else {
                 validClassLoader = proxyClass.getClassLoader().getParent() == parentClassLoader;
@@ -270,11 +280,28 @@
         TypeId<? extends T> generatedType = TypeId.get("L" + generatedName + ";");
         TypeId<T> superType = TypeId.get(baseClass);
         generateConstructorsAndFields(dexMaker, generatedType, superType, baseClass);
-        Method[] methodsToProxy = getMethodsToProxyRecursive();
+
+        Method[] methodsToProxy;
+        if (methods == null) {
+            methodsToProxy = getMethodsToProxyRecursive();
+        } else {
+            methodsToProxy = methods;
+        }
+
+        // Sort the results array so that they are in a deterministic fashion.
+        Arrays.sort(methodsToProxy, new Comparator<Method>() {
+            @Override
+            public int compare(Method method1, Method method2) {
+                return method1.toString().compareTo(method2.toString());
+            }
+        });
+
         generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType);
         dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType, getInterfacesAsTypeIds());
-        ClassLoader classLoader = dexMaker.generateAndLoad(baseClass.getClassLoader(),
-                parentClassLoader, dexCache);
+        if (sharedClassLoader) {
+            dexMaker.setSharedClassLoader(baseClass.getClassLoader());
+        }
+        ClassLoader classLoader = dexMaker.generateAndLoad(parentClassLoader, dexCache);
         try {
             proxyClass = loadClass(classLoader, generatedName);
         } catch (IllegalAccessError e) {
@@ -654,22 +681,11 @@
             results[i++] = entry.originalMethod;
         }
 
-        // Sort the results array so that they are returned by this method
-        // in a deterministic fashion.
-        Arrays.sort(results, new Comparator<Method>() {
-            @Override
-            public int compare(Method method1, Method method2) {
-                return method1.toString().compareTo(method2.toString());
-            }
-        });
-
         return results;
     }
 
     private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods,
             Class<?> c) {
-        boolean shareClassLoader = Boolean.parseBoolean(System.getProperty(
-               "dexmaker.share_classloader", "false"));
         for (Method method : c.getDeclaredMethods()) {
             if ((method.getModifiers() & Modifier.FINAL) != 0) {
                 // Skip final methods, we can't override them. We
@@ -687,8 +703,8 @@
                 continue;
             }
             if (!Modifier.isPublic(method.getModifiers())
-					&& !Modifier.isProtected(method.getModifiers())
-                    && (!shareClassLoader || Modifier.isPrivate(method.getModifiers()))) {
+                    && !Modifier.isProtected(method.getModifiers())
+                    && (!sharedClassLoader || Modifier.isPrivate(method.getModifiers()))) {
                 // Skip private methods, since they are invoked through direct
                 // invocation (as opposed to virtual). Therefore, it would not
                 // be possible to intercept any private method defined inside
@@ -820,11 +836,11 @@
      * another. For these purposes, we consider two methods to be equal if they have the same
      * name, return type, and parameter types.
      */
-    private static class MethodSetEntry {
-        private final String name;
-        private final Class<?>[] paramTypes;
-        private final Class<?> returnType;
-        private final Method originalMethod;
+    public static class MethodSetEntry {
+        public final String name;
+        public final Class<?>[] paramTypes;
+        public final Class<?> returnType;
+        public final Method originalMethod;
 
         public MethodSetEntry(Method method) {
             originalMethod = method;
diff --git a/update_source.sh b/update_source.sh
index b38a072..ff7cc66 100755
--- a/update_source.sh
+++ b/update_source.sh
@@ -25,6 +25,10 @@
     LICENSE
     dexmaker
     dexmaker-mockito
+    dexmaker-mockito-tests
+    dexmaker-mockito-inline
+    dexmaker-mockito-inline-dispatcher
+    dexmaker-mockito-inline-tests/src
     dexmaker-tests/src
     "
 
@@ -36,7 +40,11 @@
 
 echo "Fetching Dexmaker source into $working_dir"
 git clone $SOURCE $working_dir/source
-(cd $working_dir/source; git checkout $VERSION)
+ORG_DIR=$(pwd)
+cd $working_dir/source
+git checkout $VERSION
+SHA=$(git rev-parse $VERSION)
+cd $ORG_DIR
 
 for include in ${INCLUDE}; do
   echo "Updating $include"
@@ -52,11 +60,16 @@
 
 # Move the dexmaker-tests AndroidManifest.xml into the correct position.
 mv dexmaker-tests/src/main/AndroidManifest.xml dexmaker-tests/AndroidManifest.xml
+mv dexmaker-mockito-tests/src/main/AndroidManifest.xml dexmaker-mockito-tests/AndroidManifest.xml
+mv dexmaker-mockito-inline-tests/src/main/AndroidManifest.xml dexmaker-mockito-inline-tests/AndroidManifest.xml
+
+# Remove 3rd party code
+rm -r dexmaker-mockito-inline/external
 
 echo "Updating README.version"
 
 # Update the version.
-perl -pi -e "s|^Version: .*$|Version: ${VERSION}|" "README.version"
+perl -pi -e "s|^Version: .*$|Version: ${VERSION} (${SHA})|" "README.version"
 
 # Remove any documentation about local modifications.
 mv README.version README.tmp