[ApiModel] Generate and use a serialized format for populating database

Bug: 199934316
Bug: 188388130
Change-Id: Ia06c07823b47b73a10ff73adc5b6b474ab03a8a7
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
new file mode 100644
index 0000000..058201f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.function.BiConsumer;
+
+/**
+ * This is an interface for all generated classes from api-versions.xml for building a database from
+ * a serialized hashed format.
+ */
+public interface AndroidApiForHashingClass {
+
+  DexType getType();
+
+  AndroidApiLevel getApiLevel();
+
+  void visitMethodsWithApiLevels(BiConsumer<DexMethod, AndroidApiLevel> consumer);
+
+  void visitFieldsWithApiLevels(BiConsumer<DexField, AndroidApiLevel> consumer);
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
new file mode 100644
index 0000000..bc4dd8b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -0,0 +1,163 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
+import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.structural.DefaultHashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class AndroidApiLevelHashingDatabaseImpl implements AndroidApiLevelDatabase {
+
+  private static final HashFunction DEFAULT_HASH_FUNCTION = Hashing.murmur3_128();
+
+  public static Object getDefaultHasher() {
+    return DEFAULT_HASH_FUNCTION.newHasher();
+  }
+
+  private final Int2ReferenceMap<AndroidApiLevel> lookupNonAmbiguousCache =
+      new Int2ReferenceOpenHashMap<AndroidApiLevel>();
+  private final Map<String, AndroidApiLevel> ambiguousHashesWithApiLevel = new HashMap<>();
+  private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>();
+  private final DexItemFactory factory;
+
+  public AndroidApiLevelHashingDatabaseImpl(
+      DexItemFactory factory, List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
+    this.factory = factory;
+    loadData();
+    predefinedApiTypeLookup.forEach(
+        apiClass -> {
+          DexType type = apiClass.getType();
+          lookupNonAmbiguousCache.put(type.hashCode(), NOT_SET);
+          ambiguousCache.put(type, apiClass.getApiLevel());
+          apiClass.visitMethodsWithApiLevels(
+              (method, apiLevel) -> {
+                lookupNonAmbiguousCache.put(method.hashCode(), NOT_SET);
+                ambiguousCache.put(method, apiLevel);
+              });
+          apiClass.visitFieldsWithApiLevels(
+              (field, apiLevel) -> {
+                lookupNonAmbiguousCache.put(field.hashCode(), NOT_SET);
+                ambiguousCache.put(field, apiLevel);
+              });
+        });
+  }
+
+  private void loadData() {
+    int[] hashIndices;
+    byte[] apiLevels;
+    List<String> ambiguous;
+    try (InputStream indicesInputStream =
+            getClass()
+                .getClassLoader()
+                .getResourceAsStream("api_database/api_database_hash_lookup.ser");
+        ObjectInputStream indicesObjectStream = new ObjectInputStream(indicesInputStream);
+        InputStream apiInputStream =
+            getClass()
+                .getClassLoader()
+                .getResourceAsStream("api_database/api_database_api_level.ser");
+        ObjectInputStream apiObjectStream = new ObjectInputStream(apiInputStream);
+        InputStream ambiguousInputStream =
+            getClass()
+                .getClassLoader()
+                .getResourceAsStream("api_database/api_database_ambiguous.txt")) {
+      hashIndices = (int[]) indicesObjectStream.readObject();
+      apiLevels = (byte[]) apiObjectStream.readObject();
+      ambiguous =
+          new BufferedReader(new InputStreamReader(ambiguousInputStream, StandardCharsets.UTF_8))
+              .lines()
+              .collect(Collectors.toList());
+    } catch (IOException | ClassNotFoundException e) {
+      throw new RuntimeException("Could not build api database");
+    }
+    assert hashIndices.length == apiLevels.length;
+    for (int i = 0; i < hashIndices.length; i++) {
+      byte apiLevel = apiLevels[i];
+      lookupNonAmbiguousCache.put(
+          hashIndices[i], apiLevel == -1 ? NOT_SET : AndroidApiLevel.getAndroidApiLevel(apiLevel));
+    }
+    ambiguous.forEach(this::parseAmbiguous);
+  }
+
+  /**
+   * All elements in the ambiguous map are on the form <key>:<api-level>. The reason for this
+   * additional map is that the keys collide for the items using the ordinary hashing function.
+   */
+  private void parseAmbiguous(String ambiguous) {
+    String[] split = ambiguous.split(":");
+    if (split.length != 2) {
+      throw new CompilationError("Expected two entries in ambiguous map");
+    }
+    ambiguousHashesWithApiLevel.put(
+        split[0], AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(split[1])));
+  }
+
+  @Override
+  public AndroidApiLevel getTypeApiLevel(DexType type) {
+    return lookupApiLevel(type);
+  }
+
+  @Override
+  public AndroidApiLevel getMethodApiLevel(DexMethod method) {
+    return lookupApiLevel(method);
+  }
+
+  @Override
+  public AndroidApiLevel getFieldApiLevel(DexField field) {
+    return lookupApiLevel(field);
+  }
+
+  private AndroidApiLevel lookupApiLevel(DexReference reference) {
+    AndroidApiLevel result = lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), UNKNOWN);
+    if (result != NOT_SET) {
+      return result;
+    }
+    return ambiguousCache.computeIfAbsent(
+        reference,
+        ignored -> {
+          Hasher defaultHasher = DEFAULT_HASH_FUNCTION.newHasher();
+          reference.accept(
+              type -> DefaultHashingVisitor.run(type, defaultHasher, DexType::acceptHashing),
+              field ->
+                  DefaultHashingVisitor.run(field, defaultHasher, StructuralItem::acceptHashing),
+              method ->
+                  DefaultHashingVisitor.run(method, defaultHasher, StructuralItem::acceptHashing));
+          String existingHash = defaultHasher.hash().toString();
+          AndroidApiLevel androidApiLevel = ambiguousHashesWithApiLevel.get(existingHash);
+          if (androidApiLevel == null) {
+            throw new CompilationError(
+                "Failed to find api level for reference: "
+                    + reference.toSourceString()
+                    + " with hash value: "
+                    + existingHash);
+          }
+          return androidApiLevel;
+        });
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java
similarity index 61%
rename from src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java
rename to src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java
index d98013e..e48b221 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java
@@ -16,11 +16,13 @@
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.function.BiFunction;
 
-public class AndroidApiLevelDatabaseImpl implements AndroidApiLevelDatabase {
+public class AndroidApiLevelObjectDatabaseImpl implements AndroidApiLevelDatabase {
 
-  private final HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup;
+  private final Map<DexType, AndroidApiClass> predefinedApiTypeLookup;
 
   private final AndroidApiClass SENTINEL =
       new AndroidApiClass(null) {
@@ -52,8 +54,57 @@
         }
       };
 
-  public AndroidApiLevelDatabaseImpl(HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) {
-    this.predefinedApiTypeLookup = predefinedApiTypeLookup;
+  public AndroidApiLevelObjectDatabaseImpl(
+      List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
+    Map<DexType, AndroidApiClass> predefinedMap = new HashMap<>();
+    for (AndroidApiForHashingClass androidApiClass : predefinedApiTypeLookupForHashing) {
+      predefinedMap.put(
+          androidApiClass.getType(),
+          new AndroidApiClass(androidApiClass.getType().asClassReference()) {
+            @Override
+            public AndroidApiLevel getApiLevel() {
+              return androidApiClass.getApiLevel();
+            }
+
+            @Override
+            public int getMemberCount() {
+              return 0;
+            }
+
+            @Override
+            protected TraversalContinuation visitFields(
+                BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+                ClassReference holder,
+                int minApiClass) {
+              Box<TraversalContinuation> continuationBox =
+                  new Box<>(TraversalContinuation.CONTINUE);
+              androidApiClass.visitFieldsWithApiLevels(
+                  (field, apiLevel) -> {
+                    if (continuationBox.get().shouldContinue()) {
+                      continuationBox.set(visitor.apply(field.asFieldReference(), apiLevel));
+                    }
+                  });
+              return continuationBox.get();
+            }
+
+            @Override
+            protected TraversalContinuation visitMethods(
+                BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+                ClassReference holder,
+                int minApiClass) {
+              Box<TraversalContinuation> continuationBox =
+                  new Box<>(TraversalContinuation.CONTINUE);
+              androidApiClass.visitMethodsWithApiLevels(
+                  (method, apiLevel) -> {
+                    if (continuationBox.get().shouldContinue()) {
+                      continuationBox.set(visitor.apply(method.asMethodReference(), apiLevel));
+                    }
+                  });
+              return continuationBox.get();
+            }
+          });
+    }
+    this.predefinedApiTypeLookup = predefinedMap;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 8ca1b59..df4cefd 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -10,7 +10,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.HashMap;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 
 public class AndroidApiReferenceLevelCache {
 
@@ -19,13 +20,21 @@
   private final AppView<?> appView;
 
   private AndroidApiReferenceLevelCache(AppView<?> appView) {
-    this(appView, new HashMap<>());
+    this(appView, ImmutableList.of());
   }
 
   private AndroidApiReferenceLevelCache(
-      AppView<?> appView, HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) {
+      AppView<?> appView, List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
     this.appView = appView;
-    androidApiLevelDatabase = new AndroidApiLevelDatabaseImpl(predefinedApiTypeLookup);
+    // TODO(b/199934316): When the implementation has been decided on, remove the others.
+    if (appView.options().apiModelingOptions().useHashingDatabase) {
+      androidApiLevelDatabase =
+          new AndroidApiLevelHashingDatabaseImpl(
+              appView.dexItemFactory(), predefinedApiTypeLookupForHashing);
+    } else {
+      androidApiLevelDatabase =
+          new AndroidApiLevelObjectDatabaseImpl(predefinedApiTypeLookupForHashing);
+    }
     desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration;
   }
 
@@ -40,18 +49,12 @@
         }
       };
     }
-    // The apiTypeLookup is build lazily except for the mocked api types that we define in tests
-    // externally.
-    HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup = new HashMap<>();
+    ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder();
     appView
         .options()
         .apiModelingOptions()
-        .visitMockedApiReferences(
-            (classReference, androidApiClass) ->
-                predefinedApiTypeLookup.put(
-                    appView.dexItemFactory().createType(classReference.getDescriptor()),
-                    androidApiClass));
-    return new AndroidApiReferenceLevelCache(appView, predefinedApiTypeLookup);
+        .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add);
+    return new AndroidApiReferenceLevelCache(appView, builder.build());
   }
 
   public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
