Reland "[ApiModel] Use ComputedApiLevel instead of AndroidApiLevel"

This reverts commit ab84d34a0141d2cb892ab78d98c27d2450512a5a.

Bug: 207452750
Change-Id: I67dc36d1983bad82df910366b4ecf2d69f39c8b8
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 72c0cbb..722ae3e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -104,7 +104,6 @@
 import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
 import com.android.tools.r8.synthesis.SyntheticFinalization;
 import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.CollectionUtils;
@@ -880,10 +879,10 @@
     for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
       clazz.forEachProgramMember(
           member -> {
-            assert member.getDefinition().getApiLevel() != AndroidApiLevel.NOT_SET
+            assert !member.getDefinition().getApiLevel().isNotSetApiLevel()
                 : "Every member should have been analyzed";
             assert appView.options().apiModelingOptions().enableApiCallerIdentification
-                    || member.getDefinition().getApiLevel() == AndroidApiLevel.UNKNOWN
+                    || member.getDefinition().getApiLevel().isUnknownApiLevel()
                 : "Every member should have level UNKNOWN";
           });
     }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index 34a2d65..48d4d4f 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -4,67 +4,108 @@
 
 package com.android.tools.r8.androidapi;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel.KnownApiLevel;
 import com.android.tools.r8.graph.AppView;
 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.InternalOptions;
 
-public interface AndroidApiLevelCompute {
+public abstract class AndroidApiLevelCompute {
 
-  AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference);
+  private final KnownApiLevel[] knownApiLevelCache;
 
-  AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types);
-
-  default AndroidApiLevel computeApiLevelForDefinition(
-      DexMember<?, ?> reference, DexItemFactory factory) {
-    return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory));
+  public AndroidApiLevelCompute() {
+    knownApiLevelCache = new KnownApiLevel[AndroidApiLevel.LATEST.getLevel() + 1];
+    for (AndroidApiLevel value : AndroidApiLevel.values()) {
+      if (value != AndroidApiLevel.ANDROID_PLATFORM) {
+        knownApiLevelCache[value.getLevel()] = new KnownApiLevel(value);
+      }
+    }
   }
 
-  static AndroidApiLevelCompute create(AppView<?> appView) {
+  public KnownApiLevel of(AndroidApiLevel apiLevel) {
+    if (apiLevel == AndroidApiLevel.ANDROID_PLATFORM) {
+      return ComputedApiLevel.platform();
+    }
+    return knownApiLevelCache[apiLevel.getLevel()];
+  }
+
+  public abstract ComputedApiLevel computeApiLevelForLibraryReference(
+      DexReference reference, ComputedApiLevel unknownValue);
+
+  public abstract ComputedApiLevel computeApiLevelForDefinition(
+      Iterable<DexType> types, ComputedApiLevel unknownValue);
+
+  public ComputedApiLevel computeApiLevelForDefinition(
+      DexMember<?, ?> reference, DexItemFactory factory, ComputedApiLevel unknownValue) {
+    return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory), unknownValue);
+  }
+
+  public static AndroidApiLevelCompute create(AppView<?> appView) {
     return appView.options().apiModelingOptions().enableApiCallerIdentification
         ? new DefaultAndroidApiLevelCompute(appView)
         : new NoAndroidApiLevelCompute();
   }
 
-  class NoAndroidApiLevelCompute implements AndroidApiLevelCompute {
-
-    @Override
-    public AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
-      return UNKNOWN;
-    }
-
-    @Override
-    public AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference) {
-      return UNKNOWN;
+  public static ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
+    if (options.apiModelingOptions().enableApiCallerIdentification) {
+      return options.getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM
+          ? ComputedApiLevel.platform()
+          : new KnownApiLevel(options.getMinApiLevel());
+    } else {
+      return ComputedApiLevel.unknown();
     }
   }
 
-  class DefaultAndroidApiLevelCompute implements AndroidApiLevelCompute {
+  public ComputedApiLevel getPlatformApiLevelOrUnknown(AppView<?> appView) {
+    if (appView.options().getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM) {
+      return ComputedApiLevel.platform();
+    }
+    return ComputedApiLevel.unknown();
+  }
 
-    private final AndroidApiReferenceLevelCache cache;
-    private final AndroidApiLevel minApiLevel;
+  public static class NoAndroidApiLevelCompute extends AndroidApiLevelCompute {
 
-    public DefaultAndroidApiLevelCompute(AppView<?> appView) {
-      this.cache = AndroidApiReferenceLevelCache.create(appView);
-      this.minApiLevel = appView.options().getMinApiLevel();
+    @Override
+    public ComputedApiLevel computeApiLevelForDefinition(
+        Iterable<DexType> types, ComputedApiLevel unknownValue) {
+      return unknownValue;
     }
 
     @Override
-    public AndroidApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
-      AndroidApiLevel computedLevel = minApiLevel;
+    public ComputedApiLevel computeApiLevelForLibraryReference(
+        DexReference reference, ComputedApiLevel unknownValue) {
+      return unknownValue;
+    }
+  }
+
+  public static class DefaultAndroidApiLevelCompute extends AndroidApiLevelCompute {
+
+    private final AndroidApiReferenceLevelCache cache;
+    private final ComputedApiLevel minApiLevel;
+
+    public DefaultAndroidApiLevelCompute(AppView<?> appView) {
+      this.cache = AndroidApiReferenceLevelCache.create(appView, this);
+      this.minApiLevel = of(appView.options().getMinApiLevel());
+    }
+
+    @Override
+    public ComputedApiLevel computeApiLevelForDefinition(
+        Iterable<DexType> types, ComputedApiLevel unknownValue) {
+      ComputedApiLevel computedLevel = minApiLevel;
       for (DexType type : types) {
-        computedLevel = cache.lookupMax(type, computedLevel);
+        computedLevel = cache.lookupMax(type, computedLevel, unknownValue);
       }
       return computedLevel;
     }
 
     @Override
-    public AndroidApiLevel computeApiLevelForLibraryReference(DexReference reference) {
-      return cache.lookup(reference);
+    public ComputedApiLevel computeApiLevelForLibraryReference(
+        DexReference reference, ComputedApiLevel unknownValue) {
+      return cache.lookup(reference, unknownValue);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index 551085a..bedc254 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -4,12 +4,10 @@
 
 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 static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
 
 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;
@@ -41,25 +39,23 @@
       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;
+      List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
     loadData();
     predefinedApiTypeLookup.forEach(
         apiClass -> {
           DexType type = apiClass.getType();
-          lookupNonAmbiguousCache.put(type.hashCode(), NOT_SET);
+          lookupNonAmbiguousCache.put(type.hashCode(), null);
           ambiguousCache.put(type, apiClass.getApiLevel());
           apiClass.visitMethodsWithApiLevels(
               (method, apiLevel) -> {
-                lookupNonAmbiguousCache.put(method.hashCode(), NOT_SET);
+                lookupNonAmbiguousCache.put(method.hashCode(), null);
                 ambiguousCache.put(method, apiLevel);
               });
           apiClass.visitFieldsWithApiLevels(
               (field, apiLevel) -> {
-                lookupNonAmbiguousCache.put(field.hashCode(), NOT_SET);
+                lookupNonAmbiguousCache.put(field.hashCode(), null);
                 ambiguousCache.put(field, apiLevel);
               });
         });
@@ -96,7 +92,7 @@
     for (int i = 0; i < hashIndices.length; i++) {
       byte apiLevel = apiLevels[i];
       lookupNonAmbiguousCache.put(
-          hashIndices[i], apiLevel == -1 ? NOT_SET : AndroidApiLevel.getAndroidApiLevel(apiLevel));
+          hashIndices[i], apiLevel == -1 ? null : AndroidApiLevel.getAndroidApiLevel(apiLevel));
     }
     ambiguous.forEach(this::parseAmbiguous);
   }
@@ -130,9 +126,12 @@
   }
 
   private AndroidApiLevel lookupApiLevel(DexReference reference) {
-    AndroidApiLevel result = lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), UNKNOWN);
-    if (result != NOT_SET) {
-      return result;
+    // We use Android platform to track if an element is unknown since no occurrences of that api
+    // level exists in the database.
+    AndroidApiLevel result =
+        lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), ANDROID_PLATFORM);
+    if (result != null) {
+      return result == ANDROID_PLATFORM ? null : result;
     }
     return ambiguousCache.computeIfAbsent(
         reference,
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 3cb2b7d..4e17f60 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -17,87 +17,89 @@
 public class AndroidApiReferenceLevelCache {
 
   private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+  private final AndroidApiLevelCompute apiLevelCompute;
   private final AndroidApiLevelDatabase androidApiLevelDatabase;
   private final AppView<?> appView;
   private final DexItemFactory factory;
 
-  private AndroidApiReferenceLevelCache(AppView<?> appView) {
-    this(appView, ImmutableList.of());
-  }
-
   private AndroidApiReferenceLevelCache(
-      AppView<?> appView, List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
+      AppView<?> appView,
+      AndroidApiLevelCompute apiLevelCompute,
+      List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
     this.appView = appView;
+    this.apiLevelCompute = apiLevelCompute;
     factory = appView.dexItemFactory();
     androidApiLevelDatabase =
-        new AndroidApiLevelHashingDatabaseImpl(factory, predefinedApiTypeLookupForHashing);
+        new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
     desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration;
   }
 
-  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;
-        }
-      };
-    }
+  public static AndroidApiReferenceLevelCache create(
+      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+    assert appView.options().apiModelingOptions().enableApiCallerIdentification;
     ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder();
     appView
         .options()
         .apiModelingOptions()
         .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add);
