Merge "Scan for methods in extra interface hierarchy" am: ce33604360
am: 3a1969b22c

Change-Id: I5828b40d1850bdfe09a1c13fe640184e5f34191a
diff --git a/README.version b/README.version
index f823769..6f448fc 100644
--- a/README.version
+++ b/README.version
@@ -10,3 +10,4 @@
 
 Local Modifications:
         Support mocking of package private classes using dexmaker.share_classloader
+        Scan for methods in extra interface hierarchy
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 f0e7599..193a6d1 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
@@ -948,6 +948,20 @@
         proxyFor(YouDoNotChooseYourFamily.class).build();
     }
 
+    public interface ExtendsAnotherInterface extends FooReturnsString {
+
+    }
+
+    @Test
+    public void testExtraInterfaceExtendsInterface() throws Exception {
+        ExtendsAnotherInterface proxy = (ExtendsAnotherInterface)
+                proxyFor(SimpleClass.class)
+                        .implementing(ExtendsAnotherInterface.class)
+                        .build();
+        fakeHandler.setFakeResult(ExtendsAnotherInterface.class.getName());
+        assertEquals(ExtendsAnotherInterface.class.getName(), proxy.foo());
+    }
+
     /** Simple helper to add the most common args for this test to the proxy builder. */
     private <T> ProxyBuilder<T> proxyFor(Class<T> clazz) throws Exception {
         return ProxyBuilder.forClass(clazz)
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 1daf8e3..b618c84 100644
--- a/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
+++ b/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
@@ -632,9 +632,13 @@
     private Method[] getMethodsToProxyRecursive() {
         Set<MethodSetEntry> methodsToProxy = new HashSet<>();
         Set<MethodSetEntry> seenFinalMethods = new HashSet<>();
+        // Traverse the class hierarchy to ensure that all concrete methods (which could be marked
+        // as final) are visited before any abstract methods from interfaces.
         for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
             getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
         }
+        // Now traverse the interface hierarchy, starting with the ones implemented by the class,
+        // followed by any extra interfaces.
         for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
             for (Class<?> i : c.getInterfaces()) {
                 getMethodsToProxy(methodsToProxy, seenFinalMethods, i);
@@ -710,6 +714,14 @@
             }
             sink.add(entry);
         }
+
+        // Only visit the interfaces of this class if it is itself an interface. That prevents
+        // visiting interfaces of a class before its super classes.
+        if (c.isInterface()) {
+            for (Class<?> i : c.getInterfaces()) {
+                getMethodsToProxy(sink, seenFinalMethods, i);
+            }
+        }
     }
 
     private static <T> String getMethodNameForProxyOf(Class<T> clazz) {