@@ -61,10 +64,14 @@
   public AndroidApiLevel lookup(DexReference reference) {
     DexType contextType = reference.getContextType();
     if (contextType.isArrayType()) {
+      if (reference.isDexMethod()
+          && reference.asDexMethod().match(appView.dexItemFactory().objectMembers.clone)) {
+        return appView.options().minApiLevel;
+      }
       return lookup(contextType.toBaseType(appView.dexItemFactory()));
     }
     if (contextType.isPrimitiveType() || contextType.isVoidType()) {
-      return AndroidApiLevel.B;
+      return appView.options().minApiLevel;
     }
     DexClass clazz = appView.definitionFor(contextType);
     if (clazz == null) {
@@ -73,6 +80,9 @@
     if (!clazz.isLibraryClass()) {
       return appView.options().minApiLevel;
     }
+    if (isReferenceToJavaLangObject(reference)) {
+      return appView.options().minApiLevel;
+    }
     if (desugaredLibraryConfiguration.isSupported(reference, appView)) {
       // If we end up desugaring the reference, the library classes is bridged by j$ which is part
       // of the program.
@@ -83,4 +93,12 @@
         androidApiLevelDatabase::getFieldApiLevel,
         androidApiLevelDatabase::getMethodApiLevel);
   }
+
+  private boolean isReferenceToJavaLangObject(DexReference reference) {
+    if (reference.getContextType() == appView.dexItemFactory().objectType) {
+      return true;
+    }
+    return reference.isDexMethod()
+        && appView.dexItemFactory().objectMembers.isObjectMember(reference.asDexMethod());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index fd2c110..1f86536 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Collections;
@@ -49,6 +50,13 @@
   }
 
   @Override
+  public void acceptHashing(HashingVisitor visitor) {
+    visitor.visitDexType(holder);
+    visitor.visitDexString(name);
+    getReferencedTypes().forEach(visitor::visitDexType);
+  }
+
+  @Override
   public DexField self() {
     return this;
   }
@@ -192,6 +200,7 @@
     return type.toSourceString() + " " + holder.toSourceString() + "." + name.toSourceString();
   }
 
+  @Override
   public DexField withHolder(DexType holder, DexItemFactory dexItemFactory) {
     return dexItemFactory.createField(holder, type, name);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d31d8b7..24c2a3a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1370,6 +1370,11 @@
     public final DexMethod constructor;
     public final DexMethod finalize;
     public final DexMethod toString;
+    public final DexMethod notify;
+    public final DexMethod notifyAll;
+    public final DexMethod wait;
+    public final DexMethod waitLong;
+    public final DexMethod waitLongInt;
 
     private ObjectMembers() {
       // The clone method is installed on each array, so one has to use method.match(clone).
@@ -1382,6 +1387,36 @@
           finalizeMethodName, voidType.descriptor, DexString.EMPTY_ARRAY);
       toString = createMethod(objectDescriptor,
           toStringMethodName, stringDescriptor, DexString.EMPTY_ARRAY);
+      notify =
+          createMethod(objectDescriptor, notifyMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      notifyAll =
+          createMethod(
+              objectDescriptor, notifyAllMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      wait = createMethod(objectDescriptor, waitMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      waitLong =
+          createMethod(
+              objectDescriptor, waitMethodName, voidDescriptor, new DexString[] {longDescriptor});
+      waitLongInt =
+          createMethod(
+              objectDescriptor,
+              waitMethodName,
+              voidDescriptor,
+              new DexString[] {longDescriptor, intDescriptor});
+    }
+
+    public boolean isObjectMember(DexMethod method) {
+      return method.match(clone)
+          || method.match(getClass)
+          || method.match(constructor)
+          || method.match(finalize)
+          || method.match(toString)
+          || method.match(hashCode)
+          || method.match(equals)
+          || method.match(notify)
+          || method.match(notifyAll)
+          || method.match(wait)
+          || method.match(waitLong)
+          || method.match(waitLongInt);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 9b62855..1f5566c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -60,4 +60,6 @@
   public Iterable<DexType> getReferencedBaseTypes(DexItemFactory dexItemFactory) {
     return Iterables.transform(getReferencedTypes(), type -> type.toBaseType(dexItemFactory));
   }
+
+  public abstract DexMember<D, R> withHolder(DexType holder, DexItemFactory dexItemFactory);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index d7f282f..4f23975 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ArrayList;
@@ -60,6 +61,13 @@
     return visitor.visitDexMethod(this, other);
   }
 
+  @Override
+  public void acceptHashing(HashingVisitor visitor) {
+    visitor.visitDexType(holder);
+    visitor.visitDexString(name);
+    getReferencedTypes().forEach(visitor::visitDexType);
+  }
+
   public DexType getArgumentType(int argumentIndex, boolean isStatic) {
     if (isStatic) {
       return getParameter(argumentIndex);
@@ -207,9 +215,7 @@
 
   @Override
   public int computeHashCode() {
-    return holder.hashCode()
-        + proto.hashCode() * 7
-        + name.hashCode() * 31;
+    return holder.hashCode() * 7 + proto.hashCode() * 29 + name.hashCode() * 31;
   }
 
   @Override
@@ -225,7 +231,7 @@
 
   @Override
   public boolean match(DexMethod method) {
-    return match(method.getProto(), method.getName());
+    return method == this || match(method.getProto(), method.getName());
   }
 
   public boolean match(DexMethodSignature method) {
@@ -300,10 +306,11 @@
   }
 
   public DexMethod withHolder(DexDefinition definition, DexItemFactory dexItemFactory) {
-    return withHolder(definition.getReference(), dexItemFactory);
+    return withHolder(definition.getContextType(), dexItemFactory);
   }
 
-  public DexMethod withHolder(DexReference reference, DexItemFactory dexItemFactory) {
+  @Override
+  public DexMethod withHolder(DexType reference, DexItemFactory dexItemFactory) {
     return dexItemFactory.createMethod(reference.getContextType(), proto, name);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index dc30560..990eccc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -55,7 +55,7 @@
 
   @Override
   public int computeHashCode() {
-    return shorty.hashCode() + returnType.hashCode() * 7 + parameters.hashCode() * 31;
+    return shorty.hashCode() * 7 + returnType.hashCode() * 13 + parameters.hashCode() * 31;
   }
 
   public DexType getReturnType() {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 8128db2..453a445 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -440,19 +440,6 @@
       }
       return false;
     }
-    // Only merge if api reference level of source class is equal to target class.
-    if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-      AndroidApiLevel sourceApiLevel =
-          sourceClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
-      AndroidApiLevel targetApiLevel =
-          targetClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
-      if (sourceApiLevel != targetApiLevel) {
-        if (Log.ENABLED) {
-          AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
-        }
-        return false;
-      }
-    }
     return true;
   }
 
@@ -518,6 +505,20 @@
       }
       return false;
     }
+    // Only merge if api reference level of source class is equal to target class. The check is
+    // somewhat expensive.
+    if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      AndroidApiLevel sourceApiLevel =
+          sourceClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
+      AndroidApiLevel targetApiLevel =
+          targetClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
+      if (sourceApiLevel != targetApiLevel) {
+        if (Log.ENABLED) {
+          AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
+        }
+        return false;
+      }
+    }
     return true;
   }
 
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 9449f1b..2175ea0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.Version;
-import com.android.tools.r8.androidapi.AndroidApiClass;
+import com.android.tools.r8.androidapi.AndroidApiForHashingClass;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Backend;
@@ -33,6 +33,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexLibraryClass;
@@ -85,12 +86,10 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -1370,8 +1369,10 @@
 
     public boolean enableApiCallerIdentification = false;
     public boolean checkAllApiReferencesAreSet = true;
+    public boolean useHashingDatabase = true;
 