-    return new AndroidApiReferenceLevelCache(appView, builder.build());
+    return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
   }
 
-  public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
-    return lookup(reference).max(minApiLevel);
+  public ComputedApiLevel lookupMax(
+      DexReference reference, ComputedApiLevel minApiLevel, ComputedApiLevel unknownValue) {
+    assert !minApiLevel.isNotSetApiLevel();
+    return lookup(reference, unknownValue).max(minApiLevel);
   }
 
-  public AndroidApiLevel lookup(DexReference reference) {
+  public ComputedApiLevel lookup(DexReference reference, ComputedApiLevel unknownValue) {
     DexType contextType = reference.getContextType();
     if (contextType.isArrayType()) {
       if (reference.isDexMethod() && reference.asDexMethod().match(factory.objectMembers.clone)) {
-        return appView.options().getMinApiLevel();
+        return appView.computedMinApiLevel();
       }
-      return lookup(contextType.toBaseType(factory));
+      return lookup(contextType.toBaseType(factory), unknownValue);
     }
     if (contextType.isPrimitiveType() || contextType.isVoidType()) {
-      return appView.options().getMinApiLevel();
+      return appView.computedMinApiLevel();
     }
     DexClass clazz = appView.definitionFor(contextType);
     if (clazz == null) {
-      return AndroidApiLevel.UNKNOWN;
+      return unknownValue;
     }
     if (!clazz.isLibraryClass()) {
-      return appView.options().getMinApiLevel();
+      return appView.computedMinApiLevel();
     }
     if (reference.getContextType() == factory.objectType) {
-      return appView.options().getMinApiLevel();
+      return appView.computedMinApiLevel();
     }
     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.
-      return appView.options().getMinApiLevel();
+      return appView.computedMinApiLevel();
     }
-    if (reference.isDexMethod() && factory.objectMembers.isObjectMember(reference.asDexMethod())) {
+    if (reference.isDexMethod()
+        && !reference.asDexMethod().isInstanceInitializer(factory)
+        && factory.objectMembers.isObjectMember(reference.asDexMethod())) {
       // If we can lookup the method it was introduced/overwritten later. Take for example
-      // a default constructor that was not available before som api level. If unknown we default
+      // a default toString that was not available before some api level. If unknown we default
       // back to the static holder.
       AndroidApiLevel methodApiLevel =
           androidApiLevelDatabase.getMethodApiLevel(reference.asDexMethod());
-      return methodApiLevel == AndroidApiLevel.UNKNOWN
-          ? androidApiLevelDatabase.getTypeApiLevel(reference.getContextType())
-          : methodApiLevel;
+      if (methodApiLevel != null) {
+        return apiLevelCompute.of(methodApiLevel);
+      }
+      AndroidApiLevel typeApiLevel =
+          androidApiLevelDatabase.getTypeApiLevel(reference.getContextType());
+      // TODO(b/207452750): Investigate if we can return minApi here.
+      return typeApiLevel == null ? ComputedApiLevel.unknown() : apiLevelCompute.of(typeApiLevel);
     }
-    return reference
-        .apply(
+    AndroidApiLevel foundApiLevel =
+        reference.apply(
             androidApiLevelDatabase::getTypeApiLevel,
             androidApiLevelDatabase::getFieldApiLevel,
-            androidApiLevelDatabase::getMethodApiLevel)
-        .max(appView.options().getMinApiLevel());
+            androidApiLevelDatabase::getMethodApiLevel);
+    return (foundApiLevel == null)
+        ? unknownValue
+        : apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
new file mode 100644
index 0000000..2f5ab5e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
@@ -0,0 +1,165 @@
+// 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.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.structural.Equatable;
+import java.util.Objects;
+
+/**
+ * The ComputedApiLevel encodes a lattice over AndroidApiLevel with a bottom (NotSet) and a top
+ * (Unknown).
+ */
+public interface ComputedApiLevel extends Equatable<ComputedApiLevel> {
+
+  static NotSetApiLevel notSet() {
+    return NotSetApiLevel.INSTANCE;
+  }
+
+  static UnknownApiLevel unknown() {
+    return UnknownApiLevel.INSTANCE;
+  }
+
+  static KnownApiLevel platform() {
+    return KnownApiLevel.PLATFORM_INSTANCE;
+  }
+
+  default boolean isNotSetApiLevel() {
+    return false;
+  }
+
+  default boolean isUnknownApiLevel() {
+    return false;
+  }
+
+  default ComputedApiLevel max(ComputedApiLevel other) {
+    return isGreaterThanOrEqualTo(other) ? this : other;
+  }
+
+  default boolean isGreaterThanOrEqualTo(ComputedApiLevel other) {
+    assert !isNotSetApiLevel() && !other.isNotSetApiLevel()
+        : "Cannot compute relationship for not set";
+    if (isUnknownApiLevel()) {
+      return true;
+    }
+    if (other.isUnknownApiLevel()) {
+      return false;
+    }
+    assert isKnownApiLevel() && other.isKnownApiLevel();
+    return asKnownApiLevel()
+        .getApiLevel()
+        .isGreaterThanOrEqualTo(other.asKnownApiLevel().getApiLevel());
+  }
+
+  default boolean isKnownApiLevel() {
+    return false;
+  }
+
+  default KnownApiLevel asKnownApiLevel() {
+    return null;
+  }
+
+  @Override
+  default boolean isEqualTo(ComputedApiLevel other) {
+    return this.equals(other);
+  }
+
+  class NotSetApiLevel implements ComputedApiLevel {
+
+    private static final NotSetApiLevel INSTANCE = new NotSetApiLevel();
+
+    private NotSetApiLevel() {}
+
+    @Override
+    public boolean isNotSetApiLevel() {
+      return true;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+  }
+
+  class UnknownApiLevel implements ComputedApiLevel {
+
+    private static final UnknownApiLevel INSTANCE = new UnknownApiLevel();
+
+    private UnknownApiLevel() {}
+
+    @Override
+    public boolean isUnknownApiLevel() {
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return "UNKNOWN";
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+  }
+
+  class KnownApiLevel implements ComputedApiLevel {
+
+    private static final KnownApiLevel PLATFORM_INSTANCE =
+        new KnownApiLevel(AndroidApiLevel.ANDROID_PLATFORM);
+
+    private final AndroidApiLevel apiLevel;
+
+    KnownApiLevel(AndroidApiLevel apiLevel) {
+      this.apiLevel = apiLevel;
+    }
+
+    public AndroidApiLevel getApiLevel() {
+      return apiLevel;
+    }
+
+    @Override
+    public boolean isKnownApiLevel() {
+      return true;
+    }
+
+    @Override
+    public KnownApiLevel asKnownApiLevel() {
+      return this;
+    }
+
+    @Override
+    public String toString() {
+      return apiLevel.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof KnownApiLevel)) {
+        return false;
+      }
+      KnownApiLevel that = (KnownApiLevel) o;
+      return apiLevel == that.apiLevel;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(apiLevel);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 32e1f64..e21fbf4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.contexts.CompilationContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
@@ -116,6 +118,8 @@
 
   private final Thread mainThread = Thread.currentThread();
 
+  private final ComputedApiLevel computedMinApiLevel;
+
   private AppView(
       T appInfo,
       WholeProgramOptimizations wholeProgramOptimizations,
@@ -137,6 +141,8 @@
     this.libraryMethodSideEffectModelCollection = new LibraryMethodSideEffectModelCollection(this);
     this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
     this.protoShrinker = ProtoShrinker.create(withLiveness());
+
+    this.computedMinApiLevel = AndroidApiLevelCompute.computeInitialMinApiLevel(appInfo.options());
   }
 
   public boolean verifyMainThread() {
@@ -827,4 +833,8 @@
   public boolean checkForTesting(Supplier<Boolean> test) {
     return testing().enableTestAssertions ? test.get() : true;
   }
+
+  public ComputedApiLevel computedMinApiLevel() {
+    return computedMinApiLevel;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 56a302d..44a5132 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -21,7 +22,6 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
@@ -59,7 +59,7 @@
       FieldTypeSignature genericSignature,
       DexAnnotationSet annotations,
       DexValue staticValue,
-      AndroidApiLevel apiLevel,
+      ComputedApiLevel apiLevel,
       boolean deprecated,
       boolean d8R8Synthesized) {
     super(field, annotations, d8R8Synthesized, apiLevel);
@@ -103,7 +103,7 @@
   }
 
   @Override
-  public AndroidApiLevel getApiLevel() {
+  public ComputedApiLevel getApiLevel() {
     return getApiLevelForDefinition();
   }
 
@@ -365,7 +365,7 @@
     private FieldAccessFlags accessFlags;
     private FieldTypeSignature genericSignature = FieldTypeSignature.noSignature();
     private DexValue staticValue = null;
-    private AndroidApiLevel apiLevel = AndroidApiLevel.NOT_SET;
+    private ComputedApiLevel apiLevel = ComputedApiLevel.notSet();
     private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
     private boolean deprecated;
     private final boolean d8R8Synthesized;
@@ -438,7 +438,7 @@
       return this;
     }
 
-    public Builder setApiLevel(AndroidApiLevel apiLevel) {
+    public Builder setApiLevel(ComputedApiLevel apiLevel) {
       this.apiLevel = apiLevel;
       return this;
     }
@@ -474,7 +474,7 @@
       assert accessFlags != null;
       assert genericSignature != null;
       assert annotations != null;
-      assert !checkAndroidApiLevel || apiLevel != AndroidApiLevel.NOT_SET;
+      assert !checkAndroidApiLevel || !apiLevel.isNotSetApiLevel();
       DexEncodedField dexEncodedField =
           new DexEncodedField(
               field,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index a5ea49e..7f42e49 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -3,11 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -23,7 +21,7 @@
   private final boolean d8R8Synthesized;
 
   /** apiLevelForDefinition describes the api level needed for knowing all types */
-  private AndroidApiLevel apiLevelForDefinition;
+  private ComputedApiLevel apiLevelForDefinition;
 
   private final R reference;
 
@@ -31,7 +29,7 @@
       R reference,
       DexAnnotationSet annotations,
       boolean d8R8Synthesized,
-      AndroidApiLevel apiLevelForDefinition) {
+      ComputedApiLevel apiLevelForDefinition) {
     super(annotations);
     this.reference = reference;
     this.d8R8Synthesized = d8R8Synthesized;
@@ -95,18 +93,18 @@
 
   public abstract MemberOptimizationInfo<?> getOptimizationInfo();
 
-  public abstract AndroidApiLevel getApiLevel();
+  public abstract ComputedApiLevel getApiLevel();
 
-  public AndroidApiLevel getApiLevelForDefinition() {
+  public ComputedApiLevel getApiLevelForDefinition() {
     return apiLevelForDefinition;
   }
 
-  public void setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+  public void setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
     this.apiLevelForDefinition = apiLevelForDefinition;
   }
 
   public boolean hasComputedApiReferenceLevel() {
-    return getApiLevel() != NOT_SET;
+    return !getApiLevel().isNotSetApiLevel();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index ccf7d83..a8709ec 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -11,10 +11,10 @@
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 import static java.util.Objects.requireNonNull;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfConstNumber;
 import com.android.tools.r8.cf.code.CfConstString;
@@ -71,7 +71,6 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -148,8 +147,8 @@
           ParameterAnnotationsList.empty(),
           null,
           false,
-          NOT_SET,
-          NOT_SET,
+          ComputedApiLevel.notSet(),
+          ComputedApiLevel.notSet(),
           null,
           CallSiteOptimizationInfo.top(),
           DefaultMethodOptimizationInfo.getInstance(),
@@ -168,7 +167,7 @@
   private CallSiteOptimizationInfo callSiteOptimizationInfo;
   private CfVersion classFileVersion;
   /** The apiLevelForCode describes the api level needed for knowing all references in the code */
-  private AndroidApiLevel apiLevelForCode;
+  private ComputedApiLevel apiLevelForCode;
 
   private KotlinMethodLevelInfo kotlinMemberInfo = getNoKotlinInfo();
   /** Generic signature information if the attribute is present in the input */
@@ -242,8 +241,8 @@
       ParameterAnnotationsList parameterAnnotationsList,
       Code code,
       boolean d8R8Synthesized,
-      AndroidApiLevel apiLevelForDefinition,
-      AndroidApiLevel apiLevelForCode,
+      ComputedApiLevel apiLevelForDefinition,
+      ComputedApiLevel apiLevelForCode,
       CfVersion classFileVersion,
       CallSiteOptimizationInfo callSiteOptimizationInfo,
       MethodOptimizationInfo optimizationInfo,
@@ -1232,7 +1231,7 @@
             .fixupOptimizationInfo(appView, prototypeChanges.createMethodOptimizationInfoFixer())
             .setGenericSignature(MethodTypeSignature.noSignature());
     DexEncodedMethod method = builder.build();
-    method.copyMetadata(this);
+    method.copyMetadata(appView, this);
     setObsolete();
     return method;
   }
@@ -1268,23 +1267,23 @@
     return optimizationInfo;
   }
 
-  public AndroidApiLevel getApiLevelForCode() {
+  public ComputedApiLevel getApiLevelForCode() {
     return apiLevelForCode;
   }
 
-  public void clearApiLevelForCode(AppView<?> appView) {
-    this.apiLevelForCode = AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView);
+  public void clearApiLevelForCode() {
+    this.apiLevelForCode = ComputedApiLevel.notSet();
   }
 
-  public void setApiLevelForCode(AndroidApiLevel apiLevel) {
+  public void setApiLevelForCode(ComputedApiLevel apiLevel) {
     assert apiLevel != null;
     this.apiLevelForCode = apiLevel;
   }
 
   @Override
-  public AndroidApiLevel getApiLevel() {
-    return (shouldNotHaveCode() ? AndroidApiLevel.B : getApiLevelForCode())
-        .max(getApiLevelForDefinition());
+  public ComputedApiLevel getApiLevel() {
+    ComputedApiLevel apiLevelForDefinition = getApiLevelForDefinition();
+    return shouldNotHaveCode() ? apiLevelForDefinition : apiLevelForDefinition.max(apiLevelForCode);
   }
 
   public synchronized MutableMethodOptimizationInfo getMutableOptimizationInfo() {
@@ -1320,12 +1319,14 @@
     this.callSiteOptimizationInfo = callSiteOptimizationInfo;
   }
 
-  public void copyMetadata(DexEncodedMethod from) {
+  public void copyMetadata(AppView<?> appView, DexEncodedMethod from) {
     checkIfObsolete();
     if (from.hasClassFileVersion()) {
       upgradeClassFileVersion(from.getClassFileVersion());
     }
-    apiLevelForCode = getApiLevelForCode().max(from.getApiLevelForCode());
+    if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      apiLevelForCode = getApiLevelForCode().max(from.getApiLevelForCode());
+    }
   }
 
   public MethodTypeSignature getGenericSignature() {
@@ -1374,8 +1375,8 @@
     private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
     private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
     private CfVersion classFileVersion = null;
-    private AndroidApiLevel apiLevelForDefinition = NOT_SET;
-    private AndroidApiLevel apiLevelForCode = NOT_SET;
+    private ComputedApiLevel apiLevelForDefinition = ComputedApiLevel.notSet();
+    private ComputedApiLevel apiLevelForCode = ComputedApiLevel.notSet();
     private final boolean d8R8Synthesized;
     private boolean deprecated = false;
 
@@ -1611,12 +1612,12 @@
       return this;
     }
 
-    public Builder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+    public Builder setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
       this.apiLevelForDefinition = apiLevelForDefinition;
       return this;
     }
 
-    public Builder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
+    public Builder setApiLevelForCode(ComputedApiLevel apiLevelForCode) {
       this.apiLevelForCode = apiLevelForCode;
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index e297bd4..900e79c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -4,11 +4,12 @@
 package com.android.tools.r8.graph;
 
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
@@ -21,7 +22,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.SyntheticMarker;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.android.tools.r8.utils.structural.StructuralItem;
@@ -34,7 +34,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -823,26 +822,19 @@
     return checksumSupplier;
   }
 
-  public AndroidApiLevel getApiReferenceLevel(
-      AppView<?> appView,
-      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelLookup) {
+  public ComputedApiLevel getApiReferenceLevel(
+      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
     // The api level of a class is the max level of it's members, super class and interfaces.
-    AndroidApiLevel computedApiLevel = minApiLevelIfEnabledOrUnknown(appView);
-    for (DexType superType : allImmediateSupertypes()) {
-      computedApiLevel = apiLevelLookup.apply(superType, computedApiLevel);
-      if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
-        return AndroidApiLevel.UNKNOWN;
-      }
-    }
-    return computedApiLevel.max(getMembersApiReferenceLevel(appView));
+    return getMembersApiReferenceLevel(
+        apiLevelCompute.computeApiLevelForDefinition(
+            allImmediateSupertypes(), apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
   }
 
-  public AndroidApiLevel getMembersApiReferenceLevel(AppView<?> appView) {
-    AndroidApiLevel memberLevel = minApiLevelIfEnabledOrUnknown(appView);
+  public ComputedApiLevel getMembersApiReferenceLevel(ComputedApiLevel memberLevel) {
     for (DexEncodedMember<?, ?> member : members()) {
       memberLevel = memberLevel.max(member.getApiLevel());
-      if (memberLevel == AndroidApiLevel.UNKNOWN) {
-        return AndroidApiLevel.UNKNOWN;
+      if (memberLevel.isUnknownApiLevel()) {
+        return memberLevel;
       }
     }
     return memberLevel;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 844b45e..cf866d4 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
 
 /** Type representing a method definition in the programs compilation unit and its holder. */
 public final class ProgramMethod extends DexClassAndMethod
@@ -95,7 +94,7 @@
       accessFlags.demoteFromStrict();
       accessFlags.demoteFromSynchronized();
       accessFlags.promoteToAbstract();
-      getDefinition().clearApiLevelForCode(appView);
+      getDefinition().clearApiLevelForCode();
       getDefinition().unsetCode();
       getSimpleFeedback().unsetOptimizationInfoForAbstractMethod(this);
     }
@@ -105,7 +104,7 @@
   public void convertToThrowNullMethod(AppView<?> appView) {
     MethodAccessFlags accessFlags = getAccessFlags();
     accessFlags.demoteFromAbstract();
-    getDefinition().setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView));
+    getDefinition().setApiLevelForCode(appView.computedMinApiLevel());
     getDefinition().setCode(ThrowNullCode.get(), appView);
     getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS);
     getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this);
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 7f4b9f9..45e2065 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
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph.analysis;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -13,18 +14,17 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
-import com.android.tools.r8.utils.AndroidApiLevel;
 
 public class ApiModelAnalysis extends EnqueuerAnalysis {
 
   private final AppView<?> appView;
-  private final AndroidApiLevel minApiLevel;
   private final AndroidApiLevelCompute apiCompute;
+  private final ComputedApiLevel minApiLevel;
 
   public ApiModelAnalysis(AppView<?> appView, AndroidApiLevelCompute apiCompute) {
     this.appView = appView;
-    this.minApiLevel = appView.options().getMinApiLevel();
     this.apiCompute = apiCompute;
+    this.minApiLevel = appView.computedMinApiLevel();
   }
 
   @Override
@@ -73,7 +73,7 @@
   @Override
   public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {
     // We may not trace into failed resolution targets.
-    method.setApiLevelForCode(AndroidApiLevel.UNKNOWN);
+    method.setApiLevelForCode(ComputedApiLevel.unknown());
   }
 
   private void computeAndSetApiLevelForDefinition(DexClassAndMember<?, ?> member) {
@@ -81,6 +81,8 @@
         .getDefinition()
         .setApiLevelForDefinition(
             apiCompute.computeApiLevelForDefinition(
-                member.getReference(), appView.dexItemFactory()));
+                member.getReference(),
+                appView.dexItemFactory(),
+                apiCompute.getPlatformApiLevelOrUnknown(appView)));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index b1055ef..c76f43f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static com.google.common.base.Predicates.not;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -29,7 +29,6 @@
 import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -125,7 +124,7 @@
         newMethodReference.withName("$r8$clinit$synthetic", dexItemFactory);
     lensBuilder.recordNewMethodSignature(syntheticMethodReference, newMethodReference, true);
 
-    AndroidApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView);
+    ComputedApiLevel apiReferenceLevel = classInitializerMerger.getApiReferenceLevel(appView);
     DexEncodedMethod definition =
         DexEncodedMethod.syntheticBuilder()
             .setMethod(newMethodReference)
@@ -215,7 +214,7 @@
         DexEncodedField.syntheticBuilder()
             .setField(group.getClassIdField())
             .setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
-            .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+            .setApiLevel(appView.computedMinApiLevel())
             .build();
 
     // For the $r8$classId synthesized fields, we try to over-approximate the set of values it may
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
index 085276f..b1219c4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.horizontalclassmerging.code;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static java.lang.Integer.max;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -36,7 +36,6 @@
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.CfVersionUtils;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
@@ -101,11 +100,11 @@
     return null;
   }
 
-  public AndroidApiLevel getApiReferenceLevel(AppView<?> appView) {
+  public ComputedApiLevel getApiReferenceLevel(AppView<?> appView) {
     assert !classInitializers.isEmpty();
     return ListUtils.fold(
         classInitializers,
-        minApiLevelIfEnabledOrUnknown(appView),
+        appView.computedMinApiLevel(),
         (accApiLevel, method) -> accApiLevel.max(method.getDefinition().getApiLevel()));
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
index 3b7e1e0..1f953f4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -4,21 +4,21 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
-import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.utils.AndroidApiLevel;
 
-public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<AndroidApiLevel> {
+public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<ComputedApiLevel> {
 
-  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+  private final AndroidApiLevelCompute apiLevelCompute;
   private final AppView<?> appView;
   // TODO(b/188388130): Remove when stabilized.
   private final boolean enableApiCallerIdentification;
 
   public NoDifferentApiReferenceLevel(AppView<?> appView) {
-    apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+    apiLevelCompute = AndroidApiLevelCompute.create(appView);
     this.appView = appView;
     enableApiCallerIdentification =
         appView.options().apiModelingOptions().enableApiCallerIdentification;
@@ -35,8 +35,8 @@
   }
 
   @Override
-  public AndroidApiLevel getMergeKey(DexProgramClass clazz) {
+  public ComputedApiLevel getMergeKey(DexProgramClass clazz) {
     assert enableApiCallerIdentification;
-    return clazz.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax);
+    return clazz.getApiReferenceLevel(appView, apiLevelCompute);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 2b72d1e..37ce381 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -205,7 +205,6 @@
             .map(prefix -> "L" + DescriptorUtils.getPackageBinaryNameFromJavaType(prefix))
             .map(options.itemFactory::createString)
             .collect(Collectors.toList());
-    AndroidApiLevelCompute apiLevelCompute = AndroidApiLevelCompute.create(appView);
     if (options.isDesugaredLibraryCompilation()) {
       // Specific L8 Settings, performs all desugaring including L8 specific desugaring.
       //
@@ -223,8 +222,7 @@
       // - nest based access desugaring,
       // - invoke-special desugaring.
       assert options.desugarState.isOn();
-      this.instructionDesugaring =
-          CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+      this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
       this.covariantReturnTypeAnnotationTransformer = null;
       this.dynamicTypeOptimization = null;
       this.classInliner = null;
@@ -249,7 +247,7 @@
     this.instructionDesugaring =
         appView.enableWholeProgramOptimizations()
             ? CfInstructionDesugaringCollection.empty()
-            : CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+            : CfInstructionDesugaringCollection.create(appView);
     this.covariantReturnTypeAnnotationTransformer =
         options.processCovariantReturnTypeAnnotations
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -288,7 +286,8 @@
       this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
       this.serviceLoaderRewriter =
           options.enableServiceLoaderRewriting
-              ? new ServiceLoaderRewriter(appViewWithLiveness, apiLevelCompute)
+              ? new ServiceLoaderRewriter(
+                  appViewWithLiveness, AndroidApiLevelCompute.create(appView))
               : null;
       this.enumValueOptimizer =
           options.enableEnumValueOptimization ? new EnumValueOptimizer(appViewWithLiveness) : null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index c516da4..ed1260e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -1470,8 +1468,7 @@
               appView,
               builder ->
                   builder
-                      .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                      .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                      .disableAndroidApiLevelCheck()
                       .setProto(getProto(appView.dexItemFactory()))
                       .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                       .setCode(methodSig -> generateTemplateMethod(appView.options(), methodSig)));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 8328e9b..b34a515 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
@@ -28,19 +27,16 @@
  */
 public abstract class CfInstructionDesugaringCollection {
 
-  public static CfInstructionDesugaringCollection create(
-      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+  public static CfInstructionDesugaringCollection create(AppView<?> appView) {
     if (appView.options().desugarState.isOn()) {
-      return new NonEmptyCfInstructionDesugaringCollection(appView, apiLevelCompute);
+      return new NonEmptyCfInstructionDesugaringCollection(appView);
     }
     // TODO(b/145775365): invoke-special desugaring is mandatory, since we currently can't map
     //  invoke-special instructions that require desugaring into IR.
     if (appView.options().isGeneratingClassFiles()) {
-      return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(
-          appView, apiLevelCompute);
+      return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(appView);
     }
-    return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(
-        appView, apiLevelCompute);
+    return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(appView);
   }
 
   public static CfInstructionDesugaringCollection empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cd17565..a014283 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -645,7 +645,7 @@
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
-                    newMethod.copyMetadata(encodedMethod);
+                    newMethod.copyMetadata(appView, encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
                         encodedMethod.getReference(), callTarget);
 
@@ -736,7 +736,7 @@
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
-                    newMethod.copyMetadata(encodedMethod);
+                    newMethod.copyMetadata(appView, encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
                         encodedMethod.getReference(), callTarget);
                     return newMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 66d3a56..bc5c0fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.errors.Unreachable;
@@ -52,12 +51,9 @@
   private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
   private final InterfaceMethodRewriter interfaceMethodRewriter;
   private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
-  private final AndroidApiLevelCompute apiLevelCompute;
 
-  NonEmptyCfInstructionDesugaringCollection(
-      AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
+  NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
     this.appView = appView;
-    this.apiLevelCompute = apiLevelCompute;
     AlwaysThrowingInstructionDesugaring alwaysThrowingInstructionDesugaring =
         appView.enableWholeProgramOptimizations()
             ? new AlwaysThrowingInstructionDesugaring(appView.withClassHierarchy())
@@ -131,24 +127,22 @@
     }
   }
 
-  static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(
-      AppView<?> appView, AndroidApiLevelCompute computeApiLevel) {
+  static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
     assert appView.options().desugarState.isOff();
     assert appView.options().isGeneratingClassFiles();
     NonEmptyCfInstructionDesugaringCollection desugaringCollection =
-        new NonEmptyCfInstructionDesugaringCollection(appView, computeApiLevel);
+        new NonEmptyCfInstructionDesugaringCollection(appView);
     // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
     //  This should be removed once we can represent invoke-special instructions in the IR.
     desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
     return desugaringCollection;
   }
 
-  static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(
-      AppView<?> appView, AndroidApiLevelCompute computeApiLevel) {
+  static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(AppView<?> appView) {
     assert appView.options().desugarState.isOff();
     assert appView.options().isGeneratingDex();
     NonEmptyCfInstructionDesugaringCollection desugaringCollection =
-        new NonEmptyCfInstructionDesugaringCollection(appView, computeApiLevel);
+        new NonEmptyCfInstructionDesugaringCollection(appView);
     desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
     desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
     return desugaringCollection;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index 3f9e078..fb848df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.CACHE_CONSTANT;
 import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_ICCE;
 import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_NSME;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static org.objectweb.asm.Opcodes.INVOKESTATIC;
 
 import com.android.tools.r8.cf.code.CfCheckCast;
@@ -56,7 +55,6 @@
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
 import com.google.common.collect.ImmutableList;
@@ -194,12 +192,12 @@
             DexEncodedField.syntheticBuilder()
                 .setField(this.initializedValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
-                .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+                .disableAndroidApiLevelCheck()
                 .build(),
             DexEncodedField.syntheticBuilder()
                 .setField(this.constantValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
-                .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+                .disableAndroidApiLevelCheck()
                 .build()));
   }
 
@@ -210,8 +208,7 @@
                 .setMethod(getConstMethod)
                 .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                 .setCode(generateGetterCode(builder))
-                .setApiLevelForDefinition(AndroidApiLevel.S)
-                .setApiLevelForCode(AndroidApiLevel.S)
+                .disableAndroidApiLevelCheck()
                 .build()));
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 5d9e05e..14240a6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -4,8 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -595,7 +594,8 @@
         .setAccessFlags(newFlags)
         .setCode(code)
         .setApiLevelForDefinition(template.getApiLevelForDefinition())
-        .setApiLevelForCode(code == null ? NOT_SET : template.getApiLevelForCode())
+        .setApiLevelForCode(
+            code == null ? ComputedApiLevel.notSet() : template.getApiLevelForCode())
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index e1462ea..36519f8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfStackInstruction;
@@ -802,8 +800,7 @@
             .setAccessFlags(accessFlags)
             .setCode(
                 createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory))
-            .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-            .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+            .disableAndroidApiLevelCheck()
             .build();
     addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 674493f..510331b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1088,7 +1088,7 @@
             context.getDefinition().accessFlags.demoteFromSynthetic();
           }
 
-          context.getDefinition().copyMetadata(singleTargetMethod);
+          context.getDefinition().copyMetadata(appView, singleTargetMethod);
 
           if (inlineeMayHaveInvokeMethod && options.applyInliningToInlinee) {
             if (inlineeStack.size() + 1 > options.applyInliningToInlineeMaxDepth
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index d73ac60..7ef7216 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.dex.Constants;
@@ -68,7 +69,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -1527,8 +1527,8 @@
                         .setProto(outline.buildProto())
                         // It is OK to set the api level to UNKNOWN since we are not interested in
                         // inlining the outlines anyway.
-                        .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
-                        .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
+                        .setApiLevelForDefinition(ComputedApiLevel.unknown())
+                        .setApiLevelForCode(ComputedApiLevel.unknown())
                         .setCode(m -> new OutlineCode(outline));
                     if (appView.options().isGeneratingClassFiles()) {
                       builder.setClassFileVersion(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index a735ad9..260a5a3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
@@ -180,7 +178,8 @@
                 DexEncodedMethod addedMethod =
                     createSynthesizedMethod(service, classes, methodProcessingContext);
                 if (appView.options().isGeneratingClassFiles()) {
-                  addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
+                  addedMethod.upgradeClassFileVersion(
+                      code.context().getDefinition().getClassFileVersion());
                 }
                 return addedMethod;
               });
@@ -206,10 +205,11 @@
                     builder
                         .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                         .setProto(proto)
-                        .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
+                        .setApiLevelForDefinition(appView.computedMinApiLevel())
                         .setApiLevelForCode(
                             apiLevelCompute.computeApiLevelForDefinition(
-                                ListUtils.map(classes, clazz -> clazz.type)))
+                                ListUtils.map(classes, clazz -> clazz.type),
+                                apiLevelCompute.getPlatformApiLevelOrUnknown(appView)))
                         .setCode(
                             m ->
                                 ServiceLoaderSourceCode.generate(
@@ -248,7 +248,7 @@
     private final IRCode code;
     private final InvokeStatic serviceLoaderLoad;
 
-    private InstructionListIterator iterator;
+    private final InstructionListIterator iterator;
 
     Rewriter(IRCode code, InstructionListIterator iterator, InvokeStatic serviceLoaderLoad) {
       this.iterator = iterator;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index aaeb960..5639925 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
@@ -45,8 +43,8 @@
                 builder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                     .setClassFileVersion(CfVersion.V1_8)
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(method -> getToStringIfNotNullCodeTemplate(method, options))
                     .setProto(proto));
     return new UtilityMethodForCodeOptimizations(syntheticMethod);
@@ -74,8 +72,8 @@
                 builder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                     .setClassFileVersion(CfVersion.V1_8)
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(
                         method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options))
                     .setProto(proto));
@@ -104,8 +102,8 @@
                 builder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                     .setClassFileVersion(CfVersion.V1_8)
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(method -> getThrowIllegalAccessErrorCodeTemplate(method, options))
                     .setProto(proto));
     return new UtilityMethodForCodeOptimizations(syntheticMethod);
@@ -132,8 +130,8 @@
                 builder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                     .setClassFileVersion(CfVersion.V1_8)
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(
                         method -> getThrowIncompatibleClassChangeErrorCodeTemplate(method, options))
                     .setProto(proto));
@@ -162,8 +160,8 @@
                 builder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                     .setClassFileVersion(CfVersion.V1_8)
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(method -> getThrowNoSuchMethodErrorCodeTemplate(method, options))
                     .setProto(proto));
     return new UtilityMethodForCodeOptimizations(syntheticMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index e392248..9997cc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.optimize.enums;
 
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.graph.AppView;
@@ -186,8 +185,8 @@
                                   checkNotNullMethod
                                       .getDefinition()
                                       .getClassFileVersionOrElse(null))
-                              .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                              .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                              .setApiLevelForDefinition(appView.computedMinApiLevel())
+                              .setApiLevelForCode(appView.computedMinApiLevel())
                               .setCode(method -> new CheckNotZeroCode(checkNotNullMethod))
                               .setOptimizationInfo(
                                   checkNotNullMethod
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
index ddf8c42..ed8958d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.enums;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
 import com.android.tools.r8.cf.CfVersion;
@@ -131,8 +130,8 @@
             methodBuilder ->
                 methodBuilder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(codeGenerator)
                     .setClassFileVersion(CfVersion.V1_6));
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 90d49ac..182b415 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.enums;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
-
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfArrayStore;
 import com.android.tools.r8.cf.code.CfConstNumber;
@@ -149,8 +147,8 @@
             methodBuilder ->
                 methodBuilder
                     .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-                    .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-                    .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
                     .setCode(codeGenerator)
                     .setClassFileVersion(CfVersion.V1_6));
   }
@@ -232,7 +230,7 @@
                   dexItemFactory.createField(
                       sharedUtilityClassType, dexItemFactory.intArrayType, "$VALUES"))
               .setAccessFlags(FieldAccessFlags.createPublicStaticFinalSynthetic())
-              .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+              .setApiLevel(appView.computedMinApiLevel())
               .build();
       fieldAccessInfoCollectionModifierBuilder
           .recordFieldReadInUnknownContext(valuesField.getReference())
@@ -249,8 +247,8 @@
           .setAccessFlags(MethodAccessFlags.createForClassInitializer())
           .setCode(createClassInitializerCode(sharedUtilityClassType, valuesField))
           .setClassFileVersion(CfVersion.V1_6)
-          .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-          .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+          .setApiLevelForDefinition(appView.computedMinApiLevel())
+          .setApiLevelForCode(appView.computedMinApiLevel())
           .build();
     }
 
@@ -295,8 +293,8 @@
               .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
               .setCode(createValuesMethodCode(sharedUtilityClassType, valuesField))
               .setClassFileVersion(CfVersion.V1_6)
-              .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
-              .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
+              .setApiLevelForDefinition(appView.computedMinApiLevel())
+              .setApiLevelForCode(appView.computedMinApiLevel())
               .build();
       this.valuesMethod = valuesMethod;
       return valuesMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
index 27fa69a..5cb256c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.optimize.library;
 
 import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
@@ -39,7 +38,7 @@
                       .setAccessFlags(
                           FieldAccessFlags.fromCfAccessFlags(
                               Constants.ACC_PRIVATE | Constants.ACC_FINAL))
-                      .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+                      .setApiLevel(appView.computedMinApiLevel())
                       // Will be traced by the enqueuer.
                       .disableAndroidApiLevelCheck()
                       .build());
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 129acae..19bbe92 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
@@ -87,7 +86,7 @@
                       .dexItemFactory()
                       .createField(clazz.type, clinitField.type, clinitField.name))
               .setAccessFlags(accessFlags)
-              .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+              .setApiLevel(appView.computedMinApiLevel())
               .build();
       clazz.appendStaticField(encodedClinitField);
     }
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 941bcff..227cbf7 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -21,26 +22,25 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ListIterator;
 
 public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> {
 
   protected final AppView<? extends AppInfoWithClassHierarchy> appView;
   protected final Enqueuer enqueuer;
-  private final AndroidApiLevelCompute computeApiLevel;
-  private AndroidApiLevel maxApiReferenceLevel;
+  private final AndroidApiLevelCompute apiLevelCompute;
+  private ComputedApiLevel maxApiReferenceLevel;
 
   public DefaultEnqueuerUseRegistry(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod context,
       Enqueuer enqueuer,
-      AndroidApiLevelCompute computeApiLevel) {
+      AndroidApiLevelCompute apiLevelCompute) {
     super(appView, context);
     this.appView = appView;
     this.enqueuer = enqueuer;
-    this.computeApiLevel = computeApiLevel;
-    this.maxApiReferenceLevel = appView.options().getMinApiLevel();
+    this.apiLevelCompute = apiLevelCompute;
+    maxApiReferenceLevel = appView.computedMinApiLevel();
   }
 
   public DexProgramClass getContextHolder() {
@@ -223,14 +223,18 @@
     if (reference.isDexMember()) {
       maxApiReferenceLevel =
           maxApiReferenceLevel.max(
-              computeApiLevel.computeApiLevelForDefinition(
-                  reference.asDexMember(), appView.dexItemFactory()));
+              apiLevelCompute.computeApiLevelForDefinition(
+                  reference.asDexMember(),
+                  appView.dexItemFactory(),
+                  apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
     }
     maxApiReferenceLevel =
-        maxApiReferenceLevel.max(computeApiLevel.computeApiLevelForLibraryReference(reference));
+        maxApiReferenceLevel.max(
+            apiLevelCompute.computeApiLevelForLibraryReference(
+                reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
   }
 
-  public AndroidApiLevel getMaxApiReferenceLevel() {
+  public ComputedApiLevel getMaxApiReferenceLevel() {
     return maxApiReferenceLevel;
   }
 }
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 c539364..9f30d38 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -502,7 +502,7 @@
     liveFields = new LiveFieldsSet(graphReporter::registerField);
     apiLevelCompute = AndroidApiLevelCompute.create(appView);
     if (mode.isInitialTreeShaking()) {
-      desugaring = CfInstructionDesugaringCollection.create(appView, apiLevelCompute);
+      desugaring = CfInstructionDesugaringCollection.create(appView);
       interfaceProcessor = new InterfaceProcessor(appView);
     } else {
       desugaring = CfInstructionDesugaringCollection.empty();
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 4f116de..9396f50 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -8,7 +8,8 @@
 import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
 
-import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -66,7 +67,6 @@
 import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
@@ -222,7 +222,7 @@
   private final MethodPoolCollection methodPoolCollection;
   private final Timing timing;
   private Collection<DexMethod> invokes;
-  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+  private final AndroidApiLevelCompute apiLevelCompute;
 
   private final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
 
@@ -261,7 +261,7 @@
     this.executorService = executorService;
     this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
     this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
-    this.apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+    this.apiLevelCompute = AndroidApiLevelCompute.create(appView);
     this.timing = timing;
 
     Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
@@ -531,11 +531,9 @@
     // 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) {
+      ComputedApiLevel sourceApiLevel = sourceClass.getApiReferenceLevel(appView, apiLevelCompute);
+      ComputedApiLevel targetApiLevel = targetClass.getApiReferenceLevel(appView, apiLevelCompute);
+      if (!sourceApiLevel.equals(targetApiLevel)) {
         if (Log.ENABLED) {
           AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
         }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 56a9d20..0af4bf0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -3,8 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
-import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
-
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -20,7 +19,6 @@
 import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.AndroidApiLevel;
 
 public class SyntheticMethodBuilder {
 
@@ -39,8 +37,8 @@
   private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
   private DexAnnotationSet annotations = DexAnnotationSet.empty();
   private ParameterAnnotationsList parameterAnnotationsList = ParameterAnnotationsList.empty();
-  private AndroidApiLevel apiLevelForDefinition = NOT_SET;
-  private AndroidApiLevel apiLevelForCode = NOT_SET;
+  private ComputedApiLevel apiLevelForDefinition = ComputedApiLevel.notSet();
+  private ComputedApiLevel apiLevelForCode = ComputedApiLevel.notSet();
   private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
 
   private boolean checkAndroidApiLevels = true;
@@ -109,12 +107,12 @@
     return this;
   }
 
-  public SyntheticMethodBuilder setApiLevelForDefinition(AndroidApiLevel apiLevelForDefinition) {
+  public SyntheticMethodBuilder setApiLevelForDefinition(ComputedApiLevel apiLevelForDefinition) {
     this.apiLevelForDefinition = apiLevelForDefinition;
     return this;
   }
 
-  public SyntheticMethodBuilder setApiLevelForCode(AndroidApiLevel apiLevelForCode) {
+  public SyntheticMethodBuilder setApiLevelForCode(ComputedApiLevel apiLevelForCode) {
     this.apiLevelForCode = apiLevelForCode;
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 6b82664..38934f2 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.utils.structural.Ordered;
 import java.util.Arrays;
 import java.util.List;
@@ -43,9 +42,7 @@
   R(30),
   S(31),
   Sv2(32),
-  ANDROID_PLATFORM(10000),
-  UNKNOWN(10001),
-  NOT_SET(10002);
+  ANDROID_PLATFORM(10000);
 
   // When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
   // and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
@@ -78,13 +75,6 @@
     return DexVersion.getDexVersion(this);
   }
 
-  public static AndroidApiLevel minApiLevelIfEnabledOrUnknown(AppView<?> appView) {
-    InternalOptions options = appView.options();
-    return options.apiModelingOptions().enableApiCallerIdentification
-        ? options.getMinApiLevel()
-        : UNKNOWN;
-  }
-
   public static List<AndroidApiLevel> getAndroidApiLevelsSorted() {
     return Arrays.asList(AndroidApiLevel.values());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index c4a7396..73cf09f 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 
@@ -15,10 +16,6 @@
     if (!options.apiModelingOptions().enableApiCallerIdentification) {
       return true;
     }
-    if (options.isAndroidPlatform()) {
-      // Don't disable inlining in the Android platform based on the Api database.
-      return true;
-    }
     if (caller.getHolderType() == inlinee.getHolderType()) {
       return true;
     }
@@ -32,12 +29,13 @@
       LibraryMethod method,
       AndroidApiLevelCompute androidApiLevelCompute,
       InternalOptions options) {
-    AndroidApiLevel apiLevel =
-        androidApiLevelCompute.computeApiLevelForLibraryReference(method.getReference());
-    if (apiLevel == AndroidApiLevel.UNKNOWN) {
+    ComputedApiLevel apiLevel =
+        androidApiLevelCompute.computeApiLevelForLibraryReference(
+            method.getReference(), ComputedApiLevel.unknown());
+    if (apiLevel.isUnknownApiLevel()) {
       return false;
     }
     assert options.apiModelingOptions().enableApiCallerIdentification;
-    return apiLevel.isLessThanOrEqualTo(options.getMinApiLevel());
+    return apiLevel.asKnownApiLevel().getApiLevel().isLessThanOrEqualTo(options.getMinApiLevel());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index a807bba..624e70e 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -77,7 +77,6 @@
       case L_MR1:
       case M:
         return DexVersion.V35;
-      case UNKNOWN:
       default:
         throw new Unreachable("Unsupported api level " + androidApiLevel);
     }
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 ea7c922..1cbbc21 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
+
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.DataResourceConsumer;
@@ -17,6 +19,7 @@
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.androidapi.AndroidApiForHashingClass;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Backend;
@@ -424,7 +427,7 @@
   }
 
   public boolean isAndroidPlatform() {
-    return minApiLevel == AndroidApiLevel.ANDROID_PLATFORM;
+    return minApiLevel == ANDROID_PLATFORM;
   }
 
   public boolean isDesugaredLibraryCompilation() {
@@ -564,7 +567,6 @@
 
   public void setMinApiLevel(AndroidApiLevel minApiLevel) {
     assert minApiLevel != null;
-    assert minApiLevel.isLessThan(AndroidApiLevel.UNKNOWN);
     this.minApiLevel = minApiLevel;
   }
 
@@ -1470,7 +1472,7 @@
     public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
     public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
     public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
-    public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
+    public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
 
     public boolean enableApiCallerIdentification = true;
     public boolean checkAllApiReferencesAreSet = true;
@@ -1498,7 +1500,7 @@
 
                   @Override
                   public AndroidApiLevel getApiLevel() {
-                    return classApiMapping.getOrDefault(classReference, AndroidApiLevel.UNKNOWN);
+                    return classApiMapping.getOrDefault(classReference, ANDROID_PLATFORM);
                   }
 
                   @Override
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 53886e2..2d0cc12 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -867,8 +867,6 @@
       case J_MR2:
       case K_WATCH:
       case ANDROID_PLATFORM:
-      case UNKNOWN:
-      case NOT_SET:
         return false;
       default:
         return true;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index d7db635..300eed5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -103,8 +103,7 @@
     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());
+      apiLevel[i] = (byte) (androidApiLevel == null ? -1 : androidApiLevel.getLevel());
     }
 
     try (FileOutputStream fileOutputStream = new FileOutputStream(pathToIndices.toFile());
@@ -160,7 +159,7 @@
     return ((reference, apiLevel) -> {
       AndroidApiLevel existingMethod = apiLevelMap.put(reference.hashCode(), apiLevel);
       if (existingMethod != null) {
-        apiLevelMap.put(reference.hashCode(), AndroidApiLevel.NOT_SET);
+        apiLevelMap.put(reference.hashCode(), null);
         Pair<DexReference, AndroidApiLevel> existingPair = reverseMap.get(reference.hashCode());
         addAmbiguousEntry(existingPair.getSecond(), existingPair.getFirst(), ambiguousMap);
         addAmbiguousEntry(apiLevel, reference, ambiguousMap);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 88d53c1..9bbc751 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -126,7 +126,7 @@
         AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
     DexItemFactory factory = new DexItemFactory();
     AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase =
-        new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of());
+        new AndroidApiLevelHashingDatabaseImpl(ImmutableList.of());
     parsedApiClasses.forEach(
         parsedApiClass -> {
           DexType type = factory.createType(parsedApiClass.getClassReference().getDescriptor());
@@ -137,9 +137,13 @@
                   methodReferences.forEach(
                       methodReference -> {
                         DexMethod method = factory.createMethod(methodReference);
-                        androidApiLevelDatabase
-                            .getMethodApiLevel(method)
-                            .isLessThanOrEqualTo(methodApiLevel);
+                        AndroidApiLevel androidApiLevel;
+                        if (factory.objectMembers.isObjectMember(method)) {
+                          androidApiLevel = AndroidApiLevel.B;
+                        } else {
+                          androidApiLevel = androidApiLevelDatabase.getMethodApiLevel(method);
+                        }
+                        androidApiLevel.isLessThanOrEqualTo(methodApiLevel);
                       }));
           parsedApiClass.visitFieldReferences(
               (fieldApiLevel, fieldReferences) ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index c87f2cc..d4127e8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -63,15 +63,22 @@
   }
 
   public static class AccessibilityNodeInfo$AccessibilityAction {
+
+    private int i;
+
+    public AccessibilityNodeInfo$AccessibilityAction(int i, CharSequence sequence) {
+      this.i = i;
+    }
+
     int describeContents() {
-      return 42;
+      return i;
     }
   }
 
   public static class Main {
 
     public static void main(String[] args) {
-      new AccessibilityNodeInfo$AccessibilityAction().describeContents();
+      new AccessibilityNodeInfo$AccessibilityAction(42, "foobar").describeContents();
     }
   }
 }
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 8e5cc58..c8ee027 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
@@ -42,7 +43,7 @@
 
   public static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
       ThrowableConsumer<T> setMockApiLevelForMethod(
-          Constructor constructor, AndroidApiLevel apiLevel) {
+          Constructor<?> constructor, AndroidApiLevel apiLevel) {
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
           options -> {
@@ -118,7 +119,12 @@
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
           options -> {
-            options.apiModelingOptions().tracedMethodApiLevelCallback = consumer;
+            options.apiModelingOptions().tracedMethodApiLevelCallback =
+                (methodReference, computedApiLevel) -> {
+                  assertTrue(computedApiLevel.isKnownApiLevel());
+                  consumer.accept(
+                      methodReference, computedApiLevel.asKnownApiLevel().getApiLevel());
+                };
           });
     };
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index 86ccd24..51bd0cf 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -189,7 +189,7 @@
                       new DataResourceConsumerForTesting(options.dataResourceConsumer);
                   options.dataResourceConsumer = dataResourceConsumer;
                 })
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), OtherTestClass.class)
             .assertSuccessWithOutput(expectedOutput)
             .inspector();