Merge commit 'b5260b89d8dc82fee8cb07987efa5c6b11881cbd' into dev-release
diff --git a/build.gradle b/build.gradle
index 08c9141..eefc425 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2103,6 +2103,7 @@
             // Print the new index content.
             index << "<html><head><title>${title}</title>"
             index << "<style> * { font-family: monospace; }</style>"
+            index << "<meta http-equiv='refresh' content='10' />"
             index << "</head><body><h1>${title}</h1>"
             index << "<p>Run on: ${new Date()}</p>"
             index << "<p>Git branch: ${branch}</p>"
@@ -2111,7 +2112,7 @@
             }
             index << "<p><a href=\"file://${index}\">Most recent result index</a></p>"
             index << "<p><a href=\"file://${reportDir}\">Test directories</a></p>"
-            index << "<h2>Failing tests (reload to refresh)</h2><ul>"
+            index << "<h2>Failing tests (refreshing automatically every 10 seconds)</h2><ul>"
         }
     }
 
diff --git a/doc/retrace.md b/doc/retrace.md
new file mode 100644
index 0000000..aaea59b
--- /dev/null
+++ b/doc/retrace.md
@@ -0,0 +1,133 @@
+# R8, Retrace and map file versioning
+
+Programs compiled by R8 are not structurally the same as the input. For example,
+names, invokes and line numbers can all change when compiling with R8. The
+correspondence between the input and the output is recorded in a mapping file.
+The mapping file can be used with retrace to recover the original stack trace
+from the stack traces produced by running the R8 compiled program. More
+information about R8 and mapping files can be found
+[here](https://developer.android.com/studio/build/shrink-code#decode-stack-trace).
+
+## Additional information appended as comments to the file
+
+The format for additional information is encoded as comments with json formatted
+data. The data has an ID to disambiguate it from other information.
+
+### Version
+
+The version information states what version the content of the mapping file is
+using.
+
+The format of the version information is:
+```
+# {"id":"com.android.tools.r8.mapping","version":"1.0"}
+```
+Here `id` must be `com.android.tools.r8.mapping` and `version` is the version of
+the mapping file.
+
+The version information applies to content of the file following the position of
+the version entry until either the end of the file or another version entry is
+present.
+
+If no version information is present, the content is assumed to have version
+zero and no additional mapping information except [Source File](#source-file).
+
+When interpreting the mapping file, any additional mapping information
+pertaining to a later version should be ignored. In other words, treated as a
+normal comment.
+
+Retracing tools supporting this versioning scheme should issue a warning when
+given mapping files with versions higher than the version supported by the tool.
+
+### Source File
+
+The source file information states what source file a class originated from.
+
+The source file information must be placed directly below the class mapping it
+pertains to. The format of the source file information is:
+```
+some.package.Class -> foo.a:
+# {"id":"sourceFile","fileName":"R8Test.kt"}
+```
+Here `id` must be the string `sourceFile` and `fileName` is the source file name
+as a string value (in this example `R8Test.kt`).
+
+Note that the `id` of the source file information is unqualified. It is the only
+allowed unqualified identifier as it was introduced prior to the versioning
+scheme.
+
+### Synthesized (Introduced at version 1.0)
+
+The synthesized information states what parts of the compiled output program are
+synthesized by the compiler. A retracing tool should use the synthesized
+information to strip out synthesized method frames from retraced stacks.
+
+The synthesized information must be placed directly below the class, field or
+method mapping it pertains to. The format of the synthesized information is:
+```
+# {'id':'com.android.tools.r8.synthesized'}
+```
+Here `id` must be `com.android.tools.r8.synthesized`. There is no other content.
+
+A class mapping would be:
+```
+some.package.SomeSynthesizedClass -> x.y.z:
+# {'id':'com.android.tools.r8.synthesized'}
+```
+This specifies that the class `x.y.z` has been synthesized by the compiler and
+thus it did not exist in the original input program.
+
+Notice that the left-hand side and right-hand side, here
+`some.package.SomeSynthesizedClass` and `x.y.z` respectively, could be any class
+name. It is likely that the mapping for synthetics is the identity, but a useful
+name can be placed here if possible which legacy retrace tools would use when
+retracing.
+
+A field mapping would be:
+```
+some.package.Class -> x.y.z:
+  int someField -> a
+  # {'id':'com.android.tools.r8.synthesized'}
+```
+This specifies that the field `x.y.z.a` has been synthesized by the compiler. As
+for classes, since the field is not part of the original input program, the
+left- and right-hand side names could be anything. Note that a field can be
+synthesized without the class being synthesized.
+
+A method mapping would be:
+```
+some.package.SomeSynthesizedClass -> x.y.z:
+  void someMethod() -> a
+  # {'id':'com.android.tools.r8.synthesized'}
+```
+This specifies that the method `x.y.z.a()` has been synthesized by the compiler.
+As for classes, since the method is not part of the original input program, the
+left- and right-hand side names could be anything. Note that a method can be
+synthesized without the class being synthesized.
+
+For inline frames a mapping would be:
+```
+some.package.Class -> foo.a:
+    4:4:void example.Foo.lambda$main$0():225 -> a
+    4:4:void run(example.Foo):2 -> a
+    # {'id':'com.android.tools.r8.synthesized'}
+    5:5:void example.Foo.lambda$main$1():228 -> a
+    5:5:void run(example.Foo):4 -> a
+    # {'id':'com.android.tools.r8.synthesized'} <-- redundant
+```
+This specifies that line 4 in the method `foo.a.a` is in a method that has
+been synthesized by the compiler. Since the method is either synthesized or not
+any extra synthesized comments will have no effect.
+
+Synthesized information should never be placed on inlined frames:
+```
+some.package.Class -> foo.a:
+4:4:void example.Foo.syntheticThatIsInlined():225 -> a
+# {'id':'com.android.tools.r8.synthesized'}
+4:4:void run(example.Foo):2 -> a
+```
+In the above, the mapping information suggests that the inline frame
+`example.Foo.syntheticThatIsInlined` should be marked as `synthesized`. However,
+since that method was not part of the input program it should not be in the
+output mapping information at all.
+
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 6c9025d..68b9354 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -158,6 +158,7 @@
               .setGenericSignature(MethodTypeSignature.noSignature())
               .setCode(code)
               .setClassFileVersion(CfVersion.V1_6)
+              .disableAndroidApiLevelCheck()
               .build();
       if (method.isStatic() || method.isDirectMethod()) {
         directMethods.add(throwingMethod);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2ab471a..462c008 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -48,7 +48,7 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterLibraryTypeSynthesizor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterLibraryTypeSynthesizer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.records.RecordRewriter;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -310,8 +310,8 @@
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
       if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
-        DesugaredLibraryRetargeterLibraryTypeSynthesizor.checkForAssumedLibraryTypes(appView);
-        DesugaredLibraryRetargeterLibraryTypeSynthesizor.amendLibraryWithRetargetedMembers(appView);
+        DesugaredLibraryRetargeterLibraryTypeSynthesizer.checkForAssumedLibraryTypes(appView);
+        DesugaredLibraryRetargeterLibraryTypeSynthesizer.amendLibraryWithRetargetedMembers(appView);
       }
       InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
       BackportedMethodRewriter.registerAssumedLibraryTypes(options);
@@ -438,6 +438,8 @@
           annotationRemover.ensureValid().run(executorService);
           new GenericSignatureRewriter(appView, NamingLens.getIdentityLens(), genericContextBuilder)
               .run(appView.appInfo().classes(), executorService);
+
+          assert appView.checkForTesting(() -> allReferencesAssignedApiLevel(appViewWithLiveness));
         }
       } finally {
         timing.end();
@@ -888,7 +890,8 @@
   }
 
   private static boolean allReferencesAssignedApiLevel(AppView<?> appView) {
-    if (!appView.options().apiModelingOptions().checkAllApiReferencesAreSet) {
+    if (!appView.options().apiModelingOptions().checkAllApiReferencesAreSet
+        || appView.options().configurationDebugging) {
       return true;
     }
     // This will return false if we find anything in the library which is not modeled.
@@ -897,12 +900,11 @@
         .classes()
         .forEach(
             clazz -> {
-              if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-                assert clazz.getMembersApiReferenceLevel(appView) != AndroidApiLevel.UNKNOWN
-                    : "Every member should have been analyzed";
-              } else {
+              assert clazz.getMembersApiReferenceLevel(appView) != AndroidApiLevel.NOT_SET
+                  : "Every member should have been analyzed";
+              if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
                 assert clazz.getMembersApiReferenceLevel(appView) == AndroidApiLevel.UNKNOWN
-                    : "Every member should have level UNKNOWN";
+                    : "Every member should have level NOT_SET";
               }
             });
     return true;
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 9336265..03c50c0 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -657,6 +657,7 @@
               .setGenericSignature(fieldTypeSignature)
               .setAnnotations(fieldAnnotations)
               .setStaticValue(staticValue)
+              .disableAndroidApiLevelCheck()
               .build();
     }
     return fields;