-    public void visitMockedApiReferences(BiConsumer<ClassReference, AndroidApiClass> consumer) {
+    public void visitMockedApiLevelsForReferences(
+        DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) {
       if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
         return;
       }
@@ -1385,62 +1386,37 @@
       classReferences.forEach(
           classReference -> {
             consumer.accept(
-                classReference,
-                new AndroidApiClass(classReference) {
+                new AndroidApiForHashingClass() {
+                  @Override
+                  public DexType getType() {
+                    return factory.createType(classReference.getDescriptor());
+                  }
+
                   @Override
                   public AndroidApiLevel getApiLevel() {
-                    return classApiMapping.getOrDefault(classReference, AndroidApiLevel.B);
+                    return classApiMapping.getOrDefault(classReference, AndroidApiLevel.UNKNOWN);
                   }
 
                   @Override
-                  public int getMemberCount() {
-                    return 0;
+                  public void visitMethodsWithApiLevels(
+                      BiConsumer<DexMethod, AndroidApiLevel> consumer) {
+                    methodApiMapping.forEach(
+                        (methodReference, apiLevel) -> {
+                          if (methodReference.getHolderClass().equals(classReference)) {
+                            consumer.accept(factory.createMethod(methodReference), apiLevel);
+                          }
+                        });
                   }
 
                   @Override
-                  public TraversalContinuation visitFields(
-                      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
-                    for (Entry<FieldReference, AndroidApiLevel> entry :
-                        fieldApiMapping.entrySet()) {
-                      if (!entry.getKey().getHolderClass().equals(classReference)) {
-                        continue;
-                      }
-                      if (EntryUtils.accept(entry, visitor).shouldBreak()) {
-                        return TraversalContinuation.BREAK;
-                      }
-                    }
-                    return TraversalContinuation.CONTINUE;
-                  }
-
-                  @Override
-                  public TraversalContinuation visitMethods(
-                      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
-                    for (Entry<MethodReference, AndroidApiLevel> entry :
-                        methodApiMapping.entrySet()) {
-                      if (!entry.getKey().getHolderClass().equals(classReference)) {
-                        continue;
-                      }
-                      if (EntryUtils.accept(entry, visitor).shouldBreak()) {
-                        return TraversalContinuation.BREAK;
-                      }
-                    }
-                    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;
+                  public void visitFieldsWithApiLevels(
+                      BiConsumer<DexField, AndroidApiLevel> consumer) {
+                    fieldApiMapping.forEach(
+                        (fieldReference, apiLevel) -> {
+                          if (fieldReference.getHolderClass().equals(classReference)) {
+                            consumer.accept(factory.createField(fieldReference), apiLevel);
+                          }
+                        });
                   }
                 });
           });
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
index d1b6653..99fd763 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
@@ -21,4 +21,9 @@
   public static <T> void run(T item, Hasher hasher, HashingAccept<T> hashingAccept) {
     HashingVisitorWithTypeEquivalence.run(item, hasher, t -> t, hashingAccept);
   }
+
+  /** Test entry-point where guava types are not present */
+  public static <T> void runFromTest(T item, Object hasher, HashingAccept<T> hashingAccept) {
+    run(item, (Hasher) hasher, hashingAccept);
+  }
 }
diff --git a/src/main/resources/api_database/api_database_ambiguous.txt b/src/main/resources/api_database/api_database_ambiguous.txt
new file mode 100644
index 0000000..3ffb464
--- /dev/null
+++ b/src/main/resources/api_database/api_database_ambiguous.txt
@@ -0,0 +1,473 @@
+828623182530e5b5c55265b6a20263be:9
+850665aac5116ea7c5dd3ed4d672cc68:1
+d853b59bd420a046a715750d924360d0:1
+1a9e947630aeed44da4b6e668e8ac214:1
+d96d3dfd316fb498b8989986135bb848:1
+281b8e41d83f2962772157285ee84c38:1
+943b393cd0aa3d44e2609c7a7008e36f:1
+b1f7571f9f4648ce69a9e79455d3df8a:1
+22def9631f444a8421104a4c6fd26845:1
+d448211aa9d792c542c8a12499062780:1
+843e12eb3c7a2c2c7c2d1b64058d619e:1
+6fa6dc2897ec7eaec581306eb1cbacae:1
+1b6414136749889f64248ad232774cd4:1
+aeb599339c06ae54c794c71be6b2b1f0:1
+9a57214d87842677a74f893161a4b9be:1
+aa69312c004e15f4fa8efce471b0cfc1:1
+619d04422f81707ae4354cb9c8b41ed5:1
+8fe152aee223813be38a1103ba72c66e:1
+e19b30d13798c227510c8fb9d5ca20ec:1
+c892a71c9b51dff23b4b6d4d78c46ca0:1
+97d4f84cdaf7386e47d1c8bea51a45b5:1
+e1ed03e2c2a9fd3a5866fb3771530dc9:1
+2efc71cc840f54a63d8ce5ff313a7e2f:1
+e367ef6e11b586255cbde00200814031:1
+e1863e976cd3e82c00e43304db807943:1
+3fb35789d4aee6489350c670908bd59e:1
+e63feec2ab98295f4cd1f09d21e35ce9:1
+f081ee3e1cb0dec48d0127716393edec:1
+1db593f23925e8dc2fb326b2a368335c:1
+089be95001c81698ed7a1ae6eadb480b:1
+62e65d09c285da8da8dbecff2a85eb5e:1
+466714572719ed5745a5b5cd886c880b:1
+080573d2f4292d0789f942bd39af7594:1
+449e3c63d3565b07e6e39ae06b36e9a1:1
+96ca46ef9f7f9ed890508a01899764eb:1
+603e49aeac3c895300c860d6bd596135:1
+0de08429c1f0d8c9804107e8dac151f0:1
+aba1819af7abfe17006adcf74c572eab:1
+a1061d47d7a301608f4f7b396bb81ee3:1
+cf60514a6e7b90495f5a47161478a33f:1
+e8890acb1c71aa0076dffdb89dff32e3:1
+987a12e7a344e556d9544c953d62ae8b:1
+8f28909290b786920a4b9fb635ba6677:1
+a0d0240922deab6b46a7ba1509aa3168:1
+d9eb1dbdb92903db5b787e2e70e34c39:1
+9c45ce862efc619f1cca962c8caeb844:1
+0ef069b7a04f779edb4eddc5d9e68664:1
+e04bb82da6212e6b9993e2f2177d7b34:1
+e97bced61f81e5e6486cabccafd486b9:1
+c2d6153e0b11fa485a1db9b895d7ca4f:1
+4a3f92803a9af2349140ee2d20d4016f:1
+45c104bb4202da85198b615491d1de23:1
+c841eae393bc4c731d85b057b263effa:1
+641c008d5e49914df8db013a5bb050e7:1
+00217564c415ce5c03abf66b6863f30a:1
+a98b9be4a37764162034e849091e2f2a:1
+7275881c062d5fa141b8beef6d60f05b:1
+36779dd0e73608472e1c22fca2914eba:1
+ddda7f2d9e16b789b36d9235d1a4b52a:1
+1e39ef60367e236fa336f96705b2b10f:1
+5b17c7c02bc7ab47a30ea2856738af8c:1
+9963627e831a6315767f99893ca660c1:1
+43baf0e2fd2a825c22e70c4f589441df:1
+eceb74f53e3044ee33a39ed0b6c9cc73:1
+769d9f4e5046a4e0a480b614eb530cb1:1
+844cdb718520e79a957bd880c6e046b5:1
+f25a0a19ee375dbe75922bf1817f37de:1
+fe40c696eb0567e090d8968b387100e4:1
+885d4879046a451b2330409a9c316a86:1
+ebb652ecc8bc2f4a2d616a8294624e1d:1
+60c0494c6547c2aa37081b0a045f2dbb:1
+d97969716904e1a32e4ce6c4de736fac:1
+c12acdd4d8e17d803e32eb32757be4e9:1
+83255b102f7327ffe0819cc9d48d764a:1
+a159e58a81a5d7e46b1d44f845486c25:1
+c9a7fe526ed238e56f029bdd9eadd4a8:1
+a5352e6f5efd89293cad68bde5359174:21
+0873c171286bf72adca073b8232909ad:21
+75fcc63a7299ec405cbbbae6c6f4124a:21
+4242f75bd579b5860e448896bd97161e:21
+f8e3f0c3200fb1dd44acfbd301d96bed:21
+3e972f384811f77fd62ce384910c7605:21
+746752a0385785d6c4a6af907b506ff8:21
+f2c67cccefcb8826a54b39122e002062:21
+e40adee8247464101fcb01d083025c31:21
+d96183e4ed9b9b2b075bd5f8c4364100:21
+0a8ab6673c7b4e87cb2faf2d7005362a:21
+810a68c672694a924abe0f0c59a9a394:21
+fe284dc96dc744beee1ddbb1a8e4c4ee:21
+d34d323d00595f4051cfe0621331317d:21
+711a52bc9e3eafe73e0c64c4ae6d5e6a:21
+542566d5040f1a84a95caf3c78e8d54f:21
+fadc0a0d51e6ccdc60992c058edd2711:21
+ccb12040fbbda66de180c11e6251049d:21
+052dc8ce89610356ae4c22dacb2668f6:21
+eb911a19cc8e2139a72a6bf025f0ef92:21
+8a9b11e0d4a3993f76485c069942489c:21
+b50ba38de9db0ecba8a7618f6138380b:21
+5a50c9965f9cd4ee5a321bbd530cc5b2:21
+301f4d37021f2c80999eedb2219554bd:21
+391832837fd0c5bd42b6d5d58dcd80f6:21
+535462c1df188932ba4375a241ac0a3b:21
+517cb4d902b3c235f31b41bcd012b184:21
+dd9d0f0182859fbb91bfe9cb3504becf:21
+4634339ec4a3dfef9d6a8a22e18c1354:21
+4015e477f56bfed839fc88a1ba749750:21
+be73d1320eb5c1229bc65e73f82039f3:21
+1c2ab72e50012527678e434999c9044a:21
+a09c3b287f292693c49b6ac6139f3aca:21
+67ce8616e53660f5898eaa146c7f3835:21
+5b728323f7668ae4ff0e895afaf15cea:21
+b070068bc98b0eafc27ab9bb38e7cfbb:21
+eadaf0fe065d92924ebd1cade7c57e35:21
+724567eb08f8a1a1dba97c82bd0a8d00:21
+06065f8f4550b0f3530468ae56e4437b:21
+eea2af8f17a87b391353b3878325bbd1:21
+aa462a0178a7561e7c756b157cbeeb38:21
+3af411c682f8264bb7c70794211c6248:21
+b972f761c2e0edfca60661ae2abf0218:21
+da13bac632b4f32422190d415f31bab8:21
+a7c7530bd46ed4e558e4d448f1ab9b7c:21
+e2c48e8e5e90a5dcc4cd070af2c1b20e:21
+0fc8da4c46369ca9dbcee59fb490681c:21
+5bb64f5d5c0764c9c1076e2c00ee3b3c:21
+f92f02539108f4c7034055e630ce5b1b:21
+21206168597de4cc003cf94bb2476441:21
+80ffcde01a4a187455c15c7b11ae7c33:21
+5adbafe8f34e077daeacc70305eed4f1:21
+d7d607c796c81475b0c57f2646b7e662:21
+4f89f7e86a6cb4ab329ea69918ae6f49:21
+0f88600ff268e2f32d56c63ce955aeba:21
+aeb824437b0a504c4a1a3109729a8817:21
+bd00bb3c1478ff33a781c1b48bfa9112:21
+eabb0f12ea319b61bdf53d9f4a6d7044:21
+b5653063b6061db848ef666076d4a44b:21
+1bf6b15eb573dea85120bcc84f419023:21
+71ae94c8959d23501b3a25269ed58f50:21
+c0673d423ec86b469092b2eedbf4e8db:21
+52986dd229c1677a8a8fea05023f7625:21
+364cf0eb88afb77fbc7209d2c1c7273e:21
+072b20ca146065654d29d72fa24fc89a:21
+50ddd3dc05b1198214cb57e9250d7076:21
+3eb1459c5b79cc1a562b145846a1b957:21
+d84c006146e1a54bba7a7122983ef085:21
+3a16ed65c968daf194834ce7a0225b8a:21
+6321e1881f171dc79e50d8e0cbc6ce7d:21
+5800fc71343c619b5e005bbab60912e0:21
+8e087b8bbcd92fe9389bf2f469f383f6:21
+e7e98eb2a51cc3cbf30bd532d8942294:21
+c173773ee22e94ab734c05bd504d3325:21
+5bef782b74608c4de25f4d053ae543a9:21
+ba7c51aed579c0f1b473ef2035b15a17:21
+169dfedc30ae5b95af848f3184206b4b:21
+549d9811413f0c4b84cd1e84290127f1:21
+668fa5f927950cbb34952cdb7e631eb8:21
+580fe13c6b95c05247c1770fff6d0fe9:21
+02d6383509108efe665de4a2aff24702:21
+68749591b1210c540ab306d416f49c68:21
+75269722fb78a1b61a850da9a73984fc:21
+58c819dd55c2558fed93faef88f45e89:21
+e479c8a303ccdaf45f673c78971d6a44:21
+cd94f79ea296e6126eb563a6e82a73b3:21
+ab652b1e99797dd3cc3bfb7e48db5d18:21
+4d3ab1362ff964f3947b437b16aba576:21
+53dea4d4defbb7512a31f4fa659437ae:21
+252e68c63ccea47b1d02a47837383c4e:21
+a9fff0b0f85b598b277aaf29fe537ad8:21
+caf732f52661f4c573b342a3d1da85bc:21
+9401285d5c41e4e0ee4581c62f4a2fc4:21
+5d0c7f0ad2c60ef78ae47f854b90a194:21
+983db0abe1d8976946a8a19eee412c9d:21
+e7ca6d1abe4cd55a491cdf58eb8f4602:17
+a4fc9c89f980d489cd69b5a9a5379279:28
+b63216a8584c12bf002f4ee8c3b0ac3e:19
+1ad1cfa76f9692958656f17b710a0ecc:19
+ebf47efadc4c50002b13da0b1cef0646:18
+106de6ca4d0e49988f3328ce380ff517:18
+2f4aedd17528c30f8a9f16b030ff708b:18
+93aca156e3f15799914f868852723e47:18
+17cbc97604a781acb78c668b2437ffbd:18
+1709449e191fe131236bd0b8c1928b71:18
+1587f690fd5c0c886630334e89e397ff:18
+a6cf882fc575147513b7b1b505fdd335:18
+dc6ad4bf6892204b9fca73d1ee9cab04:18
+9ae673cfbb5d90688edafbf7ec2862e4:18
+acf250fcdbf54725219af8c6a8a614d9:18
+07319a58381faa784dda046d5e8fd8ae:18
+77e978f6318d01be894de8944fdc05b4:18
+81be9a78d26d25c6ff47c7e9af30a92e:18
+e87bf9d0084b39c443e421cdb5d3fc43:18
+9e973ee7f4f5ad967d2ba5bf5d88911c:18
+b47e7bbf50ad208599056f8d0f1cc5ba:18
+47a9e25289b4ece230adcc3b60797712:18
+42e4cc1882d334757afbe74f4d8cec8e:18
+024481748991b858bf7e03b0b87ce125:18
+b6ced7e04b527d83cd660894a0059ea7:18
+717e536c3a13f3602fbaf1f607ee2963:18
+18b540e31006aad6ae7a26d967ec0e0e:18
+f1483fe126d64bd7544dba7fdf0559f4:18
+3c4a0d8270a52e43316a711a2fe83268:18
+2e13bb1bfba423473a6efba65d9c8190:18
+47a5d9010bdeb561176cf070ef483e91:18
+4f2e7d32ed2c7ead316770fbe84d8aae:18
+c625e1090fc4d72afd0673839af5d46f:18
+4cc2dc623ee702db7a5899c5c401a98c:18
+eff51a62305642c720d163085061ed63:18
+780c7e0f80b5479d00e2c3679974644d:18
+69e416f1afc250d6239788b4b1fb9bb4:18
+6d60d78c1ce897131dcf45743a8d6e49:18
+e16e8dcca650cf79557cb76a86edb4e9:18
+d71a32697989404ae782e105a5eb5e4c:18
+bfebe4871ed9c4b5f3300fe8dfa2a0a3:18
+93e215f230901f20bdca460cfe8c42af:18
+8efacada80287ebb7607758a0a82b0a0:18
+487bc1228fd817b641056a1a008984f8:18
+dc6c2915cd6f0a6fd0ab01365349599d:18
+1a3dbf72e4bd135a621b8ad78a7151c1:18
+01fd60d48ba7e1434bb72696282c7113:18
+83c42b475763d64935a9ddfc6a374b64:18
+ed9a26aaf7ddb748bd1e624aeb4fdb81:18
+eeb05b41976f7bb96572330854aaab34:18
+3c17437ba337e7ce8edad092f745a363:18
+8d8fd1ffc3ae24cb43798f4eb41fc4de:18
+003f7ab7088710994a289bab815aacbf:18
+488d943cdcedfca2b076152769fc3c51:18
+cab779407e29ba3377e6a47a4f842ec5:18
+a0ddcf0f7bb4aa9d38db983941f6908d:18
+b64b41d26010783cae2dff88c7e21f1d:18
+aac6d054ad0f9bea1f64702aea698ee3:18
+90b4163293f9e35f7091692b31d3b5d7:18
+9d66fceb1054d5e2ff207591ec216b70:18
+93a65566b576af220207e30cc89ef490:18
+b3248e6a2d819bd8bb42a15179188c52:18
+f95f0545feb040d2d6bfcad6897364d0:18
+058ec3044e95ed7fcaff93a2f3105d6f:18
+39ef3516cb80752cac1c541b43a1c2fa:18
+c4b4a2994711cb342b0c8c8ef409ce8b:18
+719c3b73816fa0db79747ea264abab5a:18
+c1295d79d79340152c6b98f1c9003e8c:18
+1211e656a8eaa4423e7678533a1e7e2a:18
+1d7847b88e0feaecd299b3c5c18be763:18
+e0c7417a74df21b55367f9e1ea6c2d37:18
+e49b0a2bd830bb3b96934490b2bc6ebe:18
+91790d7c5cf146ab3936919184a70015:18
+ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18
+860e7b1a68c02eef7d2e2d7b6a3e13b5:18
+458fda8d9863be24862bb62c910f427d:18
+e05ed0452c12e62ef74e9161159b1231:18
+9419764761c059b9c2f9cf640abfdeb0:18
+ea02175c4ef64b6370f619acd84f3c3c:18
+0b01d40e1ca37aa854e77c98cc153e77:18
+eb12f3e690b201aa171d794a8288af10:18
+b955e5418d490425f99d58f441ba4525:18
+34305e28da7351c9fe8a69e055e1c250:4
+f9a8c4364b7598dbeca0186f60e71ea6:4
+6a6e8a76be9fd95bf68f4be6f6cf1bbf:4
+14a880d95891af72380815d1c91baf73:4
+a2fa13ef7ba19d24adf59f8ecc12f0b4:4
+acd568e2592b223aaec17240031ed688:4
+cf994432aef1a88d7b82ffeecbbb96dc:4
+31927400c1d74c4dbcefe4c8c787ab8a:4
+94961af3e92bca38554a63bef2e453a6:4
+c562088538d99567be8dfdc54f830661:4
+459a7fef5f29ed6bafedadf927e315ee:4
+5c9d5847e56faea367acff998cb9e02b:4
+766fb7e580b7f7211bee8e77b98859b7:4
+53dc80d117986033f21a0b5f8609c87c:4
+3d8114f2ba5b5b51c547181a89a3466f:4
+89418ba487f62fbae3843e5dff5b9040:4
+ebe40ec90a872c67a1c27ae2c1eea950:4
+35462fa6c34cbb3610497e0e7ec5604a:4
+4f7b3ab75c9ad8d5b10fec60265a3809:4
+5123777f5c011dbd46ae38689b72a7d4:4
+38c98f315f4b01c1aa53a76a214a919e:4
+25989076c985cb6136599803fbcb48d5:4
+a96139fddc921f9c720e39737bfc1e23:4
+5cc99cf3cb2f9f5b2f3da2068a3c3b14:4
+9d57682c086fd2c37173c59f7df7f7f1:4
+bae7808504fe1ce016d86125d969287f:4
+b0767396a0f7f31b48ac96880c9cbf8b:4
+e2babe17ce0f4f5b30fee91c06c0e8fa:4
+d1c3eecefff445a312cfee0fa59d1431:4
+d5702e33cc1c8a04039657bec033a3e8:4
+1449b4ca79da7eaf4a3e04a2aa7b764d:4
+f50415b6582d0e25f6949d844bf93ee8:4
+3fef2395a8a091e7dea7a3b99dfeaea6:4
+03e302c7776c88610dafb333323263af:4
+b7d9936f2c7c87d2edac782f5403001b:4
+affb389d45f01bc18197bf0f49f4b118:4
+a4eb5a999085dfe2424b2896e38b25d1:4
+9cc9248b4494b77d7b40bbbf4dcef8b3:4
+043d915c3e3d58cb001a8b32deaffea3:4
+f9d3d4bc8a5944b63f51ae472e86813d:4
+a5e0393e11eadd75dd36fb5379ae018c:4
+5980bcaa38c00e4b837f2e9785bda3d2:4
+cc63328c0b60b9a790f8a1457dfaa511:4
+afdf18c7f6d8a437d84e390721b9b8b3:4
+b08cfcf9149aa4e94b5fb9695271c575:4
+10a9f2ae427efa8d37d59f435e83d62e:4
+d689449577a88d1b20d321d4e5fd32d9:4
+75298a5c8b84105fd9b9df7184dc110a:4
+5c8ed88fdd5d5ccb1ad7a61ecdf1ef9e:4
+35a4cc8262e2a04a80fc506d1551c2b7:4
+7ef43aabb95d2f3ad79438accc09cb4c:4
+aa6a754d71d091d666fea53f8769dfe9:4
+c8341c7fe17e7ae1484fdc25aef13df9:4
+4b6c6d5caf6e3d47ffd3dfdb329842f2:4
+a93c08a489709b0b6402127f3287a0a2:4
+500d600d630b1130b906355e694caf25:4
+e2f20c930af14f873866419d98bb33c5:4
+4f67d44d8a9c73b20f9dccb0fe708910:4
+3207a78e01966b63f857a845d312c5db:4
+dc772adf9d92e1330e1e6c1bc8bbdbea:4
+9a0f97a242e5b5c8bf70dd24775d973a:4
+c51604fe4030e75ce4ed3f545e470eee:4
+e8627b4481475f494ae99206e4d614a4:4
+a3abcd010f778136d77b5c00575faa93:4
+d806274a83ce72b89939c7e1fcc7dbf1:4
+e42535765b64f8f96c6a5b13c5aa30e9:4
+0389b0ce442166bc4298bd49f8ecfbff:4
+e6c5da69c2396e1a4c404a82509edb1e:4
+d62e86b3047119ec0941418ab6365bd4:4
+3c957e719e0ffce99b544fcc9a97ab3a:4
+638115e7b634086640ad6b300846ad6d:4
+f42bda5821df4a01dc97cb7d611a98a2:4
+15884ea4526d7af7c6dd5c5c89b81c97:4
+23fb4c5f959189ae9c90b968647934f3:4
+6b538e97c482ee0993607820484dcb08:4
+e7e718f70ee49560800db2d60270603f:11
+61c32de62fbec72e56083dade766f0f9:11
+f9684960dbadc5206f33484afeba57e1:11
+f849d1e2b1fbe25e1c88aa103ce68255:11
+1186fd5fd194d7f5c3c1b19aa753d315:11
+c74e6a2c593956c2e74a9f89dac73eeb:11
+e528c2b6258e260f575488c35f7683fb:11
+e5c61babf2a6ff8f6939b7a1db7e621f:11
+48921ae81d5ad84778fd75c9f58e1bf9:11
+92e7e194987c3b4db1a4a129af2cd05f:11
+f1d93cdeba77ba27e048fd0fbc6b0bad:11
+2cb543910592d445cf09fec159d0b421:11
+cd3bfb7c471b99eb7b7acb8011e4687f:11
+df936e8a13b49cdb9b5f82e150aca29b:11
+0e452471f905f2fb311c6146b31b5705:11
+b78f4f3edd36b6326873eea475ef2476:11
+fa0bc15e6c1bcb787bdea1073a4e2709:11
+417ebe49420a8d103b8b65c5d574fd84:11
+18f7890363dbd6262668cdcffb0f3a8d:11
+28c73ab584871ffb89a5304f87335f95:11
+aa4a8bfad0ec9e57489297bc67664cfd:11
+9a5dd5c667e21679c42fc7b2cd79769c:11
+59654e2c6df1b6a8a77928223e2ab332:11
+fea47d75f1c8cdb3a12de27678c005b6:11
+c6dd561601e904cee3707ba4cfe1350f:11
+02ceeb351445bd756a73fd5ab2a3d0c4:11
+3a3e699a3fdaaaf0b0d809d114c54e19:11
+ccad7981fb017550a8f7b8925a1d0648:11
+0803d11e374ba9493cfdd8fc17f7d34c:11
+31b7ca8caafc4ac8fd6ab18a01f5e9f5:11
+762dbaa4b1beab4b7113a750639d7f6f:11
+cf1b9fa0beb54f8f57c79446a3186baa:11
+13c8db7cad8e47068720c9f9b9d8213c:11
+d942884e00b9f288de96a8b0733372d9:11
+cd5c01104e0c46080a8d499f87de4991:11
+d8c43560e159bcb899c2180b8e01d8e3:24
+ae9c7aee12dfaaa56e43834dcea91f5f:24
+12233f83b06081671815562dce938d3b:24
+106252e3f31ee17239e1ee4184147deb:24
+1fb2cd38813bad6e5eeef74bcbcb133d:24
+c5bbd4174eee5cb68830801317071dd5:24
+b876313caea388f03200ac720ed528af:24
+f4fa53a97c83899fd2da9f4112f35339:24
+dc32c09ecdd9bda4703aa944cf1eb4ca:24
+07a59dae4166f6d504bb0793c5e75178:24
+efcb5024ce3514453d44735222903a55:24
+43445dc9e50e2c4fc327097d07ff749a:24
+bcb1787dd2def454fd3f31d1b2c0967a:24
+cace738770550444ee1f91000c32c147:24
+2cc65559141cece33ffdbc4e34a2ce3b:24
+b55a09768f05f29b15e1ff3a9feb7c92:24
+54282406a85f4410d4dfc72d5873ee96:24
+1c38913c447705e315ec4486da558c9d:24
+ca3f30645fa7b480b213f4228386a09a:24
+6d8800c5cc8ad9e4f3f28e777207a870:24
+b8dc6f9b9587159f978d643aeb8e4d07:24
+44c0d27dc28d4da28655c3c62a0d7cac:24
+91eb697571f68f5850875286c5e36080:24
+ae908f7abd7d347b0bf39fead3531c12:24
+d27ceaaa92270c450aeee582cc17b985:24
+145e730e204fdca9cf169521a3812ed1:24
+294141fdc8f17ac3c7e4d8687d8ad389:24
+cfde9ea95c663bed6d56df8778b01b79:24
+8569110aa82eb44ff0b12819867c66f8:24
+48de820418038280f70eca64f878c67b:24
+f389ac23dc4ef6641321adf43d712c32:24
+ef4661e0074b431762177e10c2ac8570:24
+46de27224f4cf6fb745e30bbd6aad267:24
+75c79913d4df6b467a67c40ce67a32fc:24
+2a000aca1e17b3b0343ddf151ee313f4:24
+e9d59e0325c6dd1dd78a998365435fac:24
+0a1d88442fc34d0081115d949f1c5fa9:24
+0a7ef5f8b75fa2dd79787b03dff923c9:24
+b247285b58ca44aad532b1c3b5a5dacf:24
+63f61697ff1319a76632338fe0c13ae9:24
+e8df9edc7e29252645d2b8a6a55c4e0f:24
+aaba729f45b1617d59cec26aa41937e3:24
+7e7ae6228d4ab54b90f0f580731941d7:24
+de1c84862deb139962240505133690f0:24
+9660da0a81f885de5a3b16f1f07a1067:24
+9dbabf8883b7a4e120fcbceb00face40:24
+c3f5202b30c04801da693013368a0da2:24
+b73113e1f7c2702cfd997dd84e5ac003:24
+9b5946e54278b49302da6ccd2a3a06e1:24
+c7a5a5244ef8d55b160e29df7e310b76:24
+74fa889a011613ee983f45aed7dcb656:24
+ccb57b751fd0ed3f7244a0e096b19076:24
+dcd61dd6bd1e0fec6888029656162559:24
+c2fa9ac71ea25473f3c384e5d3c3770b:24
+43de03bf819ac106a9a66f3d7b473bd1:24
+3ed4b5f668130ecec1aadb21bf0a671a:24
+ba4d4fc3c411bf5b477323a1329574b3:24
+4e82fa0797beeab61a7a0b9b3ba7505e:24
+1cff05a0b4291e8f289a1794f016eab5:24
+11e854ed754739797dbae75ff3159e82:24
+456c3251cceb566a82d3175390b6a663:24
+76fd080fdaa80390ca05399f4d0b49ae:24
+2daef0ca0e135ad904bf7e931690cf2b:24
+d49bee1da6282ff1efd4017e2c26ff55:24
+94bf636e74064a1f45c5e81777a8b541:24
+b502fdd440592e2d637704ebef9baf01:24
+67395fd629dae7b237421aa91bc12150:24
+b69dd3d2f26fc766f3b4193b089fa71e:24
+5a13a7fac55b3378629744099623199b:24
+6c21462d7f8529e6d017e054b02d96d3:24
+ef1a937ed3ed1277a46cbbfb589bcc7c:24
+a9446b05afd48f274f6469e9617dadde:24
+03b1c2c2b9e04b66ace80c8d891dad71:24
+5af3ef227d419594dc0316392c0fd762:24
+b4146bc53ba79957adf0bf7306b2212c:24
+f1136465059f10586c4fb8379126e010:24
+8796edfd2200b66560e180ecf2c8bd3a:24
+b36bec8fd49158c020b6d8c09cc02baf:26
+799e6e8b1fe3f2c45ec9703491bd67d5:8
+12011f7bb75e99008d3ef5bbe9087e6f:8
+53737c53af7f40961557a85fb4d11b55:8
+9fac9097934ab48cd9de3d124943c3fd:8
+a85598686e913ff9a84c078990e2b74f:8
+9389bbac5d37c3655c2d112b5fb15d79:8
+c7143cf932c4620b38f241a67c698de7:8
+6bb42f291857bf93ef2407e3fe8910b3:8
+d5360a8c6fd960a52c125ad6ee65b369:8
+4c0513c3ea8aa4e0d1671697ade55dc0:8
+9ab999023c47266804e1c23912c49590:8
+c83ba14b99739545b5611680823c5e64:8
+6c3b66ed41f23693ee6ed1a2f74383ca:8
+e064938f6319932b4685797aa810233f:8
+edf457cbc465fa576479f66ed5689a8e:8
+befe448f726cd55c55d708a8ed55b087:8
+4a35639e3583bbc1b36b56f0b50a2849:8
+a2c469af732e034c5017e1fbcbb6fba0:8
+47ef9cb8fb8fa55cfe73612c56fc5a0d:8
+58e29b4d0c775a250e2751d3cb70e44b:8
+2248103eacf690b2060d9f624dee5e41:8
+7aaff576c6f4f28ee8d80236c3943c3a:8
+62068b685c80a1da76ef1824af304e47:5
+beef3d82791567a66852903d9acde2d8:14
+8f8e727dba622a81127060f53efbaf60:14
+a58ec4e669821ad885c5e539bea8f8c0:14
+c183aebc8596eae7dbaf32f831d4bf1a:14
+579a40bd08705acc7099b631706917cd:14
+c979623104ac1df2ff0b56f2eb8f40a3:14
+4a406313d3d1fc7c56f2e4ae88033ef5:14
+0534bb586552faf59923fcbb66c5c9a8:14
+027b76c362f10b646fce08dd34457533:29
diff --git a/src/main/resources/api_database/api_database_api_level.ser b/src/main/resources/api_database/api_database_api_level.ser
new file mode 100644
index 0000000..e9e539a
--- /dev/null
+++ b/src/main/resources/api_database/api_database_api_level.ser
Binary files differ
diff --git a/src/main/resources/api_database/api_database_hash_lookup.ser b/src/main/resources/api_database/api_database_hash_lookup.ser
new file mode 100644
index 0000000..42a3b61
--- /dev/null
+++ b/src/main/resources/api_database/api_database_hash_lookup.ser
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 56a2b80..0d02a13 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -90,6 +90,7 @@
   static final Path[] EMPTY_PATH = {};
 
   public static final String SOURCE_DIR = "src/main/java/";
+  public static final String RESOURCES_DIR = "src/main/resources/";
   public static final String BUILD_DIR = "build/";
   public static final String GENERATED_TEST_BUILD_DIR = BUILD_DIR + "generated/test/";
   public static final String LIBS_DIR = BUILD_DIR + "libs/";
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
new file mode 100644
index 0000000..1c1d554
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -0,0 +1,244 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl;
+import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.structural.DefaultHashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.google.common.hash.Hasher;
+import java.io.FileOutputStream;
+import java.io.ObjectOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+public class AndroidApiHashingDatabaseBuilderGenerator extends TestBase {
+
+  /**
+   * Generate the information needed for looking up api level of references in the android.jar. This
+   * method will generate three different files and store them in the passed paths. pathToIndices
+   * will be an int array with hashcode entries for each DexReference. The pathToApiLevels is a byte
+   * array with a byte describing the api level that the index in pathToIndices has. To ensure that
+   * this lookup work the generate algorithm tracks all colliding hash codes such and stores them in
+   * another format. The indices map is populated with all colliding entries and a -1 is inserted
+   * for the api level.
+   */
+  public static void generate(
+      List<ParsedApiClass> apiClasses,
+      Path pathToIndices,
+      Path pathToApiLevels,
+      Path ambiguousDefinitions)
+      throws Exception {
+    Map<ClassReference, Map<DexMethod, AndroidApiLevel>> methodMap = new HashMap<>();
+    Map<ClassReference, Map<DexField, AndroidApiLevel>> fieldMap = new HashMap<>();
+    Map<ClassReference, ParsedApiClass> lookupMap = new HashMap<>();
+    DexItemFactory factory = new DexItemFactory();
+
+    Map<Integer, AndroidApiLevel> apiLevelMap = new HashMap<>();
+    Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap = new HashMap<>();
+    Map<AndroidApiLevel, Set<DexReference>> ambiguousMap = new HashMap<>();
+    // Populate maps for faster lookup.
+    for (ParsedApiClass apiClass : apiClasses) {
+      DexType type = factory.createType(apiClass.getClassReference().getDescriptor());
+      AndroidApiLevel existing = apiLevelMap.put(type.hashCode(), apiClass.getApiLevel());
+      assert existing == null;
+      reverseMap.put(type.hashCode(), Pair.create(type, apiClass.getApiLevel()));
+    }
+
+    for (ParsedApiClass apiClass : apiClasses) {
+      Map<DexMethod, AndroidApiLevel> methodsForApiClass = new HashMap<>();
+      apiClass.visitMethodReferences(
+          (apiLevel, methods) -> {
+            methods.forEach(
+                method -> methodsForApiClass.put(factory.createMethod(method), apiLevel));
+          });
+      Map<DexField, AndroidApiLevel> fieldsForApiClass = new HashMap<>();
+      apiClass.visitFieldReferences(
+          (apiLevel, fields) -> {
+            fields.forEach(field -> fieldsForApiClass.put(factory.createField(field), apiLevel));
+          });
+      methodMap.put(apiClass.getClassReference(), methodsForApiClass);
+      fieldMap.put(apiClass.getClassReference(), fieldsForApiClass);
+      lookupMap.put(apiClass.getClassReference(), apiClass);
+    }
+
+    BiConsumer<DexReference, AndroidApiLevel> addConsumer =
+        addReferenceToMaps(apiLevelMap, reverseMap, ambiguousMap);
+
+    for (ParsedApiClass apiClass : apiClasses) {
+      computeAllReferencesInHierarchy(
+              lookupMap,
+              factory,
+              factory.createType(apiClass.getClassReference().getDescriptor()),
+              apiClass,
+              AndroidApiLevel.B,
+              new IdentityHashMap<>())
+          .forEach(addConsumer);
+    }
+
+    int[] indices = new int[apiLevelMap.size()];
+    byte[] apiLevel = new byte[apiLevelMap.size()];
+    ArrayList<Integer> integers = new ArrayList<>(apiLevelMap.keySet());
+    for (int i = 0; i < integers.size(); i++) {
+      indices[i] = integers.get(i);
+      AndroidApiLevel androidApiLevel = apiLevelMap.get(integers.get(i));
+      apiLevel[i] =
+          (byte) (androidApiLevel == AndroidApiLevel.NOT_SET ? -1 : androidApiLevel.getLevel());
+    }
+
+    try (FileOutputStream fileOutputStream = new FileOutputStream(pathToIndices.toFile());
+        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
+      objectOutputStream.writeObject(indices);
+    }
+
+    try (FileOutputStream fileOutputStream = new FileOutputStream(pathToApiLevels.toFile());
+        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
+      objectOutputStream.writeObject(apiLevel);
+    }
+
+    String ambiguousMapSerialized = serializeAmbiguousMap(ambiguousMap);
+    Files.write(ambiguousDefinitions, ambiguousMapSerialized.getBytes(StandardCharsets.UTF_8));
+  }
+
+  /** This will serialize a collection of DexReferences with the api level they correspond to. */
+  private static String serializeAmbiguousMap(
+      Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) {
+    Set<String> seen = new HashSet<>();
+    StringBuilder sb = new StringBuilder();
+    ambiguousMap.forEach(
+        (apiLevel, references) -> {
+          references.forEach(
+              reference -> {
+                Hasher defaultHasher =
+                    (Hasher) AndroidApiLevelHashingDatabaseImpl.getDefaultHasher();
+                reference.accept(
+                    type ->
+                        DefaultHashingVisitor.runFromTest(
+                            type, defaultHasher, DexType::acceptHashing),
+                    field ->
+                        DefaultHashingVisitor.runFromTest(
+                            field, defaultHasher, StructuralItem::acceptHashing),
+                    method ->
+                        DefaultHashingVisitor.runFromTest(
+                            method, defaultHasher, StructuralItem::acceptHashing));
+                String referenceHash = defaultHasher.hash().toString();
+                if (!seen.add(referenceHash)) {
+                  throw new RuntimeException(
+                      "More than one item with key: <"
+                          + referenceHash
+                          + ">. The choice of key encoding will need to change to generate a valid"
+                          + " api database");
+                }
+                sb.append(referenceHash).append(":").append(apiLevel.getLevel()).append("\n");
+              });
+        });
+    return sb.toString();
+  }
+
+  private static BiConsumer<DexReference, AndroidApiLevel> addReferenceToMaps(
+      Map<Integer, AndroidApiLevel> apiLevelMap,
+      Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap,
+      Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) {
+    return ((reference, apiLevel) -> {
+      AndroidApiLevel existingMethod = apiLevelMap.put(reference.hashCode(), apiLevel);
+      if (existingMethod != null) {
+        apiLevelMap.put(reference.hashCode(), AndroidApiLevel.NOT_SET);
+        Pair<DexReference, AndroidApiLevel> existingPair = reverseMap.get(reference.hashCode());
+        addAmbiguousEntry(existingPair.getSecond(), existingPair.getFirst(), ambiguousMap);
+        addAmbiguousEntry(apiLevel, reference, ambiguousMap);
+      } else {
+        reverseMap.put(reference.hashCode(), Pair.create(reference, apiLevel));
+      }
+    });
+  }
+
+  private static void addAmbiguousEntry(
+      AndroidApiLevel apiLevel,
+      DexReference reference,
+      Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) {
+    ambiguousMap.computeIfAbsent(apiLevel, ignored -> new HashSet<>()).add(reference);
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T extends DexMember<?, ?>>
+      Map<T, AndroidApiLevel> computeAllReferencesInHierarchy(
+          Map<ClassReference, ParsedApiClass> lookupMap,
+          DexItemFactory factory,
+          DexType holder,
+          ParsedApiClass apiClass,
+          AndroidApiLevel linkLevel,
+          Map<T, AndroidApiLevel> additionMap) {
+    if (!apiClass.getClassReference().getDescriptor().equals(factory.objectDescriptor.toString())) {
+      apiClass.visitMethodReferences(
+          (apiLevel, methodReferences) -> {
+            methodReferences.forEach(
+                methodReference -> {
+                  T member = (T) factory.createMethod(methodReference).withHolder(holder, factory);
+                  addIfNewOrApiLevelIsLower(linkLevel, additionMap, apiLevel, member);
+                });
+          });
+      apiClass.visitFieldReferences(
+          (apiLevel, fieldReferences) -> {
+            fieldReferences.forEach(
+                fieldReference -> {
+                  T member = (T) factory.createField(fieldReference).withHolder(holder, factory);
+                  addIfNewOrApiLevelIsLower(linkLevel, additionMap, apiLevel, member);
+                });
+          });
+      apiClass.visitSuperType(
+          (superType, apiLevel) -> {
+            computeAllReferencesInHierarchy(
+                lookupMap,
+                factory,
+                holder,
+                lookupMap.get(superType),
+                linkLevel.max(apiLevel),
+                additionMap);
+          });
+      apiClass.visitInterface(
+          (iFace, apiLevel) -> {
+            computeAllReferencesInHierarchy(
+                lookupMap,
+                factory,
+                holder,
+                lookupMap.get(iFace),
+                linkLevel.max(apiLevel),
+                additionMap);
+          });
+    }
+    return additionMap;
+  }
+
+  private static <T extends DexMember<?, ?>> void addIfNewOrApiLevelIsLower(
+      AndroidApiLevel linkLevel,
+      Map<T, AndroidApiLevel> additionMap,
+      AndroidApiLevel apiLevel,
+      T member) {
+    AndroidApiLevel currentApiLevel = apiLevel.max(linkLevel);
+    AndroidApiLevel existingApiLevel = additionMap.get(member);
+    if (existingApiLevel == null || currentApiLevel.isLessThanOrEqualTo(existingApiLevel)) {
+      additionMap.put(member, currentApiLevel);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
new file mode 100644
index 0000000..8a0c060
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -0,0 +1,198 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl;
+import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
+import com.google.common.collect.ImmutableList;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AndroidApiHashingDatabaseBuilderGeneratorTest extends TestBase {
+
+  protected final TestParameters parameters;
+  private static final Path API_VERSIONS_XML =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-versions", "api-versions.xml");
+  private static final Path API_DATABASE_HASH_LOOKUP =
+      Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_hash_lookup.ser");
+  private static final Path API_DATABASE_API_LEVEL =
+      Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_api_level.ser");
+  private static final Path API_DATABASE_AMBIGUOUS =
+      Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_ambiguous.txt");
+  private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.R;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public AndroidApiHashingDatabaseBuilderGeneratorTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private static class GenerateDatabaseResourceFilesResult {
+
+    private final Path indices;
+    private final Path apiLevels;
+    private final Path ambiguous;
+
+    public GenerateDatabaseResourceFilesResult(Path indices, Path apiLevels, Path ambiguous) {
+      this.indices = indices;
+      this.apiLevels = apiLevels;
+      this.ambiguous = ambiguous;
+    }
+  }
+
+  private static GenerateDatabaseResourceFilesResult generateResourcesFiles() throws Exception {
+    return generateResourcesFiles(
+        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL));
+  }
+
+  private static GenerateDatabaseResourceFilesResult generateResourcesFiles(
+      List<ParsedApiClass> apiClasses) throws Exception {
+    TemporaryFolder temp = new TemporaryFolder();
+    temp.create();
+    Path indices = temp.newFile("indices.ser").toPath();
+    Path apiLevels = temp.newFile("apiLevels.ser").toPath();
+    Path ambiguous = temp.newFile("ambiguous.ser").toPath();
+    AndroidApiHashingDatabaseBuilderGenerator.generate(apiClasses, indices, apiLevels, ambiguous);
+    return new GenerateDatabaseResourceFilesResult(indices, apiLevels, ambiguous);
+  }
+
+  @Test
+  public void testCanParseApiVersionsXml() throws Exception {
+    // This tests makes a rudimentary check on the number of classes, fields and methods in
+    // api-versions.xml to ensure that the runtime tests do not vacuously succeed.
+    List<ParsedApiClass> parsedApiClasses =
+        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
+    IntBox numberOfFields = new IntBox(0);
+    IntBox numberOfMethods = new IntBox(0);
+    parsedApiClasses.forEach(
+        apiClass -> {
+          apiClass.visitFieldReferences(
+              ((apiLevel, fieldReferences) -> {
+                fieldReferences.forEach(field -> numberOfFields.increment());
+              }));
+          apiClass.visitMethodReferences(
+              ((apiLevel, methodReferences) -> {
+                methodReferences.forEach(field -> numberOfMethods.increment());
+              }));
+        });
+    // These numbers will change when updating api-versions.xml
+    assertEquals(4742, parsedApiClasses.size());
+    assertEquals(25144, numberOfFields.get());
+    assertEquals(38661, numberOfMethods.get());
+  }
+
+  @Test
+  public void testDatabaseGenerationUpToDate() throws Exception {
+    GenerateDatabaseResourceFilesResult result = generateResourcesFiles();
+    TestBase.filesAreEqual(result.indices, API_DATABASE_HASH_LOOKUP);
+    TestBase.filesAreEqual(result.apiLevels, API_DATABASE_API_LEVEL);
+    TestBase.filesAreEqual(result.ambiguous, API_DATABASE_AMBIGUOUS);
+  }
+
+  @Test
+  public void initializeApiDatabaseTimeTest() {
+    DexItemFactory factory = new DexItemFactory();
+    long start = System.currentTimeMillis();
+    new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of());
+    long end = System.currentTimeMillis();
+    long timeSpan = end - start;
+    assertTrue("Time used was " + timeSpan, timeSpan < 100);
+  }
+
+  @Test
+  public void testCanLookUpAllParsedApiClassesAndMembers() throws Exception {
+    List<ParsedApiClass> parsedApiClasses =
+        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
+    DexItemFactory factory = new DexItemFactory();
+    AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase =
+        new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of());
+    parsedApiClasses.forEach(
+        parsedApiClass -> {
+          DexType type = factory.createType(parsedApiClass.getClassReference().getDescriptor());
+          AndroidApiLevel apiLevel = androidApiLevelDatabase.getTypeApiLevel(type);
+          assertEquals(parsedApiClass.getApiLevel(), apiLevel);
+          parsedApiClass.visitMethodReferences(
+              (methodApiLevel, methodReferences) ->
+                  methodReferences.forEach(
+                      methodReference -> {
+                        DexMethod method = factory.createMethod(methodReference);
+                        androidApiLevelDatabase
+                            .getMethodApiLevel(method)
+                            .isLessThanOrEqualTo(methodApiLevel);
+                      }));
+          parsedApiClass.visitFieldReferences(
+              (fieldApiLevel, fieldReferences) ->
+                  fieldReferences.forEach(
+                      fieldReference -> {
+                        DexField field = factory.createField(fieldReference);
+                        androidApiLevelDatabase
+                            .getFieldApiLevel(field)
+                            .isLessThanOrEqualTo(fieldApiLevel);
+                      }));
+        });
+  }
+
+  /**
+   * Main entry point for building a database over references in framework to the api level they
+   * 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.
+   */
+  public static void main(String[] args) throws Exception {
+    List<ParsedApiClass> parsedApiClasses =
+        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
+    GenerateDatabaseResourceFilesResult result = generateResourcesFiles(parsedApiClasses);
+    verifyNoDuplicateHashes(result.indices);
+    Files.move(result.indices, API_DATABASE_HASH_LOOKUP, REPLACE_EXISTING);
+    Files.move(result.apiLevels, API_DATABASE_API_LEVEL, REPLACE_EXISTING);
+    Files.move(result.ambiguous, API_DATABASE_AMBIGUOUS, REPLACE_EXISTING);
+  }
+
+  private static void verifyNoDuplicateHashes(Path indicesPath) throws Exception {
+    Set<Integer> elements = new HashSet<>();
+    int[] indices;
+    try (FileInputStream fileInputStream = new FileInputStream(indicesPath.toFile());
+        ObjectInputStream indicesObjectStream = new ObjectInputStream(fileInputStream)) {
+      indices = (int[]) indicesObjectStream.readObject();
+      for (int index : indices) {
+        assertTrue(elements.add(index));
+      }
+    }
+    assertEquals(elements.size(), indices.length);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java
similarity index 99%
rename from src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
rename to src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java
index 8c139fe..c8b6c24 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java
@@ -43,7 +43,7 @@
 import org.objectweb.asm.Label;
 import org.objectweb.asm.Opcodes;
 
-public class AndroidApiDatabaseBuilderGenerator extends TestBase {
+public class AndroidApiObjectDatabaseBuilderGenerator extends TestBase {
 
   public static String generatedMainDescriptor() {
     return descriptor(AndroidApiDatabaseBuilderTemplate.class).replace("Template", "");
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
rename to src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java
index 91accca..299a25a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.AndroidApiDatabaseBuilderGenerator.generatedMainDescriptor;
+import static com.android.tools.r8.apimodel.AndroidApiObjectDatabaseBuilderGenerator.generatedMainDescriptor;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.junit.Assert.assertEquals;
 
@@ -48,7 +48,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class AndroidApiDatabaseBuilderGeneratorTest extends TestBase {
+public class AndroidApiObjectDatabaseBuilderGeneratorTest extends TestBase {
 
   protected final TestParameters parameters;
   private static final Path API_VERSIONS_XML =
@@ -62,7 +62,7 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public AndroidApiDatabaseBuilderGeneratorTest(TestParameters parameters) {
+  public AndroidApiObjectDatabaseBuilderGeneratorTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
@@ -75,7 +75,7 @@
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
     ZipBuilder builder = ZipBuilder.builder(temp.newFile("out.jar").toPath());
-    AndroidApiDatabaseBuilderGenerator.generate(
+    AndroidApiObjectDatabaseBuilderGenerator.generate(
         apiClasses,
         (descriptor, content) -> {
           try {
@@ -139,10 +139,10 @@
   private static void validateJar(Path generated, List<ParsedApiClass> apiClasses) {
     List<BiFunction<Path, List<ParsedApiClass>, Boolean>> tests =
         ImmutableList.of(
-            AndroidApiDatabaseBuilderGeneratorTest::testGeneratedOutputForVisitClasses,
-            AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesContinue,
-            AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesBreak,
-            AndroidApiDatabaseBuilderGeneratorTest::testNoPlaceHolder);
+            AndroidApiObjectDatabaseBuilderGeneratorTest::testGeneratedOutputForVisitClasses,
+            AndroidApiObjectDatabaseBuilderGeneratorTest::testBuildClassesContinue,
+            AndroidApiObjectDatabaseBuilderGeneratorTest::testBuildClassesBreak,
+            AndroidApiObjectDatabaseBuilderGeneratorTest::testNoPlaceHolder);
     tests.forEach(
         test -> {
           try {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index 18b35f5..7c4226c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -41,6 +42,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
         .addHorizontallyMergedClassesInspector(
             inspector -> {
               if (parameters.isDexRuntime()
@@ -66,6 +68,7 @@
     }
   }
 
+  @NoHorizontalClassMerging
   static class ApiSetter {
     static void set() {
       A.api = new Api();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
index f4609d4..5392a35 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -40,7 +41,7 @@
   public void testR8() throws Exception {
     Field apiField = Api.class.getDeclaredField("foo");
     testForR8(parameters.getBackend())
-        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, ApiBuilder.class, Main.class)
+        .addProgramClasses(ApiCallerCaller.class, ApiBuilder.class, Main.class)
         .addLibraryClasses(Api.class)
         .addDefaultRuntimeLibrary(parameters)
         .setMinApi(parameters.getApiLevel())
@@ -48,6 +49,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
@@ -55,9 +57,9 @@
             inspector -> {
               if (parameters.isDexRuntime()
                   && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
-                assertThat(inspector.clazz(ApiCaller.class), not(isPresent()));
+                assertThat(inspector.clazz(ApiBuilder.class), not(isPresent()));
               } else {
-                assertThat(inspector.clazz(ApiCaller.class), isPresent());
+                assertThat(inspector.clazz(ApiBuilder.class), isPresent());
               }
             })
         .addRunClasspathClasses(Api.class)
@@ -72,19 +74,14 @@
 
   public static class ApiBuilder {
 
-    public static Api build() {
-      return new Api();
+    public Api api;
+
+    public ApiBuilder() {
+      api = new Api();
     }
-  }
 
-  @NoHorizontalClassMerging
-  public static class ApiCaller {
-
-    private Api api;
-
-    public ApiCaller(Api api) {
-      this.api = api;
-      System.out.println(api.foo);
+    public String build() {
+      return api.foo;
     }
   }
 
@@ -93,7 +90,7 @@
 
     @NeverInline
     public static void callCallApi() {
-      new ApiCaller(ApiBuilder.build());
+      System.out.println(new ApiBuilder().build());
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
index a1ecb6a..9446479 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
@@ -49,6 +50,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForField(apiField, L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, L_MR1))
         .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
index bdaf09d..a480188 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -37,10 +37,10 @@
   @Test
   public void testR8() throws Exception {
     Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
-    Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Api.class);
-    Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
+    Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Object.class);
+    Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall", Object.class);
     testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class, A.class, ApiCaller.class)
+        .addProgramClassesAndInnerClasses(Main.class, A.class, ApiCaller.class)
         .addLibraryClasses(Api.class)
         .addDefaultRuntimeLibrary(parameters)
         .setMinApi(parameters.getApiLevel())
@@ -66,10 +66,10 @@
   public static class ApiCaller {
 
     @KeepConstantArguments
-    public static void callInterfaceMethod(Api api) {
+    public static void callInterfaceMethod(Object o) {
       System.out.println("ApiCaller::callInterfaceMethod");
-      if (api != null) {
-        api.apiLevel22();
+      if (o != null) {
+        ((Api) o).apiLevel22();
       }
     }
   }
@@ -78,16 +78,24 @@
   public static class A {
 
     @NeverInline
-    public static void noApiCall() {
+    public static void noApiCall(Object o) {
       System.out.println("A::noApiCall");
-      ApiCaller.callInterfaceMethod(null);
+      ApiCaller.callInterfaceMethod(o);
     }
   }
 
   public static class Main {
 
     public static void main(String[] args) {
-      A.noApiCall();
+      A.noApiCall(
+          args.length > 0
+              ? new Api() {
+                @Override
+                public void apiLevel22() {
+                  throw new RuntimeException("Foo");
+                }
+              }
+              : null);
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index f09f09d..a366d8a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelNoInliningOfHigherApiLevelVirtualTest.ApiCaller.callVirtualMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
@@ -49,6 +50,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
index 8553034..26561ec 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -133,7 +133,12 @@
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(
                 LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
-            .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
+            .addOptionsModification(
+                options -> {
+                  options.testing.enableD8ResourcesPassThrough = true;
+                  options.dataResourceConsumer = options.programConsumer.getDataResourceConsumer();
+                  options.testing.trackDesugaredAPIConversions = true;
+                })
             .compile();
     TestDiagnosticMessages diagnosticMessages = compile.getDiagnosticMessages();
     assertTrue(