[ApiModel] Extend visitors to recurse up in the hierarchy

Bug: 193414761
Bug: 188388130
Change-Id: Idabf2c232de527fb3c1a48da86447c4136fd3074
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index ee0ffb2..ed6767a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.androidapi;
 
+import static com.android.tools.r8.utils.AndroidApiLevel.getAndroidApiLevel;
+
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -28,38 +30,56 @@
 
   public abstract int getMemberCount();
 
-  public abstract TraversalContinuation visitFields(
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor);
+  public TraversalContinuation visitFields(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+    return visitFields(visitor, classReference, 1);
+  }
 
-  public abstract TraversalContinuation visitMethods(
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor);
+  public TraversalContinuation visitMethods(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+    return visitMethods(visitor, classReference, 1);
+  }
+
+  protected abstract TraversalContinuation visitFields(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApiClass);
+
+  protected abstract TraversalContinuation visitMethods(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApiClass);
 
   protected TraversalContinuation visitField(
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApiClass,
+      int minApiField,
       String name,
-      String typeDescriptor,
-      int apiLevel,
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+      String typeDescriptor) {
     return visitor.apply(
-        Reference.field(classReference, name, Reference.typeFromDescriptor(typeDescriptor)),
-        AndroidApiLevel.getAndroidApiLevel(apiLevel));
+        Reference.field(holder, name, Reference.typeFromDescriptor(typeDescriptor)),
+        getAndroidApiLevel(Integer.max(minApiClass, minApiField)));
   }
 
   protected TraversalContinuation visitMethod(
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApiClass,
+      int minApiMethod,
       String name,
       String[] formalTypeDescriptors,
-      String returnType,
-      int apiLevel,
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+      String returnType) {
     List<TypeReference> typeReferenceList = new ArrayList<>(formalTypeDescriptors.length);
     for (String formalTypeDescriptor : formalTypeDescriptors) {
       typeReferenceList.add(Reference.typeFromDescriptor(formalTypeDescriptor));
     }
     return visitor.apply(
         Reference.method(
-            classReference,
+            holder,
             name,
             typeReferenceList,
             returnType == null ? null : Reference.returnTypeFromDescriptor(returnType)),
-        AndroidApiLevel.getAndroidApiLevel(apiLevel));
+        getAndroidApiLevel(Integer.max(minApiClass, minApiMethod)));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c9e6675..312aaf4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1406,6 +1406,22 @@
                     }
                     return TraversalContinuation.CONTINUE;
                   }
+
+                  @Override
+                  protected TraversalContinuation visitFields(
+                      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+                      ClassReference holder,
+                      int minApi) {
+                    return null;
+                  }
+
+                  @Override
+                  protected TraversalContinuation visitMethods(
+                      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+                      ClassReference holder,
+                      int minApi) {
+                    return null;
+                  }
                 });
           });
     }
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
index dfaecd1..8c139fe 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -13,6 +13,7 @@
 import static org.objectweb.asm.Opcodes.DUP;
 import static org.objectweb.asm.Opcodes.F_SAME1;
 import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.ILOAD;
 import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -27,7 +28,9 @@
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IntBox;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -89,10 +92,11 @@
             .collect(Collectors.toList());
 
     for (ParsedApiClass apiClass : apiClasses) {
+      String apiClassDescriptor = getApiClassReference(apiClass).getDescriptor();
       consumer.accept(
-          getApiClassDescriptor(apiClass),
+          apiClassDescriptor,
           transformer(AndroidApiDatabaseClassTemplate.class)
-              .setClassDescriptor(getApiClassDescriptor(apiClass))
+              .setClassDescriptor(apiClassDescriptor)
               .addMethodTransformer(getInitTransformer(apiClass))
               .addMethodTransformer(getApiLevelTransformer(apiClass))
               .addMethodTransformer(getGetMemberCountTransformer(apiClass))
@@ -103,6 +107,8 @@
               .removeMethods(MethodPredicate.onName("placeHolderForGetMemberCount"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
+              .setSourceFile(
+                  DescriptorUtils.getSimpleClassNameFromDescriptor(apiClassDescriptor) + ".java")
               .computeMaxs()
               .transform(ClassWriter.COMPUTE_MAXS));
     }
@@ -137,13 +143,17 @@
             .replace("Template", "ForPackage_" + pkg.replace(".", "_")));
   }
 