@@ -708,6 +709,7 @@
               .setAnnotations(methodAnnotations)
               .setParameterAnnotations(parameterAnnotationsIterator.getNextFor(method))
               .setCode(code)
+              .disableAndroidApiLevelCheck()
               .build();
     }
     return methods;
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 c89d8ed..5cfbeeb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -59,9 +59,9 @@
       FieldTypeSignature genericSignature,
       DexAnnotationSet annotations,
       DexValue staticValue,
+      AndroidApiLevel apiLevel,
       boolean deprecated,
-      boolean d8R8Synthesized,
-      AndroidApiLevel apiLevel) {
+      boolean d8R8Synthesized) {
     super(field, annotations, d8R8Synthesized, apiLevel);
     this.accessFlags = accessFlags;
     this.staticValue = staticValue;
@@ -365,6 +365,10 @@
     private final boolean d8R8Synthesized;
     private Consumer<DexEncodedField> buildConsumer = ConsumerUtils.emptyConsumer();
 
+    // Checks to impose on the built method. They should always be active to start with and be
+    // lowered on the use site.
+    private boolean checkAndroidApiLevel = true;
+
     private Builder(boolean d8R8Synthesized) {
       this.d8R8Synthesized = d8R8Synthesized;
     }
@@ -448,11 +452,17 @@
       return this;
     }
 
+    public Builder disableAndroidApiLevelCheck() {
+      checkAndroidApiLevel = false;
+      return this;
+    }
+
     public DexEncodedField build() {
       assert field != null;
       assert accessFlags != null;
       assert genericSignature != null;
       assert annotations != null;
+      assert !checkAndroidApiLevel || apiLevel != AndroidApiLevel.NOT_SET;
       DexEncodedField dexEncodedField =
           new DexEncodedField(
               field,
@@ -460,9 +470,9 @@
               genericSignature,
               annotations,
               staticValue,
+              apiLevel,
               deprecated,
-              d8R8Synthesized,
-              apiLevel);
+              d8R8Synthesized);
       dexEncodedField.optimizationInfo = optimizationInfo;
       buildConsumer.accept(dexEncodedField);
       return dexEncodedField;
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 b6c7d02..a153a6a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -11,6 +11,7 @@
 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 com.android.tools.r8.cf.CfVersion;
@@ -45,6 +46,7 @@
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
+import com.android.tools.r8.graph.DexEncodedMethod.CompilationState;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
 import com.android.tools.r8.ir.code.IRCode;
@@ -146,9 +148,9 @@
           ParameterAnnotationsList.empty(),
           null,
           false,
+          NOT_SET,
+          NOT_SET,
           null,
-          AndroidApiLevel.UNKNOWN,
-          AndroidApiLevel.UNKNOWN,
           false);
   public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
       new Int2ReferenceArrayMap<>(0);
@@ -238,9 +240,9 @@
       ParameterAnnotationsList parameterAnnotationsList,
       Code code,
       boolean d8R8Synthesized,
-      CfVersion classFileVersion,
       AndroidApiLevel apiLevelForDefinition,
       AndroidApiLevel apiLevelForCode,
+      CfVersion classFileVersion,
       boolean deprecated) {
     super(method, annotations, d8R8Synthesized, apiLevelForDefinition);
     this.accessFlags = accessFlags;
@@ -253,7 +255,8 @@
     assert accessFlags != null;
     assert code == null || !shouldNotHaveCode();
     assert parameterAnnotationsList != null;
-    assert apiLevelForCode != null && apiLevelForDefinition != null;
+    assert apiLevelForDefinition != null;
+    assert apiLevelForCode != null;
   }
 
   public static DexEncodedMethod toMethodDefinitionOrNull(DexClassAndMethod method) {
@@ -1270,6 +1273,8 @@
                 // Holder is companion class, or retarget method, not an interface.
                 .setStaticTarget(forwardMethod, false)
                 .build())
+        .setApiLevelForDefinition(target.getDefinition().getApiLevelForDefinition())
+        .setApiLevelForCode(target.getDefinition().getApiLevelForCode())
         .build();
   }
 
@@ -1358,7 +1363,7 @@
 
   @Override
   public AndroidApiLevel getApiLevel() {
-    return (isAbstract() ? AndroidApiLevel.B : getApiLevelForCode())
+    return (shouldNotHaveCode() ? AndroidApiLevel.B : getApiLevelForCode())
         .max(getApiLevelForDefinition());
   }
 
@@ -1441,8 +1446,8 @@
     private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.getInstance();
     private KotlinMethodLevelInfo kotlinInfo = getNoKotlinInfo();
     private CfVersion classFileVersion = null;
-    private AndroidApiLevel apiLevelForDefinition = AndroidApiLevel.UNKNOWN;
-    private AndroidApiLevel apiLevelForCode = AndroidApiLevel.UNKNOWN;
+    private AndroidApiLevel apiLevelForDefinition = NOT_SET;
+    private AndroidApiLevel apiLevelForCode = NOT_SET;
     private final boolean d8R8Synthesized;
     private boolean deprecated = false;
 
@@ -1450,6 +1455,7 @@
     // lowered on the use site.
     private boolean checkMethodNotNull = true;
     private boolean checkParameterAnnotationList = true;
+    private boolean checkAndroidApiLevels = true;
 
     private Consumer<DexEncodedMethod> buildConsumer = ConsumerUtils.emptyConsumer();
 
@@ -1687,6 +1693,11 @@
       return this;
     }
 
