Reland "Build apimodel cache on top of computed database builder"

This reverts commit 4e0cd0f7dc5ef7c59b328c28463693745e449e53.

Change-Id: Iaebb47b46059e98213c056618e6e24d22efedb2c
diff --git a/build.gradle b/build.gradle
index 7f0d08f..d9641a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -538,6 +538,8 @@
     }
 }
 
+compileJava.dependsOn downloadDeps
+
 sourceSets.configureEach { sourceSet ->
     tasks.named(sourceSet.compileJavaTaskName).configure {
         // Default disable errorprone (enabled and setup below).
@@ -727,6 +729,7 @@
 }
 
 task repackageDepsNew(type: ShadowJar) {
+    dependsOn downloadDeps
     configurations = [project.configurations.runtimeClasspath]
     mergeServiceFiles(it)
     exclude { it.getRelativePath().getPathString().endsWith("module-info.class") }
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 f33f5bd..ee0ffb2 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -26,6 +26,8 @@
 
   public abstract AndroidApiLevel getApiLevel();
 
+  public abstract int getMemberCount();
+
   public abstract TraversalContinuation visitFields(
       BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor);
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
new file mode 100644
index 0000000..124ea3f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -0,0 +1,150 @@
+// 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.apimodel.AndroidApiDatabaseBuilder;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
+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.Box;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class AndroidApiReferenceLevelCache {
+
+  private static final int BUILD_CACHE_TRESHOLD = 20;
+
+  private final Map<DexType, AndroidApiClass> apiTypeLookup;
+  private final Map<DexReference, AndroidApiLevel> apiMemberLookup = new IdentityHashMap<>();
+  private final AppView<?> appView;
+
+  private AndroidApiReferenceLevelCache(AppView<?> appView) {
+    this.appView = appView;
+    this.apiTypeLookup = new IdentityHashMap<>();
+  }
+
+  private AndroidApiReferenceLevelCache(
+      AppView<?> appView, Map<DexType, AndroidApiClass> apiTypeLookup) {
+    this.appView = appView;
+    this.apiTypeLookup = apiTypeLookup;
+  }
+
+  public static AndroidApiReferenceLevelCache create(AppView<?> appView) {
+    if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      // If enableApiCallerIdentification is not enabled then override lookup to always return
+      // AndroidApiLevel.B.
+      return new AndroidApiReferenceLevelCache(appView) {
+        @Override
+        public AndroidApiLevel lookup(DexReference reference) {
+          return AndroidApiLevel.B;
+        }
+      };
+    }
+    // The apiTypeLookup is build lazily except for the mocked api types that we define in tests
+    // externally.
+    Map<DexType, AndroidApiClass> apiTypeLookup = new IdentityHashMap<>();
+    appView
+        .options()
+        .apiModelingOptions()
+        .visitMockedApiReferences(
+            (classReference, androidApiClass) ->
+                apiTypeLookup.put(
+                    appView.dexItemFactory().createType(classReference.getDescriptor()),
+                    androidApiClass));
+    return new AndroidApiReferenceLevelCache(appView, apiTypeLookup);
+  }
+
+  public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
+    return lookup(reference).max(minApiLevel);
+  }
+
+  public AndroidApiLevel lookup(DexReference reference) {
+    DexType contextType = reference.getContextType();
+    assert !contextType.isArrayType();
+    if (contextType.isPrimitiveType() || contextType.isVoidType()) {
+      return AndroidApiLevel.B;
+    }
+    DexClass clazz = appView.definitionFor(contextType);
+    if (clazz == null) {
+      return AndroidApiLevel.UNKNOWN;
+    }
+    if (!clazz.isLibraryClass()) {
+      return appView.options().minApiLevel;
+    }
+    AndroidApiClass androidApiClass =
+        apiTypeLookup.computeIfAbsent(
+            contextType, type -> AndroidApiDatabaseBuilder.buildClass(type.asClassReference()));
+    if (androidApiClass == null) {
+      // This is a library class but we have no api model for it. This happens if using an older
+      // version of R8 to compile a new target. We simply have to disallow inlining of methods
+      // that has such references.
+      return AndroidApiLevel.UNKNOWN;
+    }
+    if (reference.isDexType()) {
+      return androidApiClass.getApiLevel();
+    }
+    return androidApiClass.getMemberCount() > BUILD_CACHE_TRESHOLD
+        ? findMemberByCaching(reference, androidApiClass)
+        : findMemberByIteration(reference.asDexMember(), androidApiClass);
+  }
+
+  private AndroidApiLevel findMemberByIteration(
+      DexMember<?, ?> reference, AndroidApiClass apiClass) {
+    DexItemFactory factory = appView.dexItemFactory();
+    // Similar to the case for api classes we are unable to find, if the member
+    // is unknown we have to be conservative.
+    Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
+    reference.apply(
+        field ->
+            apiClass.visitFields(
+                (fieldReference, apiLevel) -> {
+                  if (factory.createField(fieldReference) == field) {
+                    apiLevelBox.set(apiLevel);
+                    return TraversalContinuation.BREAK;
+                  }
+                  return TraversalContinuation.CONTINUE;
+                }),
+        method ->
+            apiClass.visitMethods(
+                (methodReference, apiLevel) -> {
+                  if (factory.createMethod(methodReference) == method) {
+                    apiLevelBox.set(apiLevel);
+                    return TraversalContinuation.BREAK;
+                  }
+                  return TraversalContinuation.CONTINUE;
+                }));
+    return apiLevelBox.get();
+  }
+
+  private AndroidApiLevel findMemberByCaching(DexReference reference, AndroidApiClass apiClass) {
+    buildCacheForMembers(reference.getContextType(), apiClass);
+    return apiMemberLookup.getOrDefault(reference, AndroidApiLevel.UNKNOWN);
+  }
+
+  private void buildCacheForMembers(DexType context, AndroidApiClass apiClass) {
+    assert apiClass.getMemberCount() > BUILD_CACHE_TRESHOLD;
+    // Use the context type as a token for us having build a cache for it.
+    if (apiMemberLookup.containsKey(context)) {
+      return;
+    }
+    DexItemFactory factory = appView.dexItemFactory();
+    apiClass.visitFields(
+        (fieldReference, apiLevel) -> {
+          apiMemberLookup.put(factory.createField(fieldReference), apiLevel);
+          return TraversalContinuation.CONTINUE;
+        });
+    apiClass.visitMethods(
+        (methodReference, apiLevel) -> {
+          apiMemberLookup.put(factory.createMethod(methodReference), apiLevel);
+          return TraversalContinuation.CONTINUE;
+        });
+    apiMemberLookup.put(context, AndroidApiLevel.UNKNOWN);
+  }
+}
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 ae9d9fb..bffc842 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.Iterables;
-import java.util.Map;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
@@ -64,12 +64,11 @@
   }
 
   public AndroidApiLevel computeApiLevelForReferencedTypes(
-      AppView<?> appView, Map<DexReference, AndroidApiLevel> apiLevelMap) {
-    AndroidApiLevel minApiLevel = appView.options().minApiLevel;
-    AndroidApiLevel apiLevel = minApiLevel;
+      AppView<?> appView, BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> computeMax) {
+    AndroidApiLevel computedLevel = appView.options().minApiLevel;
     for (DexType type : getReferencedBaseTypes(appView.dexItemFactory())) {
-      apiLevel = apiLevel.max(apiLevelMap.getOrDefault(type, minApiLevel));
+      computedLevel = computeMax.apply(type, computedLevel);
     }
-    return apiLevel;
+    return computedLevel;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index f93437e..62ed777 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -4,10 +4,10 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -16,19 +16,17 @@
 import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
 
 public class ApiModelAnalysis extends EnqueuerAnalysis {
 
   private final AppView<?> appView;
   private final AndroidApiLevel minApiLevel;
-  private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+  private final AndroidApiReferenceLevelCache referenceLevelCache;
 
-  public ApiModelAnalysis(
-      AppView<?> appView, Map<DexReference, AndroidApiLevel> referenceToApiLevelMap) {
+  public ApiModelAnalysis(AppView<?> appView, AndroidApiReferenceLevelCache referenceLevelCache) {
     this.appView = appView;
     this.minApiLevel = appView.options().minApiLevel;
-    this.referenceToApiLevelMap = referenceToApiLevelMap;
+    this.referenceLevelCache = referenceLevelCache;
   }
 
   @Override
@@ -46,6 +44,13 @@
   @Override
   public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {
     assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel);
+    if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) {
+      appView
+          .options()
+          .apiModelingOptions()
+          .tracedMethodApiLevelCallback
+          .accept(method.getMethodReference(), registry.getMaxApiReferenceLevel());
+    }
     setApiLevelForMember(method.getDefinition(), registry.getMaxApiReferenceLevel());
   }
 
@@ -77,6 +82,6 @@
   }
 
   private AndroidApiLevel computeApiLevelForReferencedTypes(DexMember<?, ?> member) {
-    return member.computeApiLevelForReferencedTypes(appView, referenceToApiLevelMap);
+    return member.computeApiLevelForReferencedTypes(appView, referenceLevelCache::lookupMax);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index f8aae86..ecd958d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -20,7 +20,7 @@
 import com.android.tools.r8.shaking.EnqueuerUseRegistryFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public class ProtoEnqueuerUseRegistry extends DefaultEnqueuerUseRegistry {
 
@@ -32,7 +32,7 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod currentMethod,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiLevelReferenceMap) {
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap) {
     super(appView, currentMethod, enqueuer, apiLevelReferenceMap);
     this.references = appView.protoShrinker().references;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index da86fe5..8a3a7e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -19,21 +19,21 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public class DefaultEnqueuerUseRegistry extends UseRegistry {
 
   protected final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final ProgramMethod context;
   protected final Enqueuer enqueuer;
-  private final Map<DexReference, AndroidApiLevel> apiReferenceMapping;
+  private final BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping;
   private AndroidApiLevel maxApiReferenceLevel;
 
   public DefaultEnqueuerUseRegistry(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod context,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiReferenceMapping) {
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping) {
     super(appView.dexItemFactory());
     this.appView = appView;
     this.context = context;
@@ -188,14 +188,13 @@
 
   private void setMaxApiReferenceLevel(DexReference reference) {
     if (reference.isDexMember()) {
-      this.maxApiReferenceLevel =
+      maxApiReferenceLevel =
           maxApiReferenceLevel.max(
               reference
                   .asDexMember()
                   .computeApiLevelForReferencedTypes(appView, apiReferenceMapping));
     }
-    this.maxApiReferenceLevel =
-        maxApiReferenceLevel.max(apiReferenceMapping.getOrDefault(reference, maxApiReferenceLevel));
+    maxApiReferenceLevel = apiReferenceMapping.apply(reference, maxApiReferenceLevel);
   }
 
   public AndroidApiLevel getMaxApiReferenceLevel() {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index abb6523..ea9b2d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -15,6 +15,7 @@
 import static java.util.Collections.emptySet;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.CfOrDexInstruction;
@@ -120,7 +121,6 @@
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
 import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
 import com.android.tools.r8.utils.Action;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.ListUtils;
@@ -255,7 +255,7 @@
 
   private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
 
-  private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
 
   /**
    * Tracks the dependency between a method and the super-method it calls, if any. Used to make
@@ -473,10 +473,7 @@
     } else {
       desugaredLibraryWrapperAnalysis = null;
     }
-    referenceToApiLevelMap = new IdentityHashMap<>();
-    if (options.apiModelingOptions().enableApiCallerIdentification) {
-      options.apiModelingOptions().appendToApiLevelMap(referenceToApiLevelMap, dexItemFactory);
-    }
+    apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
   }
 
   private AppInfoWithClassHierarchy appInfo() {
@@ -3028,7 +3025,7 @@
       registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
     }
     if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-      registerAnalysis(new ApiModelAnalysis(appView, referenceToApiLevelMap));
+      registerAnalysis(new ApiModelAnalysis(appView, apiReferenceLevelCache));
     }
     if (mode.isInitialTreeShaking()) {
       // This is simulating the effect of the "root set" applied rules.
@@ -3944,7 +3941,7 @@
 
   void traceCode(ProgramMethod method) {
     DefaultEnqueuerUseRegistry registry =
-        useRegistryFactory.create(appView, method, this, referenceToApiLevelMap);
+        useRegistryFactory.create(appView, method, this, apiReferenceLevelCache::lookupMax);
     method.registerCodeReferences(registry);
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processTracedCode(method, registry));
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
index 6bd8473..def9a41 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public interface EnqueuerUseRegistryFactory {
 
@@ -17,5 +17,5 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod currentMethod,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiLevelReferenceMap);
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap);
 }
diff --git a/src/main/java/com/android/tools/r8/utils/EntryUtils.java b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
new file mode 100644
index 0000000..e1d28b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
@@ -0,0 +1,15 @@
+// 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.utils;
+
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+
+public class EntryUtils {
+
+  public static <K, V, R> R accept(Entry<K, V> entry, BiFunction<K, V, R> consumer) {
+    return consumer.apply(entry.getKey(), entry.getValue());
+  }
+}
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 0a1fcc7..5af6963 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -14,6 +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.cf.CfVersion;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Backend;
@@ -37,7 +38,6 @@
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -52,10 +52,10 @@
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+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;
-import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration;
 import com.android.tools.r8.repackaging.Repackaging.RepackagingConfiguration;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -84,10 +84,12 @@
 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;
@@ -1319,21 +1321,68 @@
     // A mapping from references to the api-level introducing them.
     public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
     public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
-    public Map<TypeReference, AndroidApiLevel> typeApiMapping = new HashMap<>();
+    public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
+    public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
 
     public boolean enableApiCallerIdentification = false;
 
-    public void appendToApiLevelMap(
-        Map<DexReference, AndroidApiLevel> apiLevelMap, DexItemFactory factory) {
-      methodApiMapping.forEach(
-          (methodReference, apiLevel) ->
-              apiLevelMap.put(factory.createMethod(methodReference), apiLevel));
-      fieldApiMapping.forEach(
-          (fieldReference, apiLevel) ->
-              apiLevelMap.put(factory.createField(fieldReference), apiLevel));
-      typeApiMapping.forEach(
-          (typeReference, apiLevel) ->
-              apiLevelMap.put(factory.createType(typeReference.getDescriptor()), apiLevel));
+    public void visitMockedApiReferences(BiConsumer<ClassReference, AndroidApiClass> consumer) {
+      if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
+        return;
+      }
+      Set<ClassReference> classReferences = new HashSet<>(classApiMapping.keySet());
+      methodApiMapping
+          .keySet()
+          .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+      fieldApiMapping
+          .keySet()
+          .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+      classReferences.forEach(
+          classReference -> {
+            consumer.accept(
+                classReference,
+                new AndroidApiClass(classReference) {
+                  @Override
+                  public AndroidApiLevel getApiLevel() {
+                    return classApiMapping.getOrDefault(classReference, AndroidApiLevel.B);
+                  }
+
+                  @Override
+                  public int getMemberCount() {
+                    return 0;
+                  }
+
+                  @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;
+                  }
+                });
+          });
     }
   }
 
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 bc25cd4..9aa8f4d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -94,10 +94,12 @@
               .setClassDescriptor(getApiClassDescriptor(apiClass))
               .addMethodTransformer(getInitTransformer(apiClass))
               .addMethodTransformer(getApiLevelTransformer(apiClass))
+              .addMethodTransformer(getGetMemberCountTransformer(apiClass))
               .addMethodTransformer(getVisitFieldsTransformer(apiClass))
               .addMethodTransformer(getVisitMethodsTransformer(apiClass))
               .removeMethods(MethodPredicate.onName("placeHolderForInit"))
               .removeMethods(MethodPredicate.onName("placeHolderForGetApiLevel"))
+              .removeMethods(MethodPredicate.onName("placeHolderForGetMemberCount"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
               .transform());
@@ -170,6 +172,16 @@
         });
   }
 
+  // The transformer below changes AndroidApiDatabaseClassTemplate.getMemberCount from:
+  //     return placeHolderForGetMemberCount();
+  // into
+  //    return <memberCount>;
+  private static MethodTransformer getGetMemberCountTransformer(ParsedApiClass apiClass) {
+    return replaceCode(
+        "placeHolderForGetMemberCount",
+        transformer -> transformer.visitLdcInsn(apiClass.getMemberCount()));
+  }
+
   // The transformer below changes AndroidApiDatabaseClassTemplate.visitFields from:
   //     placeHolder();
   //     return TraversalContinuation.CONTINUE;
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 d85e3e5..6d46bfd 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -225,12 +225,7 @@
     ZipUtils.unzip(
         ToolHelper.DEPS.toString(),
         tempDeps.toFile(),
-        entry -> {
-          if (entry.getName().startsWith("com/android/tools/r8/apimodel/")) {
-            return false;
-          }
-          return true;
-        });
+        entry -> !entry.getName().startsWith("com/android/tools/r8/apimodel/"));
     Path modifiedDeps = Files.createTempFile("modified_deps", ".jar");
     ZipUtils.zip(modifiedDeps, tempDeps);
     return modifiedDeps;
@@ -242,6 +237,7 @@
         apiClass -> {
           expected.add(apiClass.getClassReference().getDescriptor());
           expected.add(apiClass.getApiLevel().getName());
+          expected.add(apiClass.getMemberCount() + "");
           BooleanBox added = new BooleanBox(false);
           apiClass.visitFieldReferences(
               (apiLevel, fieldReferences) -> {
@@ -292,6 +288,7 @@
             if (apiClass != null) {
               System.out.println(descriptor);
               System.out.println(apiClass.getApiLevel().getName());
+              System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
                   (reference, apiLevel) -> {
                     System.out.println(reference.getFieldType().getDescriptor());
@@ -322,6 +319,7 @@
             if (apiClass != null) {
               System.out.println(descriptor);
               System.out.println(apiClass.getApiLevel().getName());
+              System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
                   (reference, apiLevel) -> {
                     System.out.println(reference.getFieldType().getDescriptor());
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 2598898..faefd62 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
@@ -27,6 +27,12 @@
   }
 
   @Override
+  public int getMemberCount() {
+    // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
+    return placeHolderForGetMemberCount();
+  }
+
+  @Override
   public TraversalContinuation visitFields(
       BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
     // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
@@ -50,6 +56,10 @@
     return null;
   }
 
+  private static int placeHolderForGetMemberCount() {
+    return 0;
+  }
+
   private static void placeHolderForVisitFields() {}
 
   private static void placeHolderForVisitMethods() {}
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 c904dff..dbb71d3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -135,6 +135,11 @@
     private final TreeMap<AndroidApiLevel, List<FieldReference>> fieldReferences = new TreeMap<>();
     private final Map<AndroidApiLevel, List<MethodReference>> methodReferences = new TreeMap<>();
 
+    private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+      this.classReference = classReference;
+      this.apiLevel = apiLevel;
+    }
+
     public ClassReference getClassReference() {
       return classReference;
     }
@@ -143,9 +148,8 @@
       return apiLevel;
     }
 
-    private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
-      this.classReference = classReference;
-      this.apiLevel = apiLevel;
+    public int getMemberCount() {
+      return fieldReferences.size() + methodReferences.size();
     }
 
     private void register(FieldReference reference, AndroidApiLevel apiLevel) {
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 5d0c0b8..2f4e83c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
@@ -45,7 +45,7 @@
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertClassesMerged(A.class, B.class))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
-        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .compile()
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
index f6996e9..03b2f3e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
@@ -46,7 +46,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableNoHorizontalClassMergingAnnotations()
-        .apply(setMockApiLevelForType(ApiType.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(ApiType.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
         .addRunClasspathClasses(ApiType.class)
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 c7b2b1c..a1ecb6a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -4,15 +4,16 @@
 
 package com.android.tools.r8.apimodel;
 
+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;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
 
 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;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import org.junit.Test;
@@ -47,12 +48,11 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
-        .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForField(apiField, L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
-        .inspect(
-            verifyThat(parameters, apiCaller)
-                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+        .inspect(verifyThat(parameters, apiCaller).inlinedIntoFromApiLevel(apiCallerCaller, L_MR1))
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index 7d6230b..e1cf2e2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -4,16 +4,19 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
+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;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.NoVerticalClassMerging;
 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 org.junit.Test;
@@ -41,25 +44,36 @@
     Method apiCaller = ApiCaller.class.getDeclaredMethod("apiLevel22");
     Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
     testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
+        .addProgramClasses(ApiCaller.class, A.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoVerticalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(
+            addTracedApiReferenceLevelCallBack(
+                (method, apiLevel) -> {
+                  if (Reference.methodFromMethod(apiCaller).equals(method)) {
+                    if (parameters.isCfRuntime()) {
+                      assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+                    } else {
+                      assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+                    }
+                  }
+                }))
         .compile()
-        .inspect(
-            verifyThat(parameters, apiCaller)
-                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+        // We do not inline overrides calling super.
+        .inspect(verifyThat(parameters, apiCaller).notInlinedInto(apiCallerCaller))
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::apiLevel22", "Api::apiLevel22");
   }
 
-  @NoVerticalClassMerging
   public static class Api {
 
     void apiLevel22() {
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 d87ed82..f09f09d 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.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 
@@ -48,6 +49,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
index 80e213e..6cc4d9d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -44,7 +44,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 33d1deb..184536c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -11,14 +11,17 @@
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.CodeMatchers;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.function.BiConsumer;
 
 public abstract class ApiModelingTestHelper {
 
@@ -36,6 +39,23 @@
   }
 
   static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> setMockApiLevelForDefaultInstanceInitializer(
+          Class<?> clazz, AndroidApiLevel apiLevel) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options
+                .apiModelingOptions()
+                .methodApiMapping
+                .put(
+                    Reference.method(
+                        Reference.classFromClass(clazz), "<init>", ImmutableList.of(), null),
+                    apiLevel);
+          });
+    };
+  }
+
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
       ThrowableConsumer<T> setMockApiLevelForField(Field field, AndroidApiLevel apiLevel) {
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
@@ -48,14 +68,14 @@
     };
   }
 
-  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> ThrowableConsumer<T> setMockApiLevelForType(
-      Class<?> clazz, AndroidApiLevel apiLevel) {
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> setMockApiLevelForClass(Class<?> clazz, AndroidApiLevel apiLevel) {
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
           options -> {
             options
                 .apiModelingOptions()
-                .typeApiMapping
+                .classApiMapping
                 .put(Reference.classFromClass(clazz), apiLevel);
           });
     };
@@ -68,6 +88,17 @@
         });
   }
 
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
+          BiConsumer<MethodReference, AndroidApiLevel> consumer) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options.apiModelingOptions().tracedMethodApiLevelCallback = consumer;
+          });
+    };
+  }
+
   static ApiModelingMethodVerificationHelper verifyThat(TestParameters parameters, Method method) {
     return new ApiModelingMethodVerificationHelper(parameters, method);
   }
@@ -89,7 +120,7 @@
           : notInlinedInto(method);
     }
 
-    private ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
+    protected ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
       return inspector -> {
         MethodSubject candidate = inspector.method(methodOfInterest);
         assertThat(candidate, isPresent());
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
index 84abf49..6336e23 100644
--- a/third_party/android_jar/api-database.tar.gz.sha1
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -1 +1 @@
-e4da4b29079ac393e0012e7676dcca0799841e29
\ No newline at end of file
+be72aeca006f1aba8b1fe4d9c3ff4c0e76259960
\ No newline at end of file