-  private static String getApiClassDescriptor(ParsedApiClass apiClass) {
-    return DescriptorUtils.javaTypeToDescriptor(
-        AndroidApiDatabaseClassTemplate.class
-            .getTypeName()
-            .replace(
-                "Template",
-                "ForClass_" + apiClass.getClassReference().getTypeName().replace(".", "_")));
+  private static ClassReference getApiClassReference(ParsedApiClass apiClass) {
+    return fromTemplate(apiClass.getClassReference());
+  }
+
+  private static ClassReference fromTemplate(ClassReference classReference) {
+    String descriptor =
+        DescriptorUtils.javaTypeToDescriptor(
+            AndroidApiDatabaseClassTemplate.class
+                .getTypeName()
+                .replace("Template", "ForClass_" + classReference.getTypeName().replace(".", "_")));
+    return Reference.classFromDescriptor(descriptor);
   }
 
   // The transformer below changes AndroidApiDatabaseClassTemplate.<init> from:
@@ -190,17 +200,24 @@
   //     placeHolder();
   //     return TraversalContinuation.CONTINUE;
   // into
-  //    TraversalContinuation s1 = visitField("field1", "descriptor1", apiLevel1, visitor)
+  //    TraversalContinuation s1 = visitField(visitor, holder, minApiClass, apiLevel1, "field1",
+  //        "descriptor1")
   //    if (s1.shouldBreak()) {
   //       return s1;
   //    }
-  //    TraversalContinuation s2 = visitField("field2", "descriptor2", apiLevel2, visitor)
+  //    TraversalContinuation s2 = visitField(visitor, holder, minApiClass, apiLevel2, "field2",
+  //   //        "descriptor2")
   //    if (s2.shouldBreak()) {
   //       return s2;
   //    }
   //    ...
+  //    AndroidApiClassForClass_class_name1() super1 = new AndroidApiClassForClass_class_name1();
+  //    TraversalContinuation sN = super1.visitFields(
+  //        visitor, holder, max(minApiClass, minApiForLink));
+  //    ...
   //    return TraversalContinuation.CONTINUE;
   private static MethodTransformer getVisitFieldsTransformer(ParsedApiClass apiClass) {
+    IntBox lineNumberBox = new IntBox(0);
     return replaceCode(
         "placeHolderForVisitFields",
         transformer -> {
@@ -208,19 +225,26 @@
               (apiLevel, references) -> {
                 references.forEach(
                     reference -> {
+                      Label labelStart = new Label();
+                      transformer.visitLabel(labelStart);
+                      transformer.visitLineNumber(lineNumberBox.getAndIncrement(), labelStart);
                       transformer.visitVarInsn(ALOAD, 0);
+                      transformer.visitVarInsn(ALOAD, 1);
+                      transformer.visitVarInsn(ALOAD, 2);
+                      transformer.visitVarInsn(ILOAD, 3);
+                      transformer.visitLdcInsn(apiLevel.getLevel());
                       transformer.visitLdcInsn(reference.getFieldName());
                       transformer.visitLdcInsn(reference.getFieldType().getDescriptor());
-                      transformer.visitLdcInsn(apiLevel.getLevel());
-                      transformer.visitVarInsn(ALOAD, 1);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
                           ANDROID_API_CLASS.getBinaryName(),
                           "visitField",
-                          "(Ljava/lang/String;"
-                              + "Ljava/lang/String;"
+                          "(Ljava/util/function/BiFunction;"
+                              + descriptor(ClassReference.class)
                               + "I"
-                              + "Ljava/util/function/BiFunction;)"
+                              + "I"
+                              + "Ljava/lang/String;"
+                              + "Ljava/lang/String;)"
                               + TRAVERSAL_CONTINUATION.getDescriptor(),
                           false);
                       // Note that instead of storing the result here, we dup it on the stack.
@@ -246,6 +270,19 @@
                       transformer.visitInsn(POP);
                     });
               });
+          if (!apiClass.isInterface()) {
+            apiClass.visitSuperType(
+                (superType, apiLevel) -> {
+                  addMembersForParent(
+                      transformer,
+                      superType,
+                      "visitFields",
+                      apiLevel,
+                      lineNumberBox.getAndIncrement());
+                });
+          }
+          // No need to visit fields on interfaces since they have to be static and should not be
+          // called on a super instance.
         });
   }
 