+    public Builder disableAndroidApiLevelCheck() {
+      checkAndroidApiLevels = false;
+      return this;
+    }
+
     public DexEncodedMethod build() {
       assert !checkMethodNotNull || method != null;
       assert accessFlags != null;
@@ -1695,8 +1706,8 @@
       assert !checkParameterAnnotationList
           || parameterAnnotations.isEmpty()
           || parameterAnnotations.size() == method.proto.parameters.size();
-      assert apiLevelForDefinition != null;
-      assert apiLevelForCode != null;
+      assert !checkAndroidApiLevels || apiLevelForDefinition != null;
+      assert !checkAndroidApiLevels || apiLevelForCode != null;
       DexEncodedMethod result =
           new DexEncodedMethod(
               method,
@@ -1706,9 +1717,9 @@
               parameterAnnotations,
               code,
               d8R8Synthesized,
-              classFileVersion,
               apiLevelForDefinition,
               apiLevelForCode,
+              classFileVersion,
               deprecated);
       result.setKotlinMemberInfo(kotlinInfo);
       result.compilationState = compilationState;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 2164d54..4c6c165 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -38,7 +38,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.synthesis.SyntheticMarker;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AsmUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -675,6 +674,7 @@
                 .setAnnotations(annotationSet)
                 .setStaticValue(staticValue)
                 .setDeprecated(AsmUtils.isDeprecated(access))
+                .disableAndroidApiLevelCheck()
                 .build();
         if (flags.isStatic()) {
           parent.staticFields.add(field);
@@ -911,10 +911,9 @@
               .setParameterAnnotations(parameterAnnotationsList)
               .setCode(code)
               .setClassFileVersion(parent.version)
-              .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
-              .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
               .setDeprecated(deprecated)
               .disableParameterAnnotationListCheck()
+              .disableAndroidApiLevelCheck()
               .build();
       Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
       if (parent.methodSignatures.add(signature)) {
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 9da5368..85c63ad 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
@@ -7,6 +7,7 @@
 import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.LookupTarget;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramField;
@@ -47,7 +48,12 @@
           .accept(method.getMethodReference(), registry.getMaxApiReferenceLevel());
     }
     computeApiLevelForDefinition(method);
-    method.getDefinition().setApiLevelForCode(registry.getMaxApiReferenceLevel());
+    method
+        .getDefinition()
+        .setApiLevelForCode(
+            appView.options().apiModelingOptions().enableApiCallerIdentification
+                ? registry.getMaxApiReferenceLevel()
+                : AndroidApiLevel.UNKNOWN);
   }
 
   @Override
@@ -69,7 +75,17 @@
         });
   }
 