@@ -253,19 +290,24 @@
   //     placeHolderForVisitMethods();
   //     return TraversalContinuation.CONTINUE;
   // into
-  //    TraversalContinuation s1 = visitMethod(
-  //      "method1", new String[] { "param11", ... , "param1X" }, null/return1, apiLevel1, visitor)
+  //    TraversalContinuation s1 = visitMethod(visitor, holder, minApiClass, apiLevel1,
+  //      "method1", new String[] { "param11", ... , "param1X" }, null/return1)
   //    if (s1.shouldBreak()) {
   //       return s1;
   //    }
-  //    TraversalContinuation s1 = visitMethod(
-  //      "method2", new String[] { "param21", ... , "param2X" }, null/return2, apiLevel2, visitor)
+  //    TraversalContinuation s1 = visitMethod(visitor, holder, minApiClass, apiLevel2,
+  //      "method2", new String[] { "param21", ... , "param2X" }, null/return2)
   //    if (s2.shouldBreak()) {
   //       return s2;
   //    }
   //    ...
+  //    AndroidApiClassForClass_class_name1() super1 = new AndroidApiClassForClass_class_name1();
+  //    TraversalContinuation sN = super1.visitMethods(
+  //      visitor, holder, max(minApiClass, minApiForLink));
+  //    ...
   //    return TraversalContinuation.CONTINUE;
   private static MethodTransformer getVisitMethodsTransformer(ParsedApiClass apiClass) {
+    IntBox lineNumberBox = new IntBox(0);
     return replaceCode(
         "placeHolderForVisitMethods",
         transformer -> {
@@ -273,7 +315,14 @@
               (apiLevel, references) -> {
                 references.forEach(
                     reference -> {
+                      Label labelStart = new Label();
+                      transformer.visitLabel(labelStart);
+                      transformer.visitLineNumber(lineNumberBox.getAndIncrement(), labelStart);
                       transformer.visitVarInsn(ALOAD, 0);
+                      transformer.visitVarInsn(ALOAD, 1);
+                      transformer.visitVarInsn(ALOAD, 2);
+                      transformer.visitVarInsn(ILOAD, 3);
+                      transformer.visitLdcInsn(apiLevel.getLevel());
                       transformer.visitLdcInsn(reference.getMethodName());
                       List<TypeReference> formalTypes = reference.getFormalTypes();
                       transformer.visitLdcInsn(formalTypes.size());
@@ -289,16 +338,17 @@
                       } else {
                         transformer.visitInsn(ACONST_NULL);
                       }
-                      transformer.visitLdcInsn(apiLevel.getLevel());
-                      transformer.visitVarInsn(ALOAD, 1);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
                           ANDROID_API_CLASS.getBinaryName(),
                           "visitMethod",
-                          "(Ljava/lang/String;"
-                              + "[Ljava/lang/String;Ljava/lang/String;"
+                          "(Ljava/util/function/BiFunction;"
+                              + descriptor(ClassReference.class)
                               + "I"
-                              + "Ljava/util/function/BiFunction;)"
+                              + "I"
+                              + "Ljava/lang/String;"
+                              + "[Ljava/lang/String;"
+                              + "Ljava/lang/String;)"
                               + TRAVERSAL_CONTINUATION.getDescriptor(),
                           false);
                       // Note that instead of storing the result here, we dup it on the stack.
@@ -324,9 +374,72 @@
                       transformer.visitInsn(POP);
                     });
               });
+          if (!apiClass.isInterface()) {
+            // Visit super types before interfaces emulating a poor man's resolutions.
+            apiClass.visitSuperType(
+                (superType, apiLevel) -> {
+                  addMembersForParent(
+                      transformer,
+                      superType,
+                      "visitMethods",
+                      apiLevel,
+                      lineNumberBox.getAndIncrement());
+                });
+          }
+          apiClass.visitInterface(
+              (classReference, apiLevel) -> {
+                addMembersForParent(
+                    transformer,
+                    classReference,
+                    "visitMethods",
+                    apiLevel,
+                    lineNumberBox.getAndIncrement());
+              });
         });
   }
 
+  private static void addMembersForParent(
+      MethodTransformer transformer,
+      ClassReference classReference,
+      String methodName,
+      AndroidApiLevel minApiLevel,
+      int lineNumber) {
+    String binaryName = fromTemplate(classReference).getBinaryName();
+    Label labelStart = new Label();
+    transformer.visitLabel(labelStart);
+    transformer.visitLineNumber(lineNumber, labelStart);
+    transformer.visitTypeInsn(NEW, binaryName);
+    transformer.visitInsn(DUP);
+    transformer.visitMethodInsn(INVOKESPECIAL, binaryName, "<init>", "()V", false);
+    transformer.visitVarInsn(ALOAD, 1);
+    transformer.visitVarInsn(ALOAD, 2);
+    // Compute the max api level compared to what is passed in.
+    transformer.visitLdcInsn(minApiLevel.getLevel());
+    transformer.visitVarInsn(ILOAD, 3);
+    transformer.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "max", "(II)I", false);
+    transformer.visitMethodInsn(
+        INVOKEVIRTUAL,
+        binaryName,
+        methodName,
+        "(Ljava/util/function/BiFunction;"
+            + descriptor(ClassReference.class)
+            + "I)"
+            + TRAVERSAL_CONTINUATION.getDescriptor(),
+        false);
+    // Note that instead of storing the result here, we dup it on the stack.
+    transformer.visitInsn(DUP);
+    transformer.visitMethodInsn(
+        INVOKEVIRTUAL, TRAVERSAL_CONTINUATION.getBinaryName(), "shouldBreak", "()Z", false);
+    Label label = new Label();
+    transformer.visitJumpInsn(IFEQ, label);
+    transformer.visitInsn(ARETURN);
+    transformer.visitLabel(label);
+    transformer.visitFrame(
+        F_SAME1, 0, new Object[] {}, 1, new Object[] {TRAVERSAL_CONTINUATION.getBinaryName()});
+    // The pop here is needed to remove the dupped value in the case we do not return.
+    transformer.visitInsn(POP);
+  }
+
   // The transformer below changes AndroidApiDatabasePackageTemplate.buildClass from:
   //    placeHolder();
   //    return null;
@@ -359,8 +472,7 @@
                     false);
                 Label label = new Label();
                 transformer.visitJumpInsn(IFEQ, label);
-                String binaryName =
-                    DescriptorUtils.getBinaryNameFromDescriptor(getApiClassDescriptor(apiClass));
+                String binaryName = getApiClassReference(apiClass).getBinaryName();
                 transformer.visitTypeInsn(NEW, binaryName);
                 transformer.visitInsn(DUP);
                 transformer.visitMethodInsn(INVOKESPECIAL, binaryName, "<init>", "()V", false);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index 2ad1e72..8b3571a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
 import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanBox;
@@ -36,7 +37,9 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiFunction;
 import org.junit.Test;
@@ -121,6 +124,8 @@
    * were introduced. Running main will generate a new jar and run tests on it to ensure it is
    * compatible with R8 sources and works as expected.
    *
+   * <p>The generated jar depends on r8NoManifestWithoutDeps.
+   *
    * <p>If the generated jar passes tests it will be moved to third_party/android_jar/api-database/
    * and override the current file in there.
    */
@@ -141,8 +146,12 @@
             AndroidApiDatabaseBuilderGeneratorTest::testNoPlaceHolder);
     tests.forEach(
         test -> {
-          if (!test.apply(generated, apiClasses)) {
-            throw new RuntimeException("Generated jar did not pass tests");
+          try {
+            if (!test.apply(generated, apiClasses)) {
+              throw new RuntimeException("Generated jar did not pass tests");
+            }
+          } catch (Exception e) {
+            throw new RuntimeException("Generated jar did not pass tests", e);
           }
         });
   }