+  @Override
+  public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {
+    // We may not trace into failed resolution targets.
+    method.setApiLevelForCode(AndroidApiLevel.UNKNOWN);
+  }
+
   private void computeApiLevelForDefinition(DexClassAndMember<?, ?> member) {
+    if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      member.getDefinition().setApiLevelForDefinition(AndroidApiLevel.UNKNOWN);
+      return;
+    }
     member
         .getDefinition()
         .setApiLevelForDefinition(
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 0610c55..ceb7e92 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.LookupTarget;
 import com.android.tools.r8.graph.ProgramDefinition;
@@ -37,6 +38,8 @@
 
   public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {}
 
+  public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {}
+
   /**
    * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
    * analysis may enqueue items into the worklist upon the fixpoint using {@param worklist}.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index a0d130d..1e393aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPICallbackSynthesizor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPICallbackSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
@@ -64,9 +64,9 @@
       if (interfaceMethodProcessorFacade != null) {
         desugarings.add(interfaceMethodProcessorFacade);
       }
-      DesugaredLibraryAPICallbackSynthesizor apiCallbackSynthesizor =
+      DesugaredLibraryAPICallbackSynthesizer apiCallbackSynthesizor =
           appView.rewritePrefix.isRewriting()
-              ? new DesugaredLibraryAPICallbackSynthesizor(appView)
+              ? new DesugaredLibraryAPICallbackSynthesizer(appView)
               : null;
       // At this point the desugaredLibraryAPIConverter is required to be last to generate
       // call-backs on the forwarding methods.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index a18d9b9..0d0dd97 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -175,6 +175,8 @@
             .setParameterAnnotations(
                 methodDefinition.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()))
             .setCode(forwardMethodBuilder.build())
+            .setApiLevelForDefinition(methodDefinition.getApiLevelForDefinition())
+            .setApiLevelForCode(methodDefinition.getApiLevelForCode())
             .build();
     // Optimize to generate DexCode instead of CfCode.
     ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
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 c7f5ec8..c2d95ca 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
@@ -36,7 +36,6 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -161,6 +160,8 @@
                 MethodAccessFlags.fromSharedAccessFlags(
                     Constants.ACC_PUBLIC | Constants.ACC_FINAL, false))
             .setCode(LambdaMainMethodSourceCode.build(this, mainMethod))
+            // The api level is computed when tracing.
+            .disableAndroidApiLevelCheck()
             .build());
 
     // Synthesize bridge methods.
@@ -178,6 +179,8 @@
                           | Constants.ACC_BRIDGE,
                       false))
               .setCode(LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod))
+              // The api level is computed when tracing.
+              .disableAndroidApiLevelCheck()
               .build());
     }
     builder.setVirtualMethods(methods);
@@ -198,6 +201,8 @@
             .setMethod(constructor)
             .setAccessFlags(accessFlags)
             .setCode(LambdaConstructorSourceCode.build(this))
+            // The api level is computed when tracing.
+            .disableAndroidApiLevelCheck()
             .build());
 
     // Class constructor for stateless lambda classes.
@@ -209,6 +214,8 @@
                   MethodAccessFlags.fromSharedAccessFlags(
                       Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true))
               .setCode(LambdaClassConstructorSourceCode.build(this))
+              // The api level is computed when tracing.
+              .disableAndroidApiLevelCheck()
               .build());
       feedback.classInitializerMayBePostponed(methods.get(1));
     }
@@ -226,7 +233,7 @@
               .setField(getCaptureField(i))
               .setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
               // The api level is computed when tracing.
-              .setApiLevel(AndroidApiLevel.UNKNOWN)
+              .disableAndroidApiLevelCheck()
               .build());
     }
     builder.setInstanceFields(fields);
@@ -249,7 +256,7 @@
                               | Constants.ACC_STATIC))
                   .setStaticValue(DexValueNull.NULL)
                   // The api level is computed when tracing.
-                  .setApiLevel(AndroidApiLevel.UNKNOWN)
+                  .disableAndroidApiLevelCheck()
                   .build()));
     }
   }
@@ -564,6 +571,8 @@
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
                             .setCode(encodedMethod.getCode())
+                            .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
+                            .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
                     newMethod.copyMetadata(encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
@@ -647,6 +656,8 @@
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
                             .setCode(encodedMethod.getCode())
+                            .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
+                            .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
                     newMethod.copyMetadata(encodedMethod);
                     forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
@@ -708,6 +719,8 @@
                   .setMethod(callTarget)
                   .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
                   .setCode(AccessorMethodSourceCode.build(LambdaClass.this, callTarget))
+                  // The api level is computed when tracing.
+                  .disableAndroidApiLevelCheck()
                   .build());
       accessorClass.addDirectMethod(accessorMethod.getDefinition());
       if (appView.options().isDesugaredLibraryCompilation()
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 22654d6..b10ed04 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
@@ -3,7 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar.constantdynamic;
 
+import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown;
 import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKESTATIC;
 import static org.objectweb.asm.Opcodes.PUTSTATIC;
 
@@ -23,6 +25,7 @@
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
 import com.android.tools.r8.cf.code.CfMonitor;
+import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
@@ -127,12 +130,12 @@
             DexEncodedField.syntheticBuilder()
                 .setField(this.initializedValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
-                .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView))
+                .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
                 .build(),
             DexEncodedField.syntheticBuilder()
                 .setField(this.constantValueField)
                 .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic())
-                .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView))
+                .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
                 .build()));
   }
 
@@ -148,10 +151,19 @@
                 .build()));
   }
 
-  private void invokeBootstrapMethod(
-      ConstantDynamicReference reference, ImmutableList.Builder<CfInstruction> instructions) {
+  private void invokeBootstrapMethod(ImmutableList.Builder<CfInstruction> instructions) {
     assert reference.getBootstrapMethod().type.isInvokeStatic();
+    DexMethodHandle bootstrapMethodHandle = reference.getBootstrapMethod();
+    DexMethod bootstrapMethodReference = bootstrapMethodHandle.asMethod();
+    // TODO(b/178172809): Use MethodHandle.invokeWithArguments if supported.
+    instructions.add(new CfConstNull());
+    instructions.add(new CfConstString(reference.getName()));
+    instructions.add(new CfConstClass(reference.getType()));
+    instructions.add(new CfInvoke(INVOKESTATIC, bootstrapMethodReference, false));
+    instructions.add(new CfCheckCast(reference.getType()));
+  }
 
+  private CfCode generateGetterCode(SyntheticProgramClassBuilder builder) {
     // TODO(b/178172809): Use MethodHandle.invokeWithArguments if supported.
     DexMethodHandle bootstrapMethodHandle = reference.getBootstrapMethod();
     DexMethod bootstrapMethodReference = bootstrapMethodHandle.asMethod();
@@ -159,25 +171,24 @@
         appView
             .appInfoForDesugaring()
             .resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface);
-    if (resolution.isFailedResolution()) {
-      // TODO(b/178172809): Generate code which throws ICCE.
+    if ((resolution.isSingleResolution()
+            && resolution.asSingleResolution().getResolvedMethod().isStatic())
+        || resolution.isFailedResolution()) {
+      if (resolution.isSingleResolution()) {
+        // Ensure that the bootstrap method is accessible from the generated class.
+        SingleResolutionResult result = resolution.asSingleResolution();
+        MethodAccessFlags flags = result.getResolvedMethod().getAccessFlags();
+        flags.unsetPrivate();
+        flags.setPublic();
+      }
+      return generateGetterCodeInvokingBootstrapMethod(builder);
+    } else {
+      // Unconditionally throw ICCE as the RI.
+      return generateGetterCodeThrowingICCE(builder);
     }
-    SingleResolutionResult result = resolution.asSingleResolution();
-    assert result.getResolvedMethod().isStatic();
-    assert result.getResolvedHolder().isProgramClass();
-    instructions.add(new CfConstNull());
-    instructions.add(new CfConstString(reference.getName()));
-    instructions.add(new CfConstClass(reference.getType()));
-    instructions.add(new CfInvoke(INVOKESTATIC, bootstrapMethodReference, false));
-    instructions.add(new CfCheckCast(reference.getType()));
-
-    // Ensure that the bootstrap method is accessible from the generated class.
-    MethodAccessFlags flags = result.getResolvedMethod().getAccessFlags();
-    flags.unsetPrivate();
-    flags.setPublic();
   }
 
-  private CfCode generateGetterCode(SyntheticProgramClassBuilder builder) {
+  private CfCode generateGetterCodeInvokingBootstrapMethod(SyntheticProgramClassBuilder builder) {
     int maxStack = 3;
     int maxLocals = 2;
     ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
@@ -202,7 +213,7 @@
     instructions.add(new CfFieldInstruction(GETSTATIC, initializedValueField));
     instructions.add(new CfIf(If.Type.NE, ValueType.INT, initializedTrueSecond));
 
-    invokeBootstrapMethod(reference, instructions);
+    invokeBootstrapMethod(instructions);
     instructions.add(new CfFieldInstruction(PUTSTATIC, constantValueField));
     instructions.add(new CfConstNumber(1, ValueType.INT));
     instructions.add(new CfFieldInstruction(PUTSTATIC, initializedValueField));
@@ -259,6 +270,33 @@
         localVariables);
   }
 
+  private CfCode generateGetterCodeThrowingICCE(SyntheticProgramClassBuilder builder) {
+    int maxStack = 2;
+    int maxLocals = 0;
+    ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+    ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+    ImmutableList.Builder<CfInstruction> instructions = ImmutableList.builder();
+    DexItemFactory factory = builder.getFactory();
+    instructions.add(new CfNew(factory.icceType));
+    instructions.add(new CfStackInstruction(Opcode.Dup));
+    instructions.add(
+        new CfInvoke(
+            INVOKESPECIAL,
+            factory.createMethod(
+                factory.icceType,
+                factory.createProto(factory.voidType),
+                factory.constructorMethodName),
+            false));
+    instructions.add(new CfThrow());
+    return new CfCode(
+        builder.getType(),
+        maxStack,
+        maxLocals,
+        instructions.build(),
+        tryCatchRanges,
+        localVariables);
+  }
+
   public final DexProgramClass getConstantDynamicProgramClass() {
     assert clazz != null;
     return clazz;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizer.java
similarity index 98%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizer.java
index 3e0143a..92d7b92 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizer.java
@@ -30,7 +30,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
-public class DesugaredLibraryAPICallbackSynthesizor implements CfPostProcessingDesugaring {
+public class DesugaredLibraryAPICallbackSynthesizer implements CfPostProcessingDesugaring {
 
   private final AppView<?> appView;
   private final DexItemFactory factory;
@@ -38,7 +38,7 @@
   private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
   private final Set<DexMethod> trackedCallBackAPIs;
 
-  public DesugaredLibraryAPICallbackSynthesizor(AppView<?> appView) {
+  public DesugaredLibraryAPICallbackSynthesizer(AppView<?> appView) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
     this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
similarity index 97%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
index 9e50613..faa5857 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
@@ -36,7 +36,7 @@
 import java.util.Set;
 import java.util.TreeSet;
 
-public class DesugaredLibraryRetargeterLibraryTypeSynthesizor {
+public class DesugaredLibraryRetargeterLibraryTypeSynthesizer {
 
   public static void checkForAssumedLibraryTypes(AppView<?> appView) {
     Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
@@ -144,6 +144,7 @@
                               MethodAccessFlags.fromCfAccessFlags(
                                   Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
                           .setCode(null)
+                          .setApiLevelForDefinition(method.getApiLevelForDefinition())
                           .build());
             }
           });
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
index b6200cb..c268bfd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
@@ -133,8 +133,7 @@
   }
 
   private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
-    // NOTE: Never add a forwarding method to methods of classes unknown or coming from
-    // android.jar
+    // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
     DexMethod forwardMethod =
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 9bc1a8e..4feafe3 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,6 +4,8 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary;
 
+import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -561,6 +563,8 @@
         .setMethod(methodToInstall)
         .setAccessFlags(newFlags)
         .setCode(code)
+        .setApiLevelForDefinition(template.getApiLevelForDefinition())
+        .setApiLevelForCode(code == null ? NOT_SET : template.getApiLevelForCode())
         .build();
   }
 
@@ -620,6 +624,8 @@
     return DexEncodedField.syntheticBuilder()
         .setField(field)
         .setAccessFlags(fieldAccessFlags)
+        // The api level is computed when tracing.
+        .disableAndroidApiLevelCheck()
         .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 2b8e3c7..d32b3d3 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,6 +4,8 @@
 
 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,6 +804,8 @@
             .setAccessFlags(accessFlags)
             .setCode(
                 createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory))
+            .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
+            .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
             .build();
     addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index a2a2bf9..bb1a427 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfInitClass;
@@ -459,6 +460,8 @@
         .setAccessFlags(
             FieldAccessFlags.builder().setPackagePrivate().setStatic().setSynthetic().build())
         .setStaticValue(DexValueInt.DEFAULT)
+        // The api level is computed when tracing.
+        .disableAndroidApiLevelCheck()
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 7dc9de3..c110104 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -38,16 +38,9 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -66,14 +59,12 @@
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -920,61 +911,6 @@
         || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
   }
 
-  private Collection<CfInstruction> rewriteInvokeToThrowIR(
-      InvokeMethod invoke,
-      SingleResolutionResult resolutionResult,
-      IRCode code,
-      ListIterator<BasicBlock> blockIterator,
-      InstructionListIterator instructions,
-      Set<Value> affectedValues,
-      Set<BasicBlock> blocksToRemove,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext) {
-    MethodSynthesizerConsumer methodSynthesizerConsumer;
-    if (resolutionResult == null) {
-      methodSynthesizerConsumer =
-          UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
-    } else if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
-      methodSynthesizerConsumer =
-          UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
-    } else {
-      assert false;
-      return null;
-    }
-
-    // Replace by throw new SomeException.
-    UtilityMethodForCodeOptimizations throwMethod =
-        methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
-    throwMethod.optimize(methodProcessor);
-
-    InvokeStatic throwInvoke =
-        InvokeStatic.builder()
-            .setMethod(throwMethod.getMethod())
-            .setFreshOutValue(appView, code)
-            .setPosition(invoke)
-            .build();
-    instructions.previous();
-
-    // Split the block before the invoke instruction, and position the block iterator at the newly
-    // created throw block (this involves rewinding the block iterator back over the blocks created
-    // as a result of critical edge splitting, if any).
-    BasicBlock throwBlock = instructions.splitCopyCatchHandlers(code, blockIterator, options);
-    IteratorUtils.previousUntil(blockIterator, block -> block == throwBlock);
-    blockIterator.next();
-
-    // Insert the `SomeException e = throwSomeException()` invoke before the goto
-    // instruction.
-    instructions.previous();
-    instructions.add(throwInvoke);
-
-    // Insert the `throw e` instruction in the newly created throw block.
-    InstructionListIterator throwBlockIterator = throwBlock.listIterator(code);
-    throwBlockIterator.next();
-    throwBlockIterator.replaceCurrentInstructionWithThrow(
-        appView, code, blockIterator, throwInvoke.outValue(), blocksToRemove, affectedValues);
-    return null;
-  }
-
   private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
     // Here we try to avoid doing the expensive look-up on all invokes.
     if (!emulatedMethods.contains(invokedMethod.name)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 627f8af..294dbb8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -290,6 +290,8 @@
             .setMethod(method)
             .setAccessFlags(methodAccessFlags)
             .setCode(null)
+            // Will be traced by the enqueuer.
+            .disableAndroidApiLevelCheck()
             .build();
     encodedMethod.setCode(provider.generateCfCode(), appView);
     return new ProgramMethod(clazz, encodedMethod);
@@ -587,6 +589,8 @@
             .setMethod(factory.recordMembers.init)
             .setAccessFlags(methodAccessFlags)
             .setCode(null)
+            // Will be traced by the enqueuer.
+            .disableAndroidApiLevelCheck()
             .build();
     init.setCode(
         new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode(), appView);
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 2c012f0..27fa69a 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,6 +5,7 @@
 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;
@@ -38,6 +39,9 @@
                       .setAccessFlags(
                           FieldAccessFlags.fromCfAccessFlags(
                               Constants.ACC_PRIVATE | Constants.ACC_FINAL))
+                      .setApiLevel(minApiLevelIfEnabledOrUnknown(appView))
+                      // Will be traced by the enqueuer.
+                      .disableAndroidApiLevelCheck()
                       .build());
             }
           });
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
index 076d4da..1e04b54 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
@@ -10,7 +10,7 @@
 @Keep
 public interface RetraceSourceFileResult {
 
-  boolean isSynthesized();
+  boolean isInferred();
 
   String getFilename();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceSourceFileResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceSourceFileResultImpl.java
index b84b1a6..7595d69 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceSourceFileResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceSourceFileResultImpl.java
@@ -17,7 +17,7 @@
   }
 
   @Override
-  public boolean isSynthesized() {
+  public boolean isInferred() {
     return synthesized;
   }
 
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 895e610..8e8533f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3051,6 +3051,8 @@
     DexClassAndMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
     if (target == null) {
       failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
+      analyses.forEach(
+          analyses -> analyses.notifyFailedMethodResolutionTarget(resolution.getResolvedMethod()));
       return;
     }
 
@@ -3117,9 +3119,7 @@
         && appView.options().getProguardConfiguration().getKeepAttributes().signature) {
       registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
     }
-    if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-      registerAnalysis(new ApiModelAnalysis(appView, apiReferenceLevelCache));
-    }
+    registerAnalysis(new ApiModelAnalysis(appView, apiReferenceLevelCache));
 
     // Transfer the minimum keep info from the root set into the Enqueuer state.
     includeMinimumKeepInfo(rootSet);
@@ -3482,7 +3482,10 @@
   private boolean addToPendingDesugaring(ProgramMethod method) {
     if (options.isInterfaceMethodDesugaringEnabled()) {
       if (mustMoveToInterfaceCompanionMethod(method)) {
-        pendingMethodMove.add(method);
+        // TODO(b/199043500): Once "live moved methods" are tracked this can avoid the code check.
+        if (!InvalidCode.isInvalidCode(method.getDefinition().getCode())) {
+          pendingMethodMove.add(method);
+        }
         return true;
       }
       ProgramMethod nonMovedMethod = pendingMethodMoveInverse.get(method);
@@ -3552,6 +3555,7 @@
               .ensureMethodOfProgramCompanionClassStub(method, eventConsumer);
       interfaceProcessor.finalizeMoveToCompanionMethod(method, companion);
       pendingMethodMoveInverse.remove(companion);
+      // TODO(b/199043500): Once "live moved methods" are tracked this can be removed.
       if (!isMethodLive(companion)) {
         additions.addLiveMethod(companion);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 276d737..43d2d16 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
@@ -333,6 +334,11 @@
         // Private methods and static methods can only be targeted yet non-live as the result of
         // an invalid invoke. They will not actually be called at runtime but we have to keep them
         // as non-abstract (see above) to produce the same failure mode.
+        if (!allowAbstract) {
+          // If the method was not marked as live and we cannot make it abstract, set the api level
+          // to be min or unknown.
+          method.setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView));
+        }
         reachableMethods.add(
             allowAbstract ? method.toAbstractMethod() : method.toEmptyThrowingMethod(options));
       } else {
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 6e06948..8128db2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1449,8 +1449,8 @@
               .setClassFileVersion(classFileVersion)
               .setApiLevelForDefinition(method.getApiLevelForDefinition())
               .setApiLevelForCode(method.getApiLevelForDefinition())
+              .setIsLibraryMethodOverride(method.isLibraryMethodOverride())
               .build();
-      bridge.setLibraryMethodOverride(method.isLibraryMethodOverride());
       if (method.accessFlags.isPromotedToPublic()) {
         // The bridge is now the public method serving the role of the original method, and should
         // reflect that this method was publicized.
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 0e45bd8..e867c59 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.function.Consumer;
 
 public class SyntheticMethodBuilder {
@@ -116,8 +115,8 @@
             .setParameterAnnotations(parameterAnnotationsList)
             .setCode(code)
             .setClassFileVersion(classFileVersion)
-            .setApiLevelForDefinition(AndroidApiLevel.UNKNOWN)
-            .setApiLevelForCode(AndroidApiLevel.UNKNOWN)
+            // TODO(b/188388130): This should pass in api level directly.
+            .disableAndroidApiLevelCheck()
             .build();
     assert isValidSyntheticMethod(method, syntheticKind);
     if (onBuildConsumer != null) {
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 e127d6c..c2f78cf 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -42,7 +42,8 @@
   Q(29),
   R(30),
   S(31),
-  UNKNOWN(10000);
+  UNKNOWN(10000),
+  NOT_SET(10001);
 
   // 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.
@@ -77,8 +78,9 @@
   }
 
   public static AndroidApiLevel minApiLevelIfEnabledOrUnknown(AppView<?> appView) {
-    return appView.options().apiModelingOptions().enableApiCallerIdentification
-        ? appView.options().minApiLevel
+    InternalOptions options = appView.options();
+    return options.apiModelingOptions().enableApiCallerIdentification
+        ? options.minApiLevel
         : UNKNOWN;
   }
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index a457e50..ec57f95 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -869,6 +869,7 @@
       case J_MR2:
       case K_WATCH:
       case UNKNOWN:
+      case NOT_SET:
         return false;
       default:
         return true;
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
new file mode 100644
index 0000000..12ee318
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
@@ -0,0 +1,135 @@
+// 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BootstrapMethodNotFoundConstantDynamicTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final Class<?> MAIN_CLASS = A.class;
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+    assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForJvm()
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+  }
+
+  @Test
+  public void TestD8Cf() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .applyIf(
+            // When not desugaring the CF code requires JDK 11.
+            DesugarTestConfiguration::isNotDesugared,
+            r -> {
+              if (parameters.isCfRuntime()
+                  && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+                r.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+              } else {
+                r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+              }
+            })
+        .applyIf(
+            DesugarTestConfiguration::isDesugared,
+            r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(A.class)
+        // TODO(b/198142613): There should not be a warnings on class references which are
+        //  desugared away.
+        .applyIf(
+            parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+            b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+        // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> {
+              assertThrows(
+                  CompilationFailedException.class,
+                  () ->
+                      r.compileWithExpectedDiagnostics(
+                          diagnostics -> {
+                            diagnostics.assertOnlyErrors();
+                            diagnostics.assertErrorsMatch(
+                                diagnosticMessage(
+                                    containsString(
+                                        "Unsupported dynamic constant (not desugaring)")));
+                          }));
+            },
+            r ->
+                r.run(parameters.getRuntime(), MAIN_CLASS)
+                    .assertFailureWithErrorThatThrows(NoSuchMethodError.class));
+  }
+
+  private byte[] getTransformedClasses() throws IOException {
+    return transformer(A.class)
+        .setVersion(CfVersion.V11)
+        .transformConstStringToConstantDynamic(
+            "condy1", A.class, "methodNotFound", "constantName", Object.class)
+        .transform();
+  }
+
+  public static class A {
+
+    public static Object f() {
+      return "condy1"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static void main(String[] args) {
+      System.out.println(f() != null);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
new file mode 100644
index 0000000..2df349a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
@@ -0,0 +1,133 @@
+// 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BootstrapMethodPrivateConstantDynamicTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final Class<?> MAIN_CLASS = A.class;
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+    assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForJvm()
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void TestD8Cf() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .applyIf(
+            // When not desugaring the CF code requires JDK 11.
+            DesugarTestConfiguration::isNotDesugared,
+            r -> {
+              if (parameters.isCfRuntime()
+                  && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+                r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+              } else {
+                r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+              }
+            },
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(A.class)
+        // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> {
+              assertThrows(
+                  CompilationFailedException.class,
+                  () ->
+                      r.compileWithExpectedDiagnostics(
+                          diagnostics -> {
+                            diagnostics.assertOnlyErrors();
+                            diagnostics.assertErrorsMatch(
+                                diagnosticMessage(
+                                    containsString(
+                                        "Unsupported dynamic constant (not desugaring)")));
+                          }));
+            },
+            r ->
+                r.run(parameters.getRuntime(), MAIN_CLASS)
+                    .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  private byte[] getTransformedClasses() throws IOException {
+    return transformer(A.class)
+        .setVersion(CfVersion.V11)
+        .transformConstStringToConstantDynamic(
+            "condy1", A.class, "myConstant", "constantName", Object.class)
+        .transform();
+  }
+
+  public static class A {
+
+    public static Object f() {
+      return "condy1"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static void main(String[] args) {
+      System.out.println(f() != null);
+    }
+
+    private Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+      return new Object();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
new file mode 100644
index 0000000..52bd722
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
@@ -0,0 +1,133 @@
+// 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BootstrapMethodVirtualConstantDynamicTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final Class<?> MAIN_CLASS = A.class;
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+    assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForJvm()
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void TestD8Cf() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .applyIf(
+            // When not desugaring the CF code requires JDK 11.
+            DesugarTestConfiguration::isNotDesugared,
+            r -> {
+              if (parameters.isCfRuntime()
+                  && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+                r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+              } else {
+                r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+              }
+            },
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(A.class)
+        // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> {
+              assertThrows(
+                  CompilationFailedException.class,
+                  () ->
+                      r.compileWithExpectedDiagnostics(
+                          diagnostics -> {
+                            diagnostics.assertOnlyErrors();
+                            diagnostics.assertErrorsMatch(
+                                diagnosticMessage(
+                                    containsString(
+                                        "Unsupported dynamic constant (not desugaring)")));
+                          }));
+            },
+            r ->
+                r.run(parameters.getRuntime(), MAIN_CLASS)
+                    .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  private byte[] getTransformedClasses() throws IOException {
+    return transformer(A.class)
+        .setVersion(CfVersion.V11)
+        .transformConstStringToConstantDynamic(
+            "condy1", A.class, "myConstant", "constantName", Object.class)
+        .transform();
+  }
+
+  public static class A {
+
+    public static Object f() {
+      return "condy1"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static void main(String[] args) {
+      System.out.println(f() != null);
+    }
+
+    public Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
+      return new Object();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
new file mode 100644
index 0000000..3c54e23
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
@@ -0,0 +1,168 @@
+// 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MultipleBootstrapMethodConstantDynamicTest extends TestBase {
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines("true", "true", "true", "true", "true");
+  private static final Class<?> MAIN_CLASS = A.class;
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+    assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForJvm()
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void TestD8Cf() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .applyIf(
+            // When not desugaring the CF code requires JDK 11.
+            DesugarTestConfiguration::isNotDesugared,
+            r -> {
+              if (parameters.isCfRuntime()
+                  && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+                r.assertSuccessWithOutput(EXPECTED_OUTPUT);
+              } else {
+                r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+              }
+            })
+        .applyIf(
+            DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(A.class)
+        // TODO(b/198142613): There should not be a warnings on class references which are
+        //  desugared away.
+        .applyIf(
+            parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+            b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
+        // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> {
+              assertThrows(
+                  CompilationFailedException.class,
+                  () ->
+                      r.compileWithExpectedDiagnostics(
+                          diagnostics -> {
+                            diagnostics.assertOnlyErrors();
+                            diagnostics.assertErrorsMatch(
+                                diagnosticMessage(
+                                    containsString(
+                                        "Unsupported dynamic constant (not desugaring)")));
+                          }));
+            },
+            r ->
+                r.run(parameters.getRuntime(), MAIN_CLASS)
+                    .assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  private byte[] getTransformedClasses() throws IOException {
+    return transformer(A.class)
+        .setVersion(CfVersion.V11)
+        .transformConstStringToConstantDynamic(
+            "condy1", A.class, "myConstant1", "constantName", Object.class)
+        .transformConstStringToConstantDynamic(
+            "condy2", A.class, "myConstant1", "constantName", Object.class)
+        .transformConstStringToConstantDynamic(
+            "condy3", A.class, "myConstant2", "constantName", Object.class)
+        .transformConstStringToConstantDynamic(
+            "condy4", A.class, "myConstant2", "constantName", Object.class)
+        .transform();
+  }
+
+  public static class A {
+
+    public static Object f1() {
+      return "condy1"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static Object f2() {
+      return "condy2"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static Object g1() {
+      return "condy3"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static Object g2() {
+      return "condy4"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    public static void main(String[] args) {
+      System.out.println(f1() != null);
+      System.out.println(f1() == f2());
+      System.out.println(g1() != null);
+      System.out.println(g1() == g2());
+      System.out.println(f1() != g2());
+    }
+
+    private static Object myConstant1(MethodHandles.Lookup lookup, String name, Class<?> type) {
+      return new Object();
+    }
+
+    private static Object myConstant2(MethodHandles.Lookup lookup, String name, Class<?> type) {
+      return new Object();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 5e1b8c0..905529a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -126,7 +126,7 @@
             .parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
 
     for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
-      if (apiLevel == AndroidApiLevel.UNKNOWN) {
+      if (apiLevel == AndroidApiLevel.UNKNOWN || apiLevel == AndroidApiLevel.NOT_SET) {
         continue;
       }
       Path compileApiLevelDirectory = directory.resolve("compile_api_level_" + apiLevel.getLevel());
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index afc7a62..ce5a517 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -156,6 +156,7 @@
             .setAccessFlags(flags)
             .setCode(code)
             .disableMethodNotNullCheck()
+            .disableAndroidApiLevelCheck()
             .build();
     return new JumboStringRewriter(method, string, factory).rewrite();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index d8e2db4..a24f51f 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -55,6 +55,7 @@
                 .setMethod(signature)
                 .setAccessFlags(MethodAccessFlags.fromDexAccessFlags(0))
                 .setCode(null)
+                .disableAndroidApiLevelCheck()
                 .build());
     return new Node(method);
   }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index be4f6de..04921e2 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -864,6 +864,7 @@
                 .setMethod(voidReturnMethod)
                 .setAccessFlags(access)
                 .setCode(code)
+                .disableAndroidApiLevelCheck()
                 .build();
         ProgramMethod programMethod = new ProgramMethod(programClass, method);
         IRCode ir = code.buildIR(programMethod, appView, Origin.unknown());
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
index 6d9c328..4b8f03f 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
@@ -7,6 +7,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
@@ -15,6 +16,7 @@
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -79,15 +81,31 @@
   public void runCheckedInBinaryJar() throws Exception {
     // The retrace jar is only built when building r8lib.
     Path jar = ToolHelper.isTestingR8Lib() ? ToolHelper.R8_RETRACE_JAR : ToolHelper.R8_JAR;
-    for (CfRuntime cfRuntime : CfRuntime.getCheckedInCfRuntimes()) {
-      RetraceApiTestHelper.runJunitOnTests(
-          cfRuntime,
-          jar,
-          BINARY_COMPATIBILITY_JAR,
-          RetraceApiTestHelper.getBinaryCompatibilityTests());
-    }
+    RetraceApiTestHelper.runJunitOnTests(
+        CfRuntime.getSystemRuntime(),
+        jar,
+        BINARY_COMPATIBILITY_JAR,
+        RetraceApiTestHelper.getBinaryCompatibilityTests());
   }
 
+  @Test
+  public void runCheckedInWithNonExistingTest() {
+    Path jar = ToolHelper.isTestingR8Lib() ? ToolHelper.R8_RETRACE_JAR : ToolHelper.R8_JAR;
+    assertThrows(
+        AssertionError.class,
+        () -> {
+          RetraceApiTestHelper.runJunitOnTests(
+              CfRuntime.getSystemRuntime(),
+              jar,
+              BINARY_COMPATIBILITY_JAR,
+              ImmutableList.of(new RetraceApiBinaryTest() {}.getClass()));
+        });
+  }
+
+  /**
+   * To produce a new tests.jar run the code below. This will generate a new jar overwriting the
+   * existing one. Remember to upload to cloud storage afterwards.
+   */
   public static void main(String[] args) throws Exception {
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java
new file mode 100644
index 0000000..e35be6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInferSourceFileTest.java
@@ -0,0 +1,52 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceSourceFileResult;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiInferSourceFileTest extends RetraceApiTestBase {
+
+  public RetraceApiInferSourceFileTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "some.Class -> a:\n"
+            + "  1:3:int strawberry(int):99:101 -> s\n"
+            + "  4:5:int mango(float):121:122 -> s";
+
+    @Test
+    public void testRetracingSourceFile() {
+      List<RetraceSourceFileResult> sourceFileResults = new ArrayList<>();
+      Retracer.createDefault(ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+          .retraceClass(Reference.classFromTypeName("a"))
+          .forEach(clazz -> sourceFileResults.add(clazz.retraceSourceFile("")));
+      assertEquals(1, sourceFileResults.size());
+      assertTrue(sourceFileResults.get(0).isInferred());
+      assertEquals("Class.java", sourceFileResults.get(0).getFilename());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSourceFileTest.java
new file mode 100644
index 0000000..f75ea42
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSourceFileTest.java
@@ -0,0 +1,53 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceSourceFileResult;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSourceFileTest extends RetraceApiTestBase {
+
+  public RetraceApiSourceFileTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "some.Class -> a:\n"
+            + " # {'id':'sourceFile','fileName':'SomeFileName.kt'}\n"
+            + "  1:3:int strawberry(int):99:101 -> s\n"
+            + "  4:5:int mango(float):121:122 -> s\n";
+
+    @Test
+    public void testRetracingSourceFile() {
+      List<RetraceSourceFileResult> sourceFileResults = new ArrayList<>();
+      Retracer.createDefault(ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+          .retraceClass(Reference.classFromTypeName("a"))
+          .forEach(clazz -> sourceFileResults.add(clazz.retraceSourceFile("")));
+      assertEquals(1, sourceFileResults.size());
+      assertFalse(sourceFileResults.get(0).isInferred());
+      assertEquals("SomeFileName.kt", sourceFileResults.get(0).getFilename());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedClassTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedClassTest.java
new file mode 100644
index 0000000..6b6e3fd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedClassTest.java
@@ -0,0 +1,55 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceClassElement;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSynthesizedClassTest extends RetraceApiTestBase {
+
+  public RetraceApiSynthesizedClassTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+            + "some.Class -> a:\n"
+            + "# { id: 'com.android.tools.r8.synthesized' }\n"
+            + "  1:3:int strawberry(int):99:101 -> s\n"
+            + "  4:5:int mango(float):121:122 -> s";
+
+    @Test
+    public void testSyntheticClass() {
+      List<RetraceClassElement> classResults =
+          Retracer.createDefault(
+                  ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+              .retraceClass(Reference.classFromTypeName("a"))
+              .stream()
+              .collect(Collectors.toList());
+      assertEquals(1, classResults.size());
+      assertTrue(classResults.get(0).isCompilerSynthesized());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFieldTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFieldTest.java
new file mode 100644
index 0000000..d934a5f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFieldTest.java
@@ -0,0 +1,56 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceFieldElement;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSynthesizedFieldTest extends RetraceApiTestBase {
+
+  public RetraceApiSynthesizedFieldTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+            + "some.Class -> a:\n"
+            + "  int foo -> a\n"
+            + "  # { id: 'com.android.tools.r8.synthesized' }";
+
+    @Test
+    public void testSyntheticClass() {
+      List<RetraceFieldElement> fieldResults =
+          Retracer.createDefault(
+                  ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+              .retraceClass(Reference.classFromTypeName("a"))
+              .stream()
+              .flatMap(element -> element.lookupField("a").stream())
+              .collect(Collectors.toList());
+      assertEquals(1, fieldResults.size());
+      // TODO(b/172014416): Should report if synthesized.
+      assertThrows(RuntimeException.class, () -> fieldResults.get(0).isCompilerSynthesized());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
new file mode 100644
index 0000000..e7da9a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
@@ -0,0 +1,66 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSynthesizedFrameTest extends RetraceApiTestBase {
+
+  public RetraceApiSynthesizedFrameTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+            + "some.Class -> a:\n"
+            + "  3:3:int other.strawberry(int):101:101 -> a\n"
+            + "  3:3:int mango(float):28 -> a\n"
+            + "  # { id: 'com.android.tools.r8.synthesized' }";
+
+    @Test
+    public void testSyntheticClass() {
+      List<RetraceFrameElement> frameResults =
+          Retracer.createDefault(
+                  ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+              .retraceClass(Reference.classFromTypeName("a"))
+              .stream()
+              .flatMap(element -> element.lookupFrame("a", 3).stream())
+              .collect(Collectors.toList());
+      assertEquals(1, frameResults.size());
+      RetraceFrameElement retraceFrameElement = frameResults.get(0);
+      List<RetracedMethodReference> allFrames = new ArrayList<>();
+      retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
+      assertEquals(2, allFrames.size());
+      List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
+      retraceFrameElement.visitNonCompilerSynthesizedFrames(
+          (method, ignored) -> nonSyntheticFrames.add(method));
+      assertEquals(1, nonSyntheticFrames.size());
+      assertEquals(nonSyntheticFrames.get(0), allFrames.get(0));
+      assertEquals("mango", allFrames.get(1).getMethodName());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
new file mode 100644
index 0000000..96aa22d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
@@ -0,0 +1,64 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSynthesizedInnerFrameTest extends RetraceApiTestBase {
+
+  public RetraceApiSynthesizedInnerFrameTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+            + "some.Class -> a:\n"
+            + "  3:3:int other.strawberry(int):101:101 -> a\n"
+            + "  # { id: 'com.android.tools.r8.synthesized' }\n"
+            + "  3:3:int mango(float):28 -> a";
+
+    @Test
+    public void testSyntheticClass() {
+      List<RetraceFrameElement> frameResults =
+          Retracer.createDefault(
+                  ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+              .retraceClass(Reference.classFromTypeName("a"))
+              .stream()
+              .flatMap(element -> element.lookupFrame("a", 3).stream())
+              .collect(Collectors.toList());
+      assertEquals(1, frameResults.size());
+      RetraceFrameElement retraceFrameElement = frameResults.get(0);
+      List<RetracedMethodReference> allFrames = new ArrayList<>();
+      retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
+      assertEquals(2, allFrames.size());
+      List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
+      retraceFrameElement.visitNonCompilerSynthesizedFrames(
+          (method, ignored) -> nonSyntheticFrames.add(method));
+      assertEquals(allFrames, nonSyntheticFrames);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedMethodTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedMethodTest.java
new file mode 100644
index 0000000..39228e2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedMethodTest.java
@@ -0,0 +1,59 @@
+// 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceMethodElement;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiSynthesizedMethodTest extends RetraceApiTestBase {
+
+  public RetraceApiSynthesizedMethodTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  public static class ApiTest implements RetraceApiBinaryTest {
+
+    private final String mapping =
+        "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+            + "some.Class -> a:\n"
+            + "  void foo() -> a\n"
+            + "  # { id: 'com.android.tools.r8.synthesized' }";
+
+    @Test
+    public void testSyntheticClass() {
+      List<RetraceMethodElement> methodResults =
+          Retracer.createDefault(
+                  ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+              .retraceClass(Reference.classFromTypeName("a"))
+              .stream()
+              .flatMap(element -> element.lookupMethod("a").stream())
+              .collect(Collectors.toList());
+      assertEquals(1, methodResults.size());
+      RetraceMethodElement retraceMethodElement = methodResults.get(0);
+      assertFalse(retraceMethodElement.isUnknown());
+      // TODO(b/172014416): Should report if synthesized.
+      assertThrows(RuntimeException.class, () -> methodResults.get(0).isCompilerSynthesized());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
index 7f9834b..7fad751 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
@@ -16,13 +16,13 @@
 import com.android.tools.r8.transformers.ClassFileTransformer;
 import com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -34,7 +34,15 @@
   private static final String HAMCREST = "hamcrest-core-1.3.jar";
 
   public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_FOR_BINARY_COMPATIBILITY =
-      ImmutableList.of(RetraceApiEmptyTest.RetraceTest.class);
+      ImmutableList.of(
+          RetraceApiEmptyTest.RetraceTest.class,
+          RetraceApiSourceFileTest.ApiTest.class,
+          RetraceApiInferSourceFileTest.ApiTest.class,
+          RetraceApiSynthesizedClassTest.ApiTest.class,
+          RetraceApiSynthesizedFieldTest.ApiTest.class,
+          RetraceApiSynthesizedMethodTest.ApiTest.class,
+          RetraceApiSynthesizedFrameTest.ApiTest.class,
+          RetraceApiSynthesizedInnerFrameTest.ApiTest.class);
   public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
       ImmutableList.of();
 
@@ -57,12 +65,11 @@
       Collection<Class<? extends RetraceApiBinaryTest>> tests)
       throws Exception {
     List<Path> classPaths = ImmutableList.of(getJunitDependency(), getHamcrest(), r8Jar, testJar);
+    List<String> args = new ArrayList<>();
+    args.add("org.junit.runner.JUnitCore");
+    tests.forEach(test -> args.add(test.getTypeName()));
     ProcessResult processResult =
-        ToolHelper.runJava(
-            runtime,
-            classPaths,
-            "org.junit.runner.JUnitCore",
-            StringUtils.join(" ", tests, Class::getTypeName));
+        ToolHelper.runJava(runtime, classPaths, args.toArray(new String[0]));
     assertEquals(processResult.toString(), 0, processResult.exitCode);
     assertThat(processResult.stdout, containsString("OK (" + tests.size() + " test"));
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java b/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
index 7cb0d57..c7630ba 100644
--- a/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -96,7 +95,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public ConfigurationDebuggingTest(TestParameters parameters) {
@@ -111,7 +110,7 @@
             .addKeepRules("-addconfigurationdebugging")
             .addKeepRules("-keep class **.TestClass { <init>(); }")
             .noMinification()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(this::inspect)
             .writeToZip();
@@ -119,10 +118,10 @@
     R8FullTestBuilder builder =
         testForR8(parameters.getBackend())
             .addLibraryClasses(BaseClass.class, UninstantiatedClass.class, TestClass.class)
-            .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+            .addDefaultRuntimeLibrary(parameters)
             .addProgramClasses(Caller.class)
             .addKeepMainRule(Caller.class)
-            .setMinApi(parameters.getRuntime());
+            .setMinApi(parameters.getApiLevel());
     R8TestRunResult result =
         builder
             .compile()
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 3825d93..826dbae 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-63dd09b086d0d134219a379720c6b68a94afdf48
\ No newline at end of file
+bd5d0c9d87504c31d996be84ba3379838027acf6
\ No newline at end of file