@@ -232,44 +241,116 @@
   }
 
   private static String getExpected(List<ParsedApiClass> parsedApiClasses, boolean abort) {
+    Map<ClassReference, ParsedApiClass> parsedApiClassMap = new HashMap<>(parsedApiClasses.size());
+    parsedApiClasses.forEach(
+        parsedClass -> parsedApiClassMap.put(parsedClass.getClassReference(), parsedClass));
     List<String> expected = new ArrayList<>();
     parsedApiClasses.forEach(
         apiClass -> {
-          expected.add(apiClass.getClassReference().getDescriptor());
+          expected.add("CLASS: " + apiClass.getClassReference().getDescriptor());
           expected.add(apiClass.getApiLevel().getName());
           expected.add(apiClass.getTotalMemberCount() + "");
-          BooleanBox added = new BooleanBox(false);
-          apiClass.visitFieldReferences(
-              (apiLevel, fieldReferences) -> {
-                fieldReferences.forEach(
-                    fieldReference -> {
-                      if (added.isTrue() && abort) {
-                        return;
-                      }
-                      added.set();
-                      expected.add(fieldReference.getFieldType().getDescriptor());
-                      expected.add(fieldReference.getFieldName());
-                      expected.add(apiLevel.getName());
-                    });
-              });
-          added.set(false);
-          apiClass.visitMethodReferences(
-              (apiLevel, methodReferences) -> {
-                methodReferences.forEach(
-                    methodReference -> {
-                      if (added.isTrue() && abort) {
-                        return;
-                      }
-                      added.set();
-                      expected.add(methodReference.getMethodDescriptor());
-                      expected.add(methodReference.getMethodName());
-                      expected.add(apiLevel.getName());
-                    });
-              });
+          visitApiClass(expected, parsedApiClassMap, apiClass, apiClass.getApiLevel(), true, abort);
+          visitApiClass(
+              expected, parsedApiClassMap, apiClass, apiClass.getApiLevel(), false, abort);
         });
     return StringUtils.lines(expected);
   }
 
+  private static boolean visitApiClass(
+      List<String> expected,
+      Map<ClassReference, ParsedApiClass> parsedApiClassMap,
+      ParsedApiClass apiClass,
+      AndroidApiLevel apiLevel,
+      boolean visitFields,
+      boolean abort) {
+    BooleanBox added = new BooleanBox(false);
+    if (visitFields) {
+      added.set(visitFields(expected, apiClass, apiLevel, abort));
+    } else {
+      added.set(visitMethods(expected, apiClass, apiLevel, abort));
+    }
+    if (added.isTrue() && abort) {
+      return true;
+    }
+    // Go through super type methods if not interface.
+    if (!apiClass.isInterface()) {
+      apiClass.visitSuperType(
+          (classReference, linkApiLevel) -> {
+            if (added.isTrue() && abort) {
+              return;
+            }
+            ParsedApiClass superApiClass = parsedApiClassMap.get(classReference);
+            assert superApiClass != null;
+            added.set(
+                visitApiClass(
+                    expected,
+                    parsedApiClassMap,
+                    superApiClass,
+                    linkApiLevel.max(apiLevel),
+                    visitFields,
+                    abort));
+          });
+    }
+    if (!visitFields) {
+      apiClass.visitInterface(
+          (classReference, linkApiLevel) -> {
+            if (added.isTrue() && abort) {
+              return;
+            }
+            ParsedApiClass ifaceApiClass = parsedApiClassMap.get(classReference);
+            assert ifaceApiClass != null;
+            added.set(
+                visitApiClass(
+                    expected,
+                    parsedApiClassMap,
+                    ifaceApiClass,
+                    linkApiLevel.max(apiLevel),
+                    visitFields,
+                    abort));
+          });
+    }
+    return added.get();
+  }
+
+  private static boolean visitFields(
+      List<String> expected, ParsedApiClass apiClass, AndroidApiLevel minApiLevel, boolean abort) {
+    BooleanBox added = new BooleanBox(false);
+    apiClass.visitFieldReferences(
+        (apiLevel, fieldReferences) -> {
+          fieldReferences.forEach(
+              fieldReference -> {
+                if (added.isTrue() && abort) {
+                  return;
+                }
+                added.set();
+                expected.add(fieldReference.getFieldType().getDescriptor());
+                expected.add(fieldReference.getFieldName());
+                expected.add(apiLevel.max(minApiLevel).getName());
+              });
+        });
+    return added.get();
+  }
+
+  private static boolean visitMethods(
+      List<String> expected, ParsedApiClass apiClass, AndroidApiLevel minApiLevel, boolean abort) {
+    BooleanBox added = new BooleanBox(false);
+    apiClass.visitMethodReferences(
+        (apiLevel, methodReferences) -> {
+          methodReferences.forEach(
+              methodReference -> {
+                if (added.isTrue() && abort) {
+                  return;
+                }
+                added.set();
+                expected.add(methodReference.getMethodDescriptor());
+                expected.add(methodReference.getMethodName());
+                expected.add(apiLevel.max(minApiLevel).getName());
+              });
+        });
+    return added.get();
+  }
+
   public static class TestGeneratedMainVisitClasses {
 
     public static void main(String[] args) {
@@ -286,7 +367,7 @@
                 AndroidApiDatabaseBuilderTemplate.buildClass(
                     Reference.classFromDescriptor(descriptor));
             if (apiClass != null) {
-              System.out.println(descriptor);
+              System.out.println("CLASS: " + descriptor);
               System.out.println(apiClass.getApiLevel().getName());
               System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
@@ -317,7 +398,7 @@
                 AndroidApiDatabaseBuilderTemplate.buildClass(
                     Reference.classFromDescriptor(descriptor));
             if (apiClass != null) {
-              System.out.println(descriptor);
+              System.out.println("CLASS: " + descriptor);
               System.out.println(apiClass.getApiLevel().getName());
               System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
index faefd62..1a8b7e3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.apimodel;
 
 import com.android.tools.r8.androidapi.AndroidApiClass;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -34,7 +35,9 @@
 
   @Override
   public TraversalContinuation visitFields(
-      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApi) {
     // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
     placeHolderForVisitFields();
     return TraversalContinuation.CONTINUE;
@@ -42,7 +45,9 @@
 
   @Override
   public TraversalContinuation visitMethods(
-      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+      ClassReference holder,
+      int minApi) {
     // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
     placeHolderForVisitMethods();
     return TraversalContinuation.CONTINUE;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 4002bf9..24b9613 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -40,8 +41,9 @@
     this.maxApiLevel = maxApiLevel;
   }
 
-  private ParsedApiClass register(ClassReference reference, AndroidApiLevel apiLevel) {
-    ParsedApiClass parsedApiClass = new ParsedApiClass(reference, apiLevel);
+  private ParsedApiClass register(
+      ClassReference reference, AndroidApiLevel apiLevel, boolean isInterface) {
+    ParsedApiClass parsedApiClass = new ParsedApiClass(reference, apiLevel, isInterface);
     classes.add(parsedApiClass);
     return parsedApiClass;
   }
@@ -62,11 +64,19 @@
         continue;
       }
       ClassReference originalReference = clazz.getOriginalReference();
-      ParsedApiClass parsedApiClass = register(originalReference, apiLevel);
+      ParsedApiClass parsedApiClass = register(originalReference, apiLevel, clazz.isInterface());
       NodeList members = node.getChildNodes();
       for (int j = 0; j < members.getLength(); j++) {
         Node memberNode = members.item(j);
-        if (isMethod(memberNode)) {
+        if (isExtends(memberNode)) {
+          parsedApiClass.registerSuperType(
+              Reference.classFromBinaryName(getName(memberNode)),
+              hasSince(memberNode) ? getSince(memberNode) : apiLevel);
+        } else if (isImplements(memberNode)) {
+          parsedApiClass.registerInterface(
+              Reference.classFromBinaryName(getName(memberNode)),
+              hasSince(memberNode) ? getSince(memberNode) : apiLevel);
+        } else if (isMethod(memberNode)) {
           // TODO(b/190326408): Check for existence.
           parsedApiClass.register(
               getMethodReference(originalReference, memberNode),
@@ -109,16 +119,29 @@
     return node.getNodeName().equals("field");
   }
 
-  private AndroidApiLevel getMaxAndroidApiLevelFromNode(Node node, AndroidApiLevel defaultValue) {
-    if (node == null) {
-      return defaultValue;
-    }
+  private boolean isExtends(Node node) {
+    return node.getNodeName().equals("extends");
+  }
+
+  private boolean isImplements(Node node) {
+    return node.getNodeName().equals("implements");
+  }
+
+  private boolean hasSince(Node node) {
+    return node.getAttributes().getNamedItem("since") != null;
+  }
+
+  private AndroidApiLevel getSince(Node node) {
+    assert hasSince(node);
     Node since = node.getAttributes().getNamedItem("since");
-    if (since == null) {
+    return AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(since.getNodeValue()));
+  }
+
+  private AndroidApiLevel getMaxAndroidApiLevelFromNode(Node node, AndroidApiLevel defaultValue) {
+    if (node == null || !hasSince(node)) {
       return defaultValue;
     }
-    return defaultValue.max(
-        AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(since.getNodeValue())));
+    return defaultValue.max(getSince(node));
   }
 
   public static List<ParsedApiClass> getParsedApiClasses(
@@ -132,12 +155,17 @@
 
     private final ClassReference classReference;
     private final AndroidApiLevel apiLevel;
+    private final boolean isInterface;
+    private final Map<ClassReference, AndroidApiLevel> superTypes = new LinkedHashMap<>();
+    private final Map<ClassReference, AndroidApiLevel> interfaces = new LinkedHashMap<>();
     private final TreeMap<AndroidApiLevel, List<FieldReference>> fieldReferences = new TreeMap<>();
     private final Map<AndroidApiLevel, List<MethodReference>> methodReferences = new TreeMap<>();
 
-    private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+    private ParsedApiClass(
+        ClassReference classReference, AndroidApiLevel apiLevel, boolean isInterface) {
       this.classReference = classReference;
       this.apiLevel = apiLevel;
+      this.isInterface = isInterface;
     }
 
     public ClassReference getClassReference() {
@@ -171,6 +199,16 @@
       methodReferences.computeIfAbsent(apiLevel, ignoreArgument(ArrayList::new)).add(reference);
     }
 
+    private void registerSuperType(ClassReference superType, AndroidApiLevel apiLevel) {
+      AndroidApiLevel existing = superTypes.put(superType, apiLevel);
+      assert existing == null;
+    }
+
+    private void registerInterface(ClassReference iface, AndroidApiLevel apiLevel) {
+      AndroidApiLevel existing = interfaces.put(iface, apiLevel);
+      assert existing == null;
+    }
+
     public void visitFieldReferences(BiConsumer<AndroidApiLevel, List<FieldReference>> consumer) {
       fieldReferences.forEach(
           (apiLevel, references) -> {
@@ -186,5 +224,17 @@
             consumer.accept(apiLevel, references);
           });
     }
+
+    public void visitSuperType(BiConsumer<ClassReference, AndroidApiLevel> consumer) {
+      superTypes.forEach(consumer);
+    }
+
+    public void visitInterface(BiConsumer<ClassReference, AndroidApiLevel> consumer) {
+      interfaces.forEach(consumer);
+    }
+
+    public boolean isInterface() {
+      return isInterface;
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
index 32b111e..5b47c79 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
@@ -5,10 +5,8 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -35,7 +33,7 @@
     this.parameters = parameters;
   }
 
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void testR8() throws Exception {
     Method main = Main.class.getDeclaredMethod("main", String[].class);
     testForR8(parameters.getBackend())
@@ -55,16 +53,14 @@
             addTracedApiReferenceLevelCallBack(
                 (method, apiLevel) -> {
                   if (Reference.methodFromMethod(main).equals(method)) {
-                    // TODO(b/193414761): Should not be UNKNOWN.
-                    assertEquals(AndroidApiLevel.UNKNOWN, apiLevel);
+                    assertEquals(
+                        parameters.isCfRuntime()
+                            ? AndroidApiLevel.E
+                            : parameters.getApiLevel().max(AndroidApiLevel.E),
+                        apiLevel);
                   }
                 }))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              // TODO(b/193414761): We should analyze all members.
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Every member should have been analyzed"));
-            });
+        .compile();
   }
 
   /* Only here to get the test to compile */
@@ -77,7 +73,7 @@
 
     public static void main(String[] args) {
       // START_CONTINUATION_MASK is inherited from android/app/Service which was introduced at
-      // AndroidApiLevel.B.
+      // AndroidApiLevel.E.
       System.out.println(
           new /* android.accessibilityservice */ AccessibilityService().START_CONTINUATION_MASK);
     }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
index 22c0702..892f9d8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
@@ -5,15 +5,13 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import org.junit.Test;
@@ -35,7 +33,7 @@
     this.parameters = parameters;
   }
 
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void testR8() throws Exception {
     Method main = Main.class.getDeclaredMethod("main", String[].class);
     testForR8(parameters.getBackend())
@@ -48,16 +46,14 @@
             addTracedApiReferenceLevelCallBack(
                 (method, apiLevel) -> {
                   if (Reference.methodFromMethod(main).equals(method)) {
-                    // TODO(b/193414761): Should not be UNKNOWN.
-                    assertEquals(UNKNOWN, apiLevel);
+                    assertEquals(
+                        parameters.isCfRuntime()
+                            ? AndroidApiLevel.B
+                            : parameters.getApiLevel().max(AndroidApiLevel.B),
+                        apiLevel);
                   }
                 }))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              // TODO(b/193414761): We should analyze all members.
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Every member should have been analyzed"));
-            });
+        .compile();
   }
 
   public static class Main {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index 3f8bed1..04337ec 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -6,11 +6,9 @@
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
 import static com.android.tools.r8.utils.AndroidApiLevel.LATEST;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.utils.AndroidApiLevel.R;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -36,7 +34,7 @@
     this.parameters = parameters;
   }
 
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void testR8() throws Exception {
     // Landroid/view/accessibility/AccessibilityNodeInfo$AccessibilityAction; is introduced at api
     // level 21 and on api level 30 it implements android.os.Parcelable.
@@ -58,16 +56,10 @@
             addTracedApiReferenceLevelCallBack(
                 (method, apiLevel) -> {
                   if (Reference.methodFromMethod(main).equals(method)) {
-                    // TODO(b/193414761): Should be 30.
-                    assertEquals(UNKNOWN, apiLevel);
+                    assertEquals(R, apiLevel);
                   }
                 }))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              // TODO(b/193414761): We should analyze all members.
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Every member should have been analyzed"));
-            });
+        .compile();
   }
 
   public static class AccessibilityNodeInfo$AccessibilityAction {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
index 2c5868e..c79c69b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
@@ -5,10 +5,8 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -35,7 +33,7 @@
     this.parameters = parameters;
   }
 
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void testR8() throws Exception {
     Method main = Main.class.getDeclaredMethod("main", String[].class);
     testForR8(parameters.getBackend())
@@ -55,16 +53,17 @@
             addTracedApiReferenceLevelCallBack(
                 (method, apiLevel) -> {
                   if (Reference.methodFromMethod(main).equals(method)) {
-                    // TODO(b/193414761): Should not be UNKNOWN.
-                    assertEquals(AndroidApiLevel.UNKNOWN, apiLevel);
+                    // android.app.Service.stopSelf() was introduced at AndroidApiLevel.B but
+                    // android/accessibilityservice/AccessibilityService was introduced at D
+                    // so the minimum api level is D.
+                    assertEquals(
+                        parameters.isCfRuntime()
+                            ? AndroidApiLevel.D
+                            : parameters.getApiLevel().max(AndroidApiLevel.D),
+                        apiLevel);
                   }
                 }))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              // TODO(b/193414761): We should analyze all members.
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Every member should have been analyzed"));
-            });
+        .compile();
   }
 
   /* Only here to get the test to compile */
@@ -76,7 +75,8 @@
   public static class Main {
 
     public static void main(String[] args) {
-      // stopSelf() is inherited from android/app/Service which was introduced at AndroidApiLevel.B.
+      // AccessibilityService.stopSelf() is inherited from android/app/Service which was introduced
+      // at AndroidApiLevel.B.
       new /* android.accessibilityservice */ AccessibilityService().stopSelf();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 402d6b3..4ea9a82 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -120,6 +120,11 @@
   }
 
   @Override
+  public boolean isInterface() {
+    throw new Unreachable("Cannot determine if an absent class is an interface");
+  }
+
+  @Override
   public String getOriginalName() {
     return reference.getTypeName();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 8beec8a..f552a25 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -171,6 +171,8 @@
   @Override
   public abstract ClassAccessFlags getAccessFlags();
 
+  public abstract boolean isInterface();
+
   public abstract boolean isAbstract();
 
   public abstract boolean isAnnotation();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 1114bce..318f0d9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -294,6 +294,11 @@
   }
 
   @Override
+  public boolean isInterface() {
+    return dexClass.isInterface();
+  }
+
+  @Override
   public boolean isImplementing(ClassSubject subject) {
     assertTrue(subject.isPresent());
     for (DexType itf : getDexProgramClass().interfaces) {
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
index 97d5f12..2bbe555 100644
--- a/third_party/android_jar/api-database.tar.gz.sha1
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -1 +1 @@
-a3e0351d71082eb74073576e11c0632191fd8530
\ No newline at end of file
+829d7f32a482c16a2008a8878c33c58637c21a56
\ No newline at end of file