Merge "Add tests for valid class files with name conflict."
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 8d087b6..8d5857a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -182,6 +182,14 @@
     }
   }
 
+  public void swapSuccessors(BasicBlock a, BasicBlock b) {
+    assert a != b;
+    int aIndex = successors.indexOf(a);
+    int bIndex = successors.indexOf(b);
+    assert aIndex >= 0 && bIndex >= 0;
+    swapSuccessorsByIndex(aIndex, bIndex);
+  }
+
   public void swapSuccessorsByIndex(int index1, int index2) {
     assert index1 != index2;
     if (hasCatchHandlers()) {
@@ -1053,20 +1061,25 @@
     return hare;
   }
 
-  public boolean isSimpleAlwaysThrowingPath() {
+  public boolean isSimpleAlwaysThrowingPath(boolean failOnMissingPosition) {
     // See Floyd's cycle-finding algorithm for reference.
     BasicBlock hare = this;
     BasicBlock tortuous = this;
     boolean advance = false;
     while (true) {
       List<BasicBlock> normalSuccessors = hare.getNormalSuccessors();
-      if (normalSuccessors.size() == 0) {
-        return hare.exit().isThrow();
-      }
       if (normalSuccessors.size() > 1) {
         return false;
       }
 
+      if (failOnMissingPosition && hasThrowingInstructionWithoutPosition(hare)) {
+        return false;
+      }
+
+      if (normalSuccessors.size() == 0) {
+        return hare.exit().isThrow();
+      }
+
       hare = normalSuccessors.get(0);
       tortuous = advance ? tortuous.getNormalSuccessors().get(0) : tortuous;
       advance = !advance;
@@ -1076,6 +1089,15 @@
     }
   }
 
+  private boolean hasThrowingInstructionWithoutPosition(BasicBlock hare) {
+    for (Instruction instruction : hare.instructions) {
+      if (instruction.instructionTypeCanThrow() && instruction.getPosition().isNone()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public Position getPosition() {
     BasicBlock block = endOfGotoChain();
     return block != null ? block.entry().getPosition() : Position.none();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 30dde25..769228d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1434,7 +1434,7 @@
           // and the dominator or the original block has catch handlers.
           continue;
         }
-        if (!dominator.isSimpleAlwaysThrowingPath()) {
+        if (!dominator.isSimpleAlwaysThrowingPath(false)) {
           // Only move string constants into blocks being part of simple
           // always throwing path.
           continue;
@@ -1874,11 +1874,16 @@
 
   public void simplifyIf(IRCode code, TypeEnvironment typeEnvironment) {
     int color = code.reserveMarkingColor();
+    boolean ifBranchFlipped = false;
     for (BasicBlock block : code.blocks) {
       if (block.isMarked(color)) {
         continue;
       }
       if (block.exit().isIf()) {
+        // Flip then/else branches if needed.
+        if (flipIfBranchesIfNeeded(block, code.hasDebugPositions)) {
+          ifBranchFlipped = true;
+        }
         // First rewrite zero comparison.
         rewriteIfWithConstZero(block);
 
@@ -1934,6 +1939,9 @@
     }
     code.removeMarkedBlocks(color);
     code.returnMarkingColor(color);
+    if (ifBranchFlipped) {
+      code.traceBlocks();
+    }
     assert code.isConsistentSSA();
   }
 
@@ -2199,6 +2207,28 @@
     }
   }
 
+  private boolean flipIfBranchesIfNeeded(BasicBlock block, boolean failOnMissingPosition) {
+    If theIf = block.exit().asIf();
+    BasicBlock trueTarget = theIf.getTrueTarget();
+    BasicBlock fallthrough = theIf.fallthroughBlock();
+    assert trueTarget != fallthrough;
+
+    if (!fallthrough.isSimpleAlwaysThrowingPath(failOnMissingPosition) ||
+        trueTarget.isSimpleAlwaysThrowingPath(failOnMissingPosition)) {
+      return false;
+    }
+
+    // In case fall-through block always trows there is a good chance that it
+    // is created for error checks and 'trueTarget' represents most more common
+    // non-error case. Flipping the if in this case may result in faster code
+    // on older Android versions.
+    List<Value> inValues = theIf.inValues();
+    If newIf = new If(theIf.getType().inverted(), inValues);
+    block.replaceLastInstruction(newIf);
+    block.swapSuccessors(trueTarget, fallthrough);
+    return true;
+  }
+
   public void rewriteLongCompareAndRequireNonNull(IRCode code, InternalOptions options) {
     if (options.canUseLongCompareAndObjectsNonNull()) {
       return;
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index b1b572f..db26fe6 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import java.io.IOException;
@@ -32,9 +33,10 @@
         exceptionClass);
   }
 
-  protected void ensureSameOutput(String main, int apiLevel, byte[]... classes) throws Exception {
+  protected void ensureSameOutput(String main, AndroidApiLevel apiLevel, byte[]... classes)
+      throws Exception {
     AndroidApp app = buildAndroidApp(classes);
-    Consumer<InternalOptions> setMinApiLevel = o -> o.minApiLevel = apiLevel;
+    Consumer<InternalOptions> setMinApiLevel = o -> o.minApiLevel = apiLevel.getLevel();
     ProcessResult javaResult = runOnJava(main, classes);
     ProcessResult d8Result = runOnArtRaw(compileWithD8(app, setMinApiLevel), main);
     ProcessResult r8Result = runOnArtRaw(compileWithR8(app, setMinApiLevel), main);
diff --git a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
index 4cde5d9..6d0ef5a 100644
--- a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
@@ -65,7 +65,8 @@
                     "--main-dex-list",
                     mainDexList.toString(),
                     "--lib",
-                    ToolHelper.getAndroidJar(minApiLevel).toString(),
+                    ToolHelper.getAndroidJar(
+                        AndroidApiLevel.getAndroidApiLevel(minApiLevel)).toString(),
                     "--classpath",
                     lib1.toString(),
                     "--classpath",
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 05d303c..caa1d48 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -49,8 +50,8 @@
     }
 
     @Override
-    D8IncrementalTestRunner withMinApiLevel(int minApiLevel) {
-      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    D8IncrementalTestRunner withMinApiLevel(AndroidApiLevel minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel.getLevel()));
     }
 
     @Override
@@ -175,8 +176,8 @@
       } else {
         throw new Unreachable("Unexpected output mode " + outputMode);
       }
-      addLibraryReference(builder, ToolHelper
-          .getAndroidJar(androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion));
+      addLibraryReference(builder, ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
       try {
         return ToolHelper.runD8(builder, this::combinedOptionConsumer);
       } catch (Unimplemented | CompilationError | InternalCompilerError re) {
@@ -324,7 +325,7 @@
 
   @Override
   protected Path buildDexThroughIntermediate(String packageName, Path input, OutputMode outputMode,
-      int minApi, String... mainDexClasses) throws Throwable {
+      AndroidApiLevel minApi, String... mainDexClasses) throws Throwable {
     // tests using this should already been skipped.
     throw new Unreachable();
   }
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 832a781..dc4a9b4 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -66,7 +66,7 @@
 
   @Test
   public void dexPerClassFileWithDesugaringAndFolderClasspath() throws Throwable {
-    int minAPILevel = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minAPILevel = AndroidApiLevel.K;
     Path inputFile =
         Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION);
     Path tmpClassesDir = temp.newFolder().toPath();
@@ -78,7 +78,7 @@
     {
       D8Command.Builder command =
           D8Command.builder()
-              .setMinApiLevel(minAPILevel)
+              .setMinApiLevel(minAPILevel.getLevel())
               .addLibraryFiles(androidJar)
               .addProgramFiles(inputFile);
 
@@ -98,7 +98,7 @@
     for (Path classFile : individualClassFiles) {
       D8Command.Builder builder =
           D8Command.builder()
-              .setMinApiLevel(minAPILevel)
+              .setMinApiLevel(minAPILevel.getLevel())
               .addLibraryFiles(androidJar)
               .addClasspathFiles(tmpClassesDir)
               .addProgramFiles(classFile);
@@ -111,7 +111,7 @@
               });
       individalDexes.add(individualResult.getDexProgramResourcesForTesting().get(0));
     }
-    AndroidApp mergedResult = mergeDexResources(minAPILevel, individalDexes);
+    AndroidApp mergedResult = mergeDexResources(minAPILevel.getLevel(), individalDexes);
 
     assertTrue(Arrays.equals(
         readResource(fullBuildResult.getDexProgramResourcesForTesting().get(0)),
diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index 905d209..e16ecd1 100644
--- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -22,8 +22,8 @@
 
     @Override
     void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
-      builder.addLibraryFiles(ToolHelper
-          .getAndroidJar(androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion));
+      builder.addLibraryFiles(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index edb73f7..b5802f4 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -27,8 +27,8 @@
     }
 
     @Override
-    D8TestRunner withMinApiLevel(int minApiLevel) {
-      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    D8TestRunner withMinApiLevel(AndroidApiLevel minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel.getLevel()));
     }
 
     D8TestRunner withClasspath(Path... classpath) {
@@ -43,7 +43,7 @@
       }
       builder.addLibraryFiles(
           ToolHelper.getAndroidJar(
-              androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion));
+              androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
       builder.addProgramFiles(inputFile);
       try {
         ToolHelper.runD8(builder, this::combinedOptionConsumer);
@@ -70,7 +70,7 @@
     D8TestRunner lib1 =
         test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A")
             .withInterfaceMethodDesugaring(OffOrAuto.Off)
-            .withMinApiLevel(AndroidApiLevel.K.getLevel());
+            .withMinApiLevel(AndroidApiLevel.K);
     try  {
       lib1.build();
 
@@ -87,7 +87,7 @@
     D8TestRunner lib1 =
         test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
             .withInterfaceMethodDesugaring(OffOrAuto.Auto)
-            .withMinApiLevel(AndroidApiLevel.K.getLevel());
+            .withMinApiLevel(AndroidApiLevel.K);
     lib1.build();
 
     // lib2: interface B extends A { default String foo() { return "B"; } }
@@ -96,7 +96,7 @@
         test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
             .withInterfaceMethodDesugaring(OffOrAuto.Auto)
             .withClasspath(lib1.getInputJar())
-            .withMinApiLevel(AndroidApiLevel.K.getLevel());
+            .withMinApiLevel(AndroidApiLevel.K);
     lib2.build();
 
     // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation
@@ -107,7 +107,7 @@
         test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A")
             .withInterfaceMethodDesugaring(OffOrAuto.Auto)
             .withClasspath(lib1.getInputJar())
-            .withMinApiLevel(AndroidApiLevel.K.getLevel());
+            .withMinApiLevel(AndroidApiLevel.K);
     test.build();
 
     // TODO check compilation warnings are correctly reported
@@ -116,7 +116,7 @@
 
   @Test
   public void testMissingInterfaceDesugared2AndroidK() throws Throwable {
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
 
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
@@ -170,7 +170,7 @@
 
   @Test
   public void testMissingInterfaceDesugared2AndroidO() throws Throwable {
-    int minApi = AndroidApiLevel.O.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.O;
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
         test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
@@ -218,7 +218,7 @@
   @Test
   public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable {
 
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
         test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
@@ -271,7 +271,7 @@
 
   @Test
   public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable {
-    int minApi = AndroidApiLevel.O.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.O;
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
         test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
@@ -318,7 +318,7 @@
 
   @Test
   public void testMissingSuperDesugaredAndroidK() throws Throwable {
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
 
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
@@ -366,7 +366,7 @@
 
   @Test
   public void testMissingSuperDesugaredAndroidO() throws Throwable {
-    int minApi = AndroidApiLevel.O.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.O;
 
     // lib1: interface A { default String foo() { return "A"; } }
     D8TestRunner lib1 =
@@ -416,7 +416,7 @@
 
   @Test
   public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable {
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
 
     // lib1: interface A { default String foo() { return "A"; } }
     //       interface A2 { default String foo2() { return "A2"; } }
@@ -461,7 +461,7 @@
 
   @Test
   public void testMissingSuperDesugaredWithClasspathCrossImplementationAndroidK() throws Throwable {
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
 
     // lib1: interface A { default String foo() { return "A"; } }
     //       interface A2 { default String foo2() { return "A2"; } }
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java
index b8e9675..0ae485a 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java
@@ -37,7 +37,7 @@
       }
       // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
       builder
-          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel()))
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
           .addProgramFiles(inputFile)
           .setOutput(out, OutputMode.DexIndexed);
       try {
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/D8RunExamplesJava9Test.java
index 71ffde2..ece7f31 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesJava9Test.java
@@ -37,7 +37,7 @@
       }
       // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
       builder
-          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()))
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
           .addProgramFiles(inputFile)
           .setOutput(out, OutputMode.DexIndexed);
       try {
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index 8e8ca36..6533e08 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.naming.ProguardMapSupplier;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.VersionProperties;
 import java.nio.file.Paths;
 import java.util.HashSet;
@@ -17,15 +18,15 @@
 public class ProguardMapMarkerTest {
   @Test
   public void proguardMapMarkerTest24() throws CompilationFailedException {
-    proguardMapMarkerTest(24);
+    proguardMapMarkerTest(AndroidApiLevel.N);
   }
 
   @Test
   public void proguardMapMarkerTest26() throws CompilationFailedException {
-    proguardMapMarkerTest(26);
+    proguardMapMarkerTest(AndroidApiLevel.O);
   }
 
-  private void proguardMapMarkerTest(int minApiLevel) throws CompilationFailedException {
+  private void proguardMapMarkerTest(AndroidApiLevel minApiLevel) throws CompilationFailedException {
     String classFile = ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
     R8.run(
         R8Command.builder()
@@ -43,10 +44,10 @@
                   public void finished(DiagnosticsHandler handler) {}
                 })
             .addLibraryFiles(ToolHelper.getAndroidJar(minApiLevel))
-            .setMinApiLevel(minApiLevel)
+            .setMinApiLevel(minApiLevel.getLevel())
             .setProguardMapConsumer(
                 (proguardMap, handler) -> {
-                  verifyMarkers(proguardMap, minApiLevel);
+                  verifyMarkers(proguardMap, minApiLevel.getLevel());
                 })
             .build());
   }
diff --git a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
index 59536de..28fe5dc 100644
--- a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
@@ -22,7 +22,7 @@
 
   static final Path JAR = Paths.get("tests", "r8_api_usage_sample.jar");
   static final String MAIN = "com.android.tools.apiusagesample.R8ApiUsageSample";
-  static final int MIN_API = AndroidApiLevel.K.getLevel();
+  static final AndroidApiLevel MIN_API = AndroidApiLevel.K;
 
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
@@ -54,7 +54,7 @@
                     "--output",
                     temp.newFolder().toString(),
                     "--min-api",
-                    Integer.toString(MIN_API),
+                    Integer.toString(MIN_API.getLevel()),
                     "--pg-conf",
                     pgConf.toString(),
                     "--main-dex-rules",
diff --git a/src/test/java/com/android/tools/r8/R8IgnoreMissingClassesTest.java b/src/test/java/com/android/tools/r8/R8IgnoreMissingClassesTest.java
index 697158a..83bc302 100644
--- a/src/test/java/com/android/tools/r8/R8IgnoreMissingClassesTest.java
+++ b/src/test/java/com/android/tools/r8/R8IgnoreMissingClassesTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.origin.EmbeddedOrigin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
@@ -11,14 +12,14 @@
 
 public class R8IgnoreMissingClassesTest {
 
-  private static final int MIN_API = 26;
+  private static final AndroidApiLevel MIN_API = AndroidApiLevel.O;
   private static final Path EXAMPLE = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "usestdlib.jar");
   private static final Path LIBRARY = ToolHelper.getAndroidJar(MIN_API);
 
   private R8Command.Builder config() {
     return R8Command.builder()
         .addProgramFiles(EXAMPLE)
-        .setMinApiLevel(MIN_API)
+        .setMinApiLevel(MIN_API.getLevel())
         .setProgramConsumer(DexIndexedConsumer.emptyConsumer());
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 2278d67..8cd4323 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -105,46 +105,46 @@
       "third_party/gradle/gradle/lib/plugins/hamcrest-core-1.3.jar";
 
   // Test that required to set min-api to a specific value.
-  private static Map<String, Integer> needMinSdkVersion =
-      new ImmutableMap.Builder<String, Integer>()
+  private static Map<String, AndroidApiLevel> needMinSdkVersion =
+      new ImmutableMap.Builder<String, AndroidApiLevel>()
           // Android O
-          .put("952-invoke-custom", AndroidApiLevel.O.getLevel())
-          .put("952-invoke-custom-kinds", AndroidApiLevel.O.getLevel())
-          .put("953-invoke-polymorphic-compiler", AndroidApiLevel.O.getLevel())
-          .put("957-methodhandle-transforms", AndroidApiLevel.O.getLevel())
-          .put("958-methodhandle-stackframe", AndroidApiLevel.O.getLevel())
-          .put("959-invoke-polymorphic-accessors", AndroidApiLevel.O.getLevel())
-          .put("979-const-method-handle", AndroidApiLevel.P.getLevel())
-          .put("990-method-handle-and-mr", AndroidApiLevel.O.getLevel())
+          .put("952-invoke-custom", AndroidApiLevel.O)
+          .put("952-invoke-custom-kinds", AndroidApiLevel.O)
+          .put("953-invoke-polymorphic-compiler", AndroidApiLevel.O)
+          .put("957-methodhandle-transforms", AndroidApiLevel.O)
+          .put("958-methodhandle-stackframe", AndroidApiLevel.O)
+          .put("959-invoke-polymorphic-accessors", AndroidApiLevel.O)
+          .put("979-const-method-handle", AndroidApiLevel.P)
+          .put("990-method-handle-and-mr", AndroidApiLevel.O)
           // Test intentionally asserts presence of bridge default methods desugar removes.
-          .put("044-proxy", AndroidApiLevel.N.getLevel())
+          .put("044-proxy", AndroidApiLevel.N)
           // Test intentionally asserts absence of default interface method in a class.
-          .put("048-reflect-v8", AndroidApiLevel.N.getLevel())
+          .put("048-reflect-v8", AndroidApiLevel.N)
           // Uses default interface methods.
-          .put("162-method-resolution", AndroidApiLevel.N.getLevel())
-          .put("616-cha-interface-default", AndroidApiLevel.N.getLevel())
-          .put("1910-transform-with-default", AndroidApiLevel.N.getLevel())
+          .put("162-method-resolution", AndroidApiLevel.N)
+          .put("616-cha-interface-default", AndroidApiLevel.N)
+          .put("1910-transform-with-default", AndroidApiLevel.N)
           // Interface initializer is not triggered after desugaring.
-          .put("962-iface-static", AndroidApiLevel.N.getLevel())
+          .put("962-iface-static", AndroidApiLevel.N)
           // Interface initializer is not triggered after desugaring.
-          .put("964-default-iface-init-gen", AndroidApiLevel.N.getLevel())
+          .put("964-default-iface-init-gen", AndroidApiLevel.N)
           // AbstractMethodError (for method not implemented in class) instead of
           // IncompatibleClassChangeError (for conflict of default interface methods).
-          .put("968-default-partial-compile-gen", AndroidApiLevel.N.getLevel())
+          .put("968-default-partial-compile-gen", AndroidApiLevel.N)
           // NoClassDefFoundError (for companion class) instead of NoSuchMethodError.
-          .put("970-iface-super-resolution-gen", AndroidApiLevel.N.getLevel())
+          .put("970-iface-super-resolution-gen", AndroidApiLevel.N)
           // NoClassDefFoundError (for companion class) instead of AbstractMethodError.
-          .put("971-iface-super", AndroidApiLevel.N.getLevel())
+          .put("971-iface-super", AndroidApiLevel.N)
           // Test for miranda methods is not relevant for desugaring scenario.
-          .put("972-default-imt-collision", AndroidApiLevel.N.getLevel())
+          .put("972-default-imt-collision", AndroidApiLevel.N)
           // Uses default interface methods.
-          .put("972-iface-super-multidex", AndroidApiLevel.N.getLevel())
+          .put("972-iface-super-multidex", AndroidApiLevel.N)
           // java.util.Objects is missing and test has default methods.
-          .put("973-default-multidex", AndroidApiLevel.N.getLevel())
+          .put("973-default-multidex", AndroidApiLevel.N)
           // a.klass.that.does.not.Exist is missing and test has default methods.
-          .put("974-verify-interface-super", AndroidApiLevel.N.getLevel())
+          .put("974-verify-interface-super", AndroidApiLevel.N)
           // Desugaring of interface private methods is not yet supported.
-          .put("975-iface-private", AndroidApiLevel.N.getLevel())
+          .put("975-iface-private", AndroidApiLevel.N)
           .build();
 
   // Tests that timeout when run with Art.
@@ -1390,13 +1390,13 @@
                 .setMode(mode)
                 .addProgramFiles(ListUtils.map(fileNames, Paths::get))
                 .setOutput(Paths.get(resultPath), OutputMode.DexIndexed);
-        Integer minSdkVersion = needMinSdkVersion.get(name);
+        AndroidApiLevel minSdkVersion = needMinSdkVersion.get(name);
         if (minSdkVersion != null) {
-          builder.setMinApiLevel(minSdkVersion);
+          builder.setMinApiLevel(minSdkVersion.getLevel());
           builder.addLibraryFiles(ToolHelper.getAndroidJar(minSdkVersion));
         } else {
           builder
-              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.getDefault().getLevel()));
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.getDefault()));
         }
         D8.run(builder.build());
         break;
@@ -1409,12 +1409,12 @@
                   .setOutput(Paths.get(resultPath), OutputMode.DexIndexed);
           // Add program files directly to the underlying app to avoid errors on DEX inputs.
           ToolHelper.getAppBuilder(builder).addProgramFiles(ListUtils.map(fileNames, Paths::get));
-          Integer minSdkVersion = needMinSdkVersion.get(name);
+          AndroidApiLevel minSdkVersion = needMinSdkVersion.get(name);
           if (minSdkVersion != null) {
-            builder.setMinApiLevel(minSdkVersion);
+            builder.setMinApiLevel(minSdkVersion.getLevel());
             ToolHelper.addFilteredAndroidJar(builder, minSdkVersion);
           } else {
-            ToolHelper.addFilteredAndroidJar(builder, AndroidApiLevel.getDefault().getLevel());
+            ToolHelper.addFilteredAndroidJar(builder, AndroidApiLevel.getDefault());
           }
           if (keepRulesFile != null) {
             builder.addProguardConfigurationFiles(Paths.get(keepRulesFile));
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 751c34e..b6b9ba8 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -48,7 +48,7 @@
   @Test
   public void invokeCustomWithShrinking() throws Throwable {
     test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
-        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.O)
         .withBuilderTransformation(builder ->
             builder.addProguardConfigurationFiles(
                 Paths.get(ToolHelper.EXAMPLES_ANDROID_O_DIR, "invokecustom/keep-rules.txt")))
@@ -62,8 +62,8 @@
     }
 
     @Override
-    R8TestRunner withMinApiLevel(int minApiLevel) {
-      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    R8TestRunner withMinApiLevel(AndroidApiLevel minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel.getLevel()));
     }
 
     @Override
@@ -72,8 +72,8 @@
       for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
         builder = transformation.apply(builder);
       }
-      builder.addLibraryFiles(ToolHelper
-          .getAndroidJar(androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion));
+      builder.addLibraryFiles(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
       R8Command command = builder.addProgramFiles(inputFile).build();
       ToolHelper.runR8(command, this::combinedOptionConsumer);
     }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
index becfa6d..f4665a9 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -72,7 +72,7 @@
         builder = transformation.apply(builder);
       }
       // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
-      builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel()));
+      builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O));
       R8Command command =
           builder.addProgramFiles(inputFile).setOutput(out, OutputMode.DexIndexed).build();
       ToolHelper.runR8(command, this::combinedOptionConsumer);
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/R8RunExamplesJava9Test.java
index 1a307a5..670e6be 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesJava9Test.java
@@ -28,7 +28,7 @@
         builder = transformation.apply(builder);
       }
       // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
-      builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+      builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P));
       R8Command command =
           builder.addProgramFiles(inputFile).setOutput(out, OutputMode.DexIndexed).build();
       ToolHelper.runR8(command, this::combinedOptionConsumer);
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index fa507b0..1bbcfdc 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -57,7 +57,7 @@
     final String packageName;
     final String mainClass;
 
-    Integer androidJarVersion = null;
+    AndroidApiLevel androidJarVersion = null;
 
     final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
     final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
@@ -162,9 +162,9 @@
       execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out});
     }
 
-    abstract C withMinApiLevel(int minApiLevel);
+    abstract C withMinApiLevel(AndroidApiLevel minApiLevel);
 
-    C withAndroidJar(int androidJarVersion) {
+    C withAndroidJar(AndroidApiLevel androidJarVersion) {
       assert this.androidJarVersion == null;
       this.androidJarVersion = androidJarVersion;
       return self();
@@ -291,56 +291,56 @@
   @Test
   public void stringConcat() throws Throwable {
     test("stringconcat", "stringconcat", "StringConcat")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
         .run();
   }
 
   @Test
   public void invokeCustom() throws Throwable {
     test("invokecustom", "invokecustom", "InvokeCustom")
-        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.O)
         .run();
   }
 
   @Test
   public void invokeCustom2() throws Throwable {
     test("invokecustom2", "invokecustom2", "InvokeCustom")
-        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.O)
         .run();
   }
 
   @Test
   public void invokeCustomErrorDueToMinSdk() throws Throwable {
     test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
-        .withMinApiLevel(25)
+        .withMinApiLevel(AndroidApiLevel.N_MR1)
         .run();
   }
 
   @Test
   public void invokePolymorphic() throws Throwable {
     test("invokepolymorphic", "invokepolymorphic", "InvokePolymorphic")
-        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.O)
         .run();
   }
 
   @Test
   public void invokePolymorphicErrorDueToMinSdk() throws Throwable {
     test("invokepolymorphic-error-due-to-min-sdk", "invokepolymorphic", "InvokePolymorphic")
-        .withMinApiLevel(25)
+        .withMinApiLevel(AndroidApiLevel.N_MR1)
         .run();
   }
 
   @Test
   public void lambdaDesugaring() throws Throwable {
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
         .run();
   }
 
   @Test
   public void lambdaDesugaringNPlus() throws Throwable {
     test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
         .withInterfaceMethodDesugaring(OffOrAuto.Auto)
         .run();
   }
@@ -348,8 +348,8 @@
   @Test
   public void desugarDefaultMethodInAndroidJar25() throws Throwable {
     test("DefaultMethodInAndroidJar25", "desugaringwithandroidjar25", "DefaultMethodInAndroidJar25")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
-        .withAndroidJar(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withAndroidJar(AndroidApiLevel.O)
         .withInterfaceMethodDesugaring(OffOrAuto.Auto)
         .run();
   }
@@ -357,8 +357,8 @@
   @Test
   public void desugarStaticMethodInAndroidJar25() throws Throwable {
     test("StaticMethodInAndroidJar25", "desugaringwithandroidjar25", "StaticMethodInAndroidJar25")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
-        .withAndroidJar(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withAndroidJar(AndroidApiLevel.O)
         .withInterfaceMethodDesugaring(OffOrAuto.Auto)
         .run();
   }
@@ -366,14 +366,14 @@
   @Test
   public void lambdaDesugaringValueAdjustments() throws Throwable {
     test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
-        .withMinApiLevel(AndroidApiLevel.K.getLevel())
+        .withMinApiLevel(AndroidApiLevel.K)
         .run();
   }
 
   @Test
   public void paramNames() throws Throwable {
     test("paramnames", "paramnames", "ParameterNames")
-        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .withMinApiLevel(AndroidApiLevel.O)
         .run();
   }
 
@@ -469,7 +469,7 @@
       int expectedMainDexListSize,
       String... mainDexClasses)
       throws Throwable {
-    int minApi = AndroidApiLevel.K.getLevel();
+    AndroidApiLevel minApi = AndroidApiLevel.K;
 
     // Full build, will be used as reference.
     TestRunner<?> full =
@@ -514,7 +514,7 @@
       String packageName,
       Path input,
       OutputMode outputMode,
-      int minApi,
+      AndroidApiLevel minApi,
       String... mainDexClasses)
       throws Throwable {
     Path intermediateDex =
@@ -522,7 +522,7 @@
     // Build intermediate with D8.
     D8Command.Builder command = D8Command.builder()
         .setOutput(intermediateDex, outputMode)
-        .setMinApiLevel(minApi)
+        .setMinApiLevel(minApi.getLevel())
         .addLibraryFiles(ToolHelper.getAndroidJar(minApi))
         .setIntermediate(true)
         .addProgramFiles(input);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 500b55c..77f9ad9 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -113,9 +113,7 @@
 
   protected static AndroidApp readClassesAndAndriodJar(List<Class> programClasses)
       throws IOException {
-    return readClassesAndAndriodJar(programClasses,
-        AndroidApiLevel.getAndroidApiLevel(
-            ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm())));
+    return readClassesAndAndriodJar(programClasses, ToolHelper.getMinApiLevelForDexVm());
   }
 
   protected static AndroidApp readClassesAndAndriodJar(
@@ -125,7 +123,7 @@
     for (Class clazz : programClasses) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
     }
-    builder.addLibraryFiles(ToolHelper.getAndroidJar(androidLibrary.getLevel()));
+    builder.addLibraryFiles(ToolHelper.getAndroidJar(androidLibrary));
     return builder.build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 83cc931..d9f10ae 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -88,7 +88,7 @@
   public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
 
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
-  private static final int DEFAULT_MIN_SDK = AndroidApiLevel.I.getLevel();
+  private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
 
   private static final String PROGUARD = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
 
@@ -483,32 +483,36 @@
   }
 
   public static Path getDefaultAndroidJar() {
-    return getAndroidJar(AndroidApiLevel.getDefault().getLevel());
+    return getAndroidJar(AndroidApiLevel.getDefault());
   }
 
-  public static Path getAndroidJar(int minSdkVersion) {
-    if (minSdkVersion == AndroidApiLevel.P.getLevel()) {
+  public static Path getAndroidJar(int apiLevel) {
+    return getAndroidJar(AndroidApiLevel.getAndroidApiLevel(apiLevel));
+  }
+
+  public static Path getAndroidJar(AndroidApiLevel apiLevel) {
+    if (apiLevel == AndroidApiLevel.P) {
       // TODO(mikaelpeltier) Android P does not yet have his android.jar use the O version
-      minSdkVersion = AndroidApiLevel.O.getLevel();
+      apiLevel = AndroidApiLevel.O;
     }
     String jar = String.format(
         ANDROID_JAR_PATTERN,
-        minSdkVersion == AndroidApiLevel.getDefault().getLevel() ? DEFAULT_MIN_SDK : minSdkVersion);
+        (apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
     assert Files.exists(Paths.get(jar))
-        : "Expected android jar to exist for API level " + minSdkVersion;
+        : "Expected android jar to exist for API level " + apiLevel;
     return Paths.get(jar);
   }
 
-  public static Path getJdwpTestsCfJarPath(int minSdk) {
-    if (minSdk >= AndroidApiLevel.N.getLevel()) {
+  public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
+    if (minSdk.getLevel() >= AndroidApiLevel.N.getLevel()) {
       return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar");
     } else {
       return Paths.get(ToolHelper.BUILD_DIR, "libs", "jdwp-tests-preN.jar");
     }
   }
 
-  public static Path getJdwpTestsDexJarPath(int minSdk) {
-    if (minSdk >= AndroidApiLevel.N.getLevel()) {
+  public static Path getJdwpTestsDexJarPath(AndroidApiLevel minSdk) {
+    if (minSdk.getLevel() >= AndroidApiLevel.N.getLevel()) {
       return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-hostdex.jar");
     } else {
       return Paths.get(ToolHelper.BUILD_DIR, "libs", "jdwp-tests-preN-dex.jar");
@@ -593,20 +597,24 @@
     }
   }
 
-  public static int getMinApiLevelForDexVm(DexVm dexVm) {
+  public static AndroidApiLevel getMinApiLevelForDexVm() {
+    return getMinApiLevelForDexVm(ToolHelper.getDexVm());
+  }
+
+  public static AndroidApiLevel getMinApiLevelForDexVm(DexVm dexVm) {
     switch (dexVm.version) {
       case DEFAULT:
-        return AndroidApiLevel.O.getLevel();
+        return AndroidApiLevel.O;
       case V7_0_0:
-        return AndroidApiLevel.N.getLevel();
+        return AndroidApiLevel.N;
       case V6_0_1:
-        return AndroidApiLevel.M.getLevel();
+        return AndroidApiLevel.M;
       case V5_1_1:
-        return AndroidApiLevel.L_MR1.getLevel();
+        return AndroidApiLevel.L_MR1;
       case V4_4_4:
-        return AndroidApiLevel.K.getLevel();
+        return AndroidApiLevel.K;
       case V4_0_4:
-        return AndroidApiLevel.I_MR1.getLevel();
+        return AndroidApiLevel.I_MR1;
       default:
         throw new Unreachable("Missing min api level for dex vm " + dexVm);
     }
@@ -780,7 +788,7 @@
       // Add the android library matching the minsdk. We filter out junit and testing classes
       // from the android jar to avoid duplicate classes in art and jctf tests.
       AndroidApp.Builder builder = AndroidApp.builder(app);
-      addFilteredAndroidJar(builder, command.getMinApiLevel());
+      addFilteredAndroidJar(builder, AndroidApiLevel.getAndroidApiLevel(command.getMinApiLevel()));
       app = builder.build();
     }
     InternalOptions options = command.getInternalOptions();
@@ -792,15 +800,15 @@
     return compatSink.build();
   }
 
-  public static void addFilteredAndroidJar(BaseCommand.Builder builder, int minSdkVersion)
+  public static void addFilteredAndroidJar(BaseCommand.Builder builder, AndroidApiLevel apiLevel)
       throws IOException {
-    addFilteredAndroidJar(getAppBuilder(builder), minSdkVersion);
+    addFilteredAndroidJar(getAppBuilder(builder), apiLevel);
   }
 
-  public static void addFilteredAndroidJar(AndroidApp.Builder builder, int minSdkVersion)
+  public static void addFilteredAndroidJar(AndroidApp.Builder builder, AndroidApiLevel apiLevel)
       throws IOException {
     builder.addFilteredLibraryArchives(Collections.singletonList(
-        new FilteredClassPath(getAndroidJar(minSdkVersion),
+        new FilteredClassPath(getAndroidJar(apiLevel),
             ImmutableList.of("!junit/**", "!android/test/**"))));
   }
 
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java
index 18e17f9..34b92b0 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTest.java
@@ -25,7 +25,7 @@
     R8Command.Builder builder = R8Command.builder();
     builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(A.class.getPackage()));
     builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
+    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
 
     // Note: we use '-checkdiscard' to indirectly check that the access relaxation is
     // done which leads to inlining of all pB*** methods so they are removed. Without
diff --git a/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
index 6049b7d..e8fa240 100644
--- a/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
@@ -13,8 +13,7 @@
 /** Base test configuration with CF version of JDWP. */
 public class CfDebugTestConfig extends DebugTestConfig {
 
-  public static final Path JDWP_JAR =
-      ToolHelper.getJdwpTestsCfJarPath(AndroidApiLevel.N.getLevel());
+  public static final Path JDWP_JAR = ToolHelper.getJdwpTestsCfJarPath(AndroidApiLevel.N);
 
   public CfDebugTestConfig() {
     this(Collections.emptyList());
diff --git a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
index 8480d24..1f13006 100644
--- a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -21,12 +22,12 @@
 
   public static AndroidApp d8Compile(List<Path> paths, Consumer<InternalOptions> optionsConsumer) {
     try {
-      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
       D8Command.Builder builder = D8Command.builder();
       return ToolHelper.runD8(
           builder
               .addProgramFiles(paths)
-              .setMinApiLevel(minSdk)
+              .setMinApiLevel(minSdk.getLevel())
               .setMode(CompilationMode.DEBUG)
               .addLibraryFiles(ToolHelper.getAndroidJar(minSdk)),
           optionsConsumer);
diff --git a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
index 3b7e4e8..6432530 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
@@ -8,10 +8,10 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -33,7 +33,7 @@
       boolean writeProguardMap)
       throws Exception {
     assert outputMode == OutputMode.DexIndexed || outputMode == OutputMode.ClassFile;
-    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
     String prefix = outputMode == OutputMode.ClassFile ? "cf" : "dex";
     Path outdir = temp.newFolder().toPath();
     Path outjar = outdir.resolve(prefix + "_r8_compiled.jar");
@@ -41,7 +41,7 @@
     R8Command.Builder builder =
         R8Command.builder()
             .addProgramFiles(DEBUGGEE_JAR)
-            .setMinApiLevel(minSdk)
+            .setMinApiLevel(minSdk.getLevel())
             .addLibraryFiles(ToolHelper.getAndroidJar(minSdk))
             .setMode(CompilationMode.RELEASE)
             .setOutput(outjar, outputMode);
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 94d5666..cbfcca6 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -115,7 +115,7 @@
 
   protected static final boolean supportsDefaultMethod(DebugTestConfig config) {
     return config.isCfRuntime()
-        || ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()) >= AndroidApiLevel.N.getLevel();
+        || ToolHelper.getMinApiLevelForDexVm().getLevel() >= AndroidApiLevel.N.getLevel();
   }
 
   protected final void runDebugTest(
diff --git a/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
index 27e3d30..babf170 100644
--- a/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
@@ -13,7 +13,7 @@
 public class DexDebugTestConfig extends DebugTestConfig {
 
   public static final Path JDWP_DEX_JAR =
-      ToolHelper.getJdwpTestsDexJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
+      ToolHelper.getJdwpTestsDexJarPath(ToolHelper.getMinApiLevelForDexVm());
 
   public DexDebugTestConfig() {
     this(Collections.emptyList());
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index d7e4d25..e251662 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -7,9 +7,9 @@
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import org.junit.Test;
 
 /** Tests source file and line numbers on inlined methods. */
@@ -31,14 +31,14 @@
       boolean writeProguardMap,
       boolean dontOptimizeByEnablingDebug)
       throws Exception {
-    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
     Path outdir = temp.newFolder().toPath();
     Path outjar = outdir.resolve("r8_compiled.jar");
     Path proguardMapPath = writeProguardMap ? outdir.resolve("proguard.map") : null;
     R8Command.Builder builder =
         R8Command.builder()
             .addProgramFiles(DEBUGGEE_JAR)
-            .setMinApiLevel(minSdk)
+            .setMinApiLevel(minSdk.getLevel())
             .addLibraryFiles(ToolHelper.getAndroidJar(minSdk))
             .setMode(dontOptimizeByEnablingDebug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
             .setOutput(outjar, OutputMode.DexIndexed);
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 7bf989c..5f63038 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -9,10 +9,10 @@
 import com.android.tools.r8.TestBase.MinifyMode;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -68,14 +68,14 @@
       proguardConfigurations = builder.build();
     }
 
-    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
     Path dexOutputDir = temp.newFolder().toPath();
     Path proguardMap = writeProguardMap ? dexOutputDir.resolve("proguard.map") : null;
     R8Command.Builder builder =
         R8Command.builder()
             .addProgramFiles(DEBUGGEE_JAR)
             .setOutput(dexOutputDir, OutputMode.DexIndexed)
-            .setMinApiLevel(minSdk)
+            .setMinApiLevel(minSdk.getLevel())
             .setMode(CompilationMode.DEBUG)
             .addLibraryFiles(ToolHelper.getAndroidJar(minSdk));
     if (proguardMap != null) {
diff --git a/src/test/java/com/android/tools/r8/debug/R8CfDebugTestResourcesConfig.java b/src/test/java/com/android/tools/r8/debug/R8CfDebugTestResourcesConfig.java
index d5bbe18..4916e1d 100644
--- a/src/test/java/com/android/tools/r8/debug/R8CfDebugTestResourcesConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/R8CfDebugTestResourcesConfig.java
@@ -8,10 +8,10 @@
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import org.junit.rules.TemporaryFolder;
 
 // Shared test configuration for R8/CF compiled resources from the "debugTestResources" target.
@@ -21,11 +21,11 @@
 
   private static synchronized AndroidApp getCompiledResources() throws Throwable {
     if (compiledResources == null) {
-      int minApi = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      AndroidApiLevel minApi = ToolHelper.getMinApiLevelForDexVm();
       AndroidAppConsumers sink = new AndroidAppConsumers();
       R8.run(
           R8Command.builder()
-              .setMinApiLevel(minApi)
+              .setMinApiLevel(minApi.getLevel())
               .setMode(CompilationMode.DEBUG)
               .addProgramFiles(DebugTestBase.DEBUGGEE_JAR)
               .setProgramConsumer(sink.wrapClassFileConsumer(null))
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index ab5ce01..97047e8 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -85,7 +85,7 @@
         D8Command.builder()
             .addClasspathFiles(classpath)
             .addProgramFiles(toCompile)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
             .setMinApiLevel(AndroidApiLevel.K.getLevel()),
         options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
   }
@@ -99,7 +99,7 @@
         D8Command.builder()
             .addClasspathFiles(classpath)
             .addProgramFiles(toCompile)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
             .setMinApiLevel(AndroidApiLevel.K.getLevel()),
         options -> options.interfaceMethodDesugaring = OffOrAuto.Off);
   }
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index 1349830..af8382e 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.io.File;
@@ -270,7 +271,7 @@
   @BeforeClass
   public static void compileLibraries() throws Exception {
     // Selects appropriate jar according to min api level for the selected runtime.
-    int minApi = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    AndroidApiLevel minApi = ToolHelper.getMinApiLevelForDexVm();
     Path jdwpTestsJar = ToolHelper.getJdwpTestsCfJarPath(minApi);
     Path classPath = ToolHelper.getClassPathForTests();
     Path testPath = classPath.resolve(Paths.get("com","android", "tools", "r8", "jdwp"));
@@ -285,7 +286,7 @@
             .addProgramFiles(jdwpTestsJar)
             .addProgramFiles(extraTestResources)
             .setOutput(d8Out.toPath(), OutputMode.DexIndexed)
-            .setMinApiLevel(minApi)
+            .setMinApiLevel(minApi.getLevel())
             .setMode(CompilationMode.DEBUG)
             .build());
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index d79d099..15c20c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -24,7 +24,6 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Map;
 import org.junit.Assume;
 
@@ -90,17 +89,16 @@
     return classSubject;
   }
 
-  private static MethodSubject checkMethod(ClassSubject classSubject, String methodName,
-      String methodReturnType, List<String> methodParameterTypes, boolean isPresent) {
-    return checkMethod(classSubject,
-        new MethodSignature(methodName, methodReturnType, methodParameterTypes), isPresent);
-  }
-
   protected static MethodSubject checkMethodIsPresent(ClassSubject classSubject,
       MethodSignature methodSignature) {
     return checkMethod(classSubject, methodSignature, true);
   }
 
+  protected static MethodSubject checkMethodIsAbsent(ClassSubject classSubject,
+      MethodSignature methodSignature) {
+    return checkMethod(classSubject, methodSignature, false);
+  }
+
   protected static MethodSubject checkMethod(ClassSubject classSubject,
       MethodSignature methodSignature, boolean isPresent) {
     MethodSubject methodSubject = classSubject.method(methodSignature);
@@ -121,21 +119,6 @@
     return code.asDexCode();
   }
 
-  private static DexCode extractCodeFor(DexInspector dexInspector, String className,
-      String methodName,
-      String methodReturnType, List<String> methodParameterTypes) {
-    ClassSubject classSubject = checkClassExists(dexInspector, className);
-    MethodSubject methodSubject = checkMethodIsPresent(classSubject, methodName, methodReturnType,
-        methodParameterTypes);
-    return getDexCode(methodSubject);
-  }
-
-  protected static MethodSubject checkMethodIsPresent(ClassSubject classSubject, String methodName,
-      String methodReturnType,
-      List<String> methodParameterTypes) {
-    return checkMethod(classSubject, methodName, methodReturnType, methodParameterTypes, true);
-  }
-
   private String buildProguardRules(String mainClass) {
     ProguardRulesBuilder proguardRules = new ProguardRulesBuilder();
     proguardRules.appendWithLineSeparator(keepMainProguardConfiguration(mainClass));
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 343fe9c..fe0efd1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -10,11 +10,8 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Map;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -61,17 +58,16 @@
 
       // Getters should be removed after inlining, which is possible only if access is relaxed.
       final boolean areGetterPresent = !allowAccessModification;
+      checkMethod(dataClass, NAME_GETTER_METHOD, areGetterPresent);
+      checkMethod(dataClass, AGE_GETTER_METHOD, areGetterPresent);
 
-      Map<MethodSignature, Boolean> presenceMap = ImmutableMap.<MethodSignature, Boolean>builder()
-          .put(NAME_GETTER_METHOD, areGetterPresent)
-          .put(AGE_GETTER_METHOD, areGetterPresent)
-          // ComponentN and copy methods are not used.
-          .put(COMPONENT1_METHOD, false)
-          .put(COMPONENT2_METHOD, false)
-          .put(COPY_METHOD, false)
-          .put(COPY_DEFAULT_METHOD, false)
-          .build();
-      checkMethodsPresence(dataClass, presenceMap);
+      // No use of componentN functions.
+      checkMethodIsAbsent(dataClass, COMPONENT1_METHOD);
+      checkMethodIsAbsent(dataClass, COMPONENT2_METHOD);
+
+      // No use of copy functions.
+      checkMethodIsAbsent(dataClass, COPY_METHOD);
+      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
 
       ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
       MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
@@ -98,17 +94,16 @@
       // ComponentN functions should be removed after inlining, which is possible only if access
       // is relaxed.
       final boolean areComponentMethodsPresent = !allowAccessModification;
+      checkMethod(dataClass, COMPONENT1_METHOD, areComponentMethodsPresent);
+      checkMethod(dataClass, COMPONENT2_METHOD, areComponentMethodsPresent);
 
-      Map<MethodSignature, Boolean> presenceMap = ImmutableMap.<MethodSignature, Boolean>builder()
-          .put(NAME_GETTER_METHOD, false)
-          .put(AGE_GETTER_METHOD, false)
-          // ComponentN and copy methods are not used.
-          .put(COMPONENT1_METHOD, areComponentMethodsPresent)
-          .put(COMPONENT2_METHOD, areComponentMethodsPresent)
-          .put(COPY_METHOD, false)
-          .put(COPY_DEFAULT_METHOD, false)
-          .build();
-      checkMethodsPresence(dataClass, presenceMap);
+      // No use of getter.
+      checkMethodIsAbsent(dataClass, NAME_GETTER_METHOD);
+      checkMethodIsAbsent(dataClass, AGE_GETTER_METHOD);
+
+      // No use of copy functions.
+      checkMethodIsAbsent(dataClass, COPY_METHOD);
+      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
 
       ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
       MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
@@ -132,17 +127,18 @@
       ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
 
       boolean component2IsPresent = !allowAccessModification;
+      checkMethod(dataClass, COMPONENT2_METHOD, component2IsPresent);
 
-      Map<MethodSignature, Boolean> presenceMap = ImmutableMap.<MethodSignature, Boolean>builder()
-          .put(NAME_GETTER_METHOD, false)
-          .put(AGE_GETTER_METHOD, false)
-          // ComponentN and copy methods are not used.
-          .put(COMPONENT1_METHOD, false)
-          .put(COMPONENT2_METHOD, component2IsPresent)
-          .put(COPY_METHOD, false)
-          .put(COPY_DEFAULT_METHOD, false)
-          .build();
-      checkMethodsPresence(dataClass, presenceMap);
+      // Function component1 is not used.
+      checkMethodIsAbsent(dataClass, COMPONENT1_METHOD);
+
+      // No use of getter.
+      checkMethodIsAbsent(dataClass, NAME_GETTER_METHOD);
+      checkMethodIsAbsent(dataClass, AGE_GETTER_METHOD);
+
+      // No use of copy functions.
+      checkMethodIsAbsent(dataClass, COPY_METHOD);
+      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
 
       ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
       MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
@@ -156,27 +152,23 @@
   }
 
   @Test
-  public void test_dataclass_copy() throws Exception {
-    final String mainClassName = "dataclass.MainCopyKt";
-    runTest("dataclass", mainClassName, (app) -> {
+  public void test_dataclass_copyIsRemovedIfNotUsed() throws Exception {
+    final String mainClassName = "dataclass.MainComponentOnlyKt";
+    final MethodSignature testMethodSignature =
+        new MethodSignature("testDataClassCopy", "void", Collections.emptyList());
+    final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
+    runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
 
-      // Copy method is small enough that it is always inlined.
-      final boolean copyMethodIsPresent = false;
-
-      Map<MethodSignature, Boolean> presenceMap = ImmutableMap.<MethodSignature, Boolean>builder()
-          .put(COPY_METHOD, copyMethodIsPresent)
-          .put(COPY_DEFAULT_METHOD, false)
-          .build();
-      checkMethodsPresence(dataClass, presenceMap);
+      checkMethodIsAbsent(dataClass, COPY_METHOD);
+      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
     });
   }
 
   @Test
-  @Ignore("See b/72871423")
-  public void test_dataclass_copyDefault() throws Exception {
-    final String mainClassName = "dataclass.MainCopyWithDefaultKt";
+  public void test_dataclass_copyDefaultIsRemovedIfNotUsed() throws Exception {
+    final String mainClassName = "dataclass.MainCopyKt";
     final MethodSignature testMethodSignature =
         new MethodSignature("testDataClassCopyWithDefault", "void", Collections.emptyList());
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
@@ -184,30 +176,7 @@
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
 
-      // Copy$default method is inlined if access is relaxed.
-      final boolean copyDefaultMethodIsPresent = !allowAccessModification;
-
-      // copy$default is a wrapper around copy to deal with default values. If it's inlined thus
-      // copy is inlined as well.
-      final boolean copyMethodIsPresent = copyDefaultMethodIsPresent;
-
-      Map<MethodSignature, Boolean> presenceMap = ImmutableMap.<MethodSignature, Boolean>builder()
-          .put(COPY_DEFAULT_METHOD, copyDefaultMethodIsPresent)
-          .put(COPY_METHOD, copyMethodIsPresent)
-          .build();
-      checkMethodsPresence(dataClass, presenceMap);
-
-      if (copyDefaultMethodIsPresent) {
-        ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
-        MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
-        DexCode dexCode = getDexCode(testMethod);
-        checkMethodIsInvokedAtLeastOnce(dexCode, COPY_DEFAULT_METHOD);
-      }
-      if (copyMethodIsPresent) {
-        MethodSubject testMethod = checkMethod(dataClass, COPY_DEFAULT_METHOD, true);
-        DexCode dexCode = getDexCode(testMethod);
-        checkMethodIsInvokedAtLeastOnce(dexCode, COPY_METHOD);
-      }
+      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
     });
   }
 
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index e33d785..57faa69 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -47,7 +47,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -58,7 +58,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -69,7 +69,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -80,7 +80,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -91,7 +91,7 @@
         EXAMPLE_O_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -131,7 +131,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
-        AndroidApiLevel.I.getLevel());
+        AndroidApiLevel.I);
   }
 
   private void doTest(
@@ -140,7 +140,7 @@
       String buildDir,
       Path mainDexRules,
       Path expectedMainDexList,
-      int minSdk)
+      AndroidApiLevel minSdk)
       throws Throwable {
     doTest(
         testName,
@@ -160,7 +160,7 @@
       String buildDir,
       Path mainDexRules,
       Path expectedMainDexList,
-      int minSdk,
+      AndroidApiLevel minSdk,
       Consumer<InternalOptions> optionsConsumer)
       throws Throwable {
     Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
@@ -170,7 +170,7 @@
       // Build main-dex list using GenerateMainDexList and test the output from run.
       GenerateMainDexListCommand.Builder mdlCommandBuilder = GenerateMainDexListCommand.builder();
       GenerateMainDexListCommand mdlCommand = mdlCommandBuilder
-          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
           .addProgramFiles(inputJar)
           .addProgramFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
           .addMainDexRulesFiles(mainDexRules)
@@ -189,7 +189,7 @@
       final Box mainDexListOutput = new Box();
       mdlCommandBuilder = GenerateMainDexListCommand.builder();
       mdlCommand = mdlCommandBuilder
-          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
           .addProgramFiles(inputJar)
           .addProgramFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
           .addMainDexRulesFiles(mainDexRules)
@@ -207,7 +207,7 @@
       R8Command.Builder r8CommandBuilder = R8Command.builder();
       R8Command command =
           r8CommandBuilder
-              .setMinApiLevel(minSdk)
+              .setMinApiLevel(minSdk.getLevel())
               .addProgramFiles(inputJar)
               .addProgramFiles(
                   Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
index 1d2a942..0f1359e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -37,7 +37,7 @@
   // and the Android library.
   private void buildInstrumentationTestCaseApplication(BaseCommand.Builder builder) {
     builder
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel()))
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
         .addProgramFiles(
             Paths.get("build", "test", "examplesAndroidApi",
                 "classes", "instrumentationtest", "InstrumentationTest.class"))
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index 9dbfb13..490162a 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -52,21 +52,39 @@
 
     // Check the instruction used for testInlinedIntoVoidMethod
     MethodSubject methodThrowToBeInlined =
-        clazz.method("void", "foo", ImmutableList.of("java.lang.String"));
+        clazz.method("void", "foo", ImmutableList.of(
+            "java.lang.String", "java.lang.String", "java.lang.String", "java.lang.String"));
     assertTrue(methodThrowToBeInlined.isPresent());
     validateSequence(methodThrowToBeInlined.iterateInstructions(),
-        InstructionSubject::isIfNez,
+        // 'if' with "foo#1" is flipped.
+        InstructionSubject::isIfEqz,
+
+        // 'if' with "foo#2" is removed along with the constant.
+
+        // 'if' with "foo#3" is removed so now we have unconditional call.
+        insn -> insn.isConstString("StringConstants::foo#3"),
+        InstructionSubject::isInvokeStatic,
+        InstructionSubject::isThrow,
+
+        // 'if's with "foo#4" and "foo#5" are flipped, but their throwing branches
+        // are not moved to the end of the code (area for improvement?).
+        insn -> insn.isConstString("StringConstants::foo#4"),
+        InstructionSubject::isIfEqz, // Flipped if
+        InstructionSubject::isGoto, // Jump around throwing branch.
+        InstructionSubject::isInvokeStatic, // Throwing branch.
+        InstructionSubject::isThrow,
+
+        insn -> insn.isConstString("StringConstants::foo#5"),
+        InstructionSubject::isIfEqz, // Flipped if
+        InstructionSubject::isReturnVoid, // Final return statement.
+        InstructionSubject::isInvokeStatic, // Throwing branch.
+        InstructionSubject::isThrow,
+
+        // After 'if' with "foo#1" flipped, always throwing branch
+        // moved here along with the constant.
         insn -> insn.isConstString("StringConstants::foo#1"),
-        // Below two are removed by optimization: non-null argument "".
-        // InstructionSubject::isIfNez,
-        // insn -> insn.isConstString("StringConstants::foo#2"),
-        // InstructionSubject::isIfNez, 'removed by optimization'
-        insn -> insn.isConstString("StringConstants::foo#3")
-        // Below four are removed, since a safe call of arg.length() indicates arg is not null.
-        // insn -> insn.isConstString("StringConstants::foo#4"),
-        // InstructionSubject::isIfNez,
-        // insn -> insn.isConstString("StringConstants::foo#5"),
-        // InstructionSubject::isIfNez
+        InstructionSubject::isInvokeStatic,
+        InstructionSubject::isThrow
     );
   }
 
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
index b801804..bf13fc3 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/TestClass.java
@@ -5,19 +5,19 @@
 package com.android.tools.r8.movestringconstants;
 
 public class TestClass {
-  static void foo(String arg) {
-    Utils.check(arg, "StringConstants::foo#1");
+  static void foo(String arg1, String arg2, String arg3, String arg4) {
+    Utils.check(arg1, "StringConstants::foo#1");
     Utils.check("", "StringConstants::foo#2");
-    if (arg.length() == 12345) {
+    if (arg2.length() == 12345) {
       Utils.check(null, "StringConstants::foo#3");
     }
     try {
-      Utils.check(arg, "StringConstants::foo#4");
+      Utils.check(arg3, "StringConstants::foo#4");
     } catch (Exception e) {
       System.out.println(e.getMessage());
     }
     try {
-      Utils.check(arg, "StringConstants::foo#5");
+      Utils.check(arg4, "StringConstants::foo#5");
     } finally {
       System.out.println("finally");
     }
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index 84dd3b7..ca86a53 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.debug.DebugTestBase;
 import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.debug.DexDebugTestConfig;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import org.junit.BeforeClass;
@@ -26,7 +27,7 @@
 
   @BeforeClass
   public static void initDebuggeePath() throws Exception {
-    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
     Path outdir = temp.newFolder().toPath();
     Path outjar = outdir.resolve("r8_compiled.jar");
     Path proguardMapPath = outdir.resolve("proguard.map");
@@ -39,7 +40,7 @@
                       ImmutableList.of("SourceFile", "LineNumberTable"));
                 })
             .addProgramFiles(DEBUGGEE_JAR)
-            .setMinApiLevel(minSdk)
+            .setMinApiLevel(minSdk.getLevel())
             .addLibraryFiles(ToolHelper.getAndroidJar(minSdk))
             .setMode(CompilationMode.DEBUG)
             .setOutput(outjar, OutputMode.DexIndexed)
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java
index af4c4b2..96b8a06 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/A.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.naming.overloadaggressively;
 
 public class A {
-  volatile int f1;
+  public volatile int f1;
   volatile Object f2;
-  volatile B f3;
+  public volatile B f3;
 }
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
index 78f5a24..6660196 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
@@ -4,6 +4,19 @@
 package com.android.tools.r8.naming.overloadaggressively;
 
 public class B {
-  volatile int f1;
-  volatile Object f2;
+  volatile int f1 = 8;
+  volatile Object f2 = "d8";
+  volatile String f3 = "r8";
+
+  public int getF1() {
+    return f1;
+  }
+
+  public Object getF2() {
+    return f2;
+  }
+
+  public String getF3() {
+    return f3;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldResolution.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldResolution.java
new file mode 100644
index 0000000..26f5df6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldResolution.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2018, 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.naming.overloadaggressively;
+
+import java.lang.reflect.Field;
+import java.util.Random;
+
+public class FieldResolution {
+  public static void main(String[] args) throws Exception {
+    A a = new A();
+    B b = new B();
+
+    Field f3 = A.class.getField("f3");
+    f3.set(a, b);
+    assert a.f3 != null;
+    assert a.f3 == b;
+
+    Field f1 = A.class.getField("f1");
+    Random random = new Random();
+    int next = random.nextInt();
+    f1.set(a, next);
+    a.f3.f1 = next;
+    int diff = a.f1 - b.f1;
+    System.out.println("diff: " + diff);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldUpdater.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java
rename to src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldUpdater.java
index 956b198..8f5011c 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/Main.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/FieldUpdater.java
@@ -7,7 +7,7 @@
 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
-public class Main {
+public class FieldUpdater {
   @SuppressWarnings("unchecked")
   public static void main(String[] args) throws Exception {
     A a = new A();
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/MethodResolution.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/MethodResolution.java
new file mode 100644
index 0000000..c791031
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/MethodResolution.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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.naming.overloadaggressively;
+
+import java.lang.reflect.Method;
+
+public class MethodResolution {
+  public static void main(String[] args) throws Exception {
+    B b = new B();
+
+    int originalF1 = b.getF1();
+    Method getF1 = B.class.getMethod("getF1", (Class[]) null);
+    int diff = ((Integer) getF1.invoke(b)) - originalF1;
+    System.out.println("diff: " + diff);
+
+    Object originalF2 = b.getF2();
+    Method getF2 = B.class.getMethod("getF2", (Class[]) null);
+    System.out.println(originalF2 + " v.s. " + getF2.invoke(b));
+
+    String originalF3 = b.getF3();
+    Method getF3 = B.class.getMethod("getF3", (Class[]) null);
+    System.out.println(originalF3 + " v.s. " + getF3.invoke(b));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index d0cdb28..1254d4b 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -55,54 +56,144 @@
     return testCases;
   }
 
-  @Test
-  public void overloadAggressivelyTest() throws Exception {
-    Assume.assumeTrue(ToolHelper.artSupported());
-    byte[][] classes = {
-        ToolHelper.getClassAsBytes(Main.class),
-        ToolHelper.getClassAsBytes(A.class),
-        ToolHelper.getClassAsBytes(B.class)
-    };
-    AndroidApp originalApp = buildAndroidApp(classes);
-    Path out = temp.getRoot().toPath();
-    R8Command command =
+  private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+     R8Command command =
         ToolHelper.addProguardConfigurationConsumer(
-            ToolHelper.prepareR8CommandBuilder(originalApp),
+            ToolHelper.prepareR8CommandBuilder(app),
             pgConfig -> {
               pgConfig.setPrintMapping(true);
               pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
             })
         .addProguardConfiguration(
             ImmutableList.copyOf(Iterables.concat(ImmutableList.of(
-                keepMainProguardConfiguration(Main.class),
+                keepMainProguardConfiguration(main),
                 overloadaggressively ? "-overloadaggressively" : ""),
                 CompatProguardCommandBuilder.REFLECTIONS)),
             Origin.unknown())
         .setOutput(out, OutputMode.DexIndexed)
         .build();
-    AndroidApp processedApp = ToolHelper.runR8(command);
+    return ToolHelper.runR8(command, o -> o.inlineAccessors = false);
+  }
 
-    DexInspector dexInspector = new DexInspector(
-        out.resolve(ToolHelper.DEFAULT_DEX_FILENAME),
-        out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE).toString());
+  @Test
+  public void fieldUpdater() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    byte[][] classes = {
+        ToolHelper.getClassAsBytes(FieldUpdater.class),
+        ToolHelper.getClassAsBytes(A.class),
+        ToolHelper.getClassAsBytes(B.class)
+    };
+    AndroidApp originalApp = buildAndroidApp(classes);
+    Path out = temp.getRoot().toPath();
+    AndroidApp processedApp = runR8(originalApp, FieldUpdater.class, out);
+
+    DexInspector dexInspector = new DexInspector(processedApp);
     ClassSubject a = dexInspector.clazz(A.class.getCanonicalName());
     DexEncodedField f1 = a.field("int", "f1").getField();
     assertNotNull(f1);
     DexEncodedField f2 = a.field("java.lang.Object", "f2").getField();
     assertNotNull(f2);
+    // TODO(b/72858955): due to the potential reflective access, they should have different names
+    //   by R8's improved reflective access detection or via keep rules.
     assertEquals(overloadaggressively, f1.field.name == f2.field.name);
     DexEncodedField f3 = a.field(B.class.getCanonicalName(), "f3").getField();
     assertNotNull(f3);
+    // TODO(b/72858955): ditto
     assertEquals(overloadaggressively, f1.field.name == f3.field.name);
+    // TODO(b/72858955): ditto
     assertEquals(overloadaggressively, f2.field.name == f3.field.name);
 
-    ProcessResult javaOutput = runOnJava(Main.class.getCanonicalName(), classes);
-    ProcessResult artOutput = runOnArtRaw(processedApp, Main.class.getCanonicalName(), dexVm);
+    String main = FieldUpdater.class.getCanonicalName();
+    ProcessResult javaOutput = runOnJava(main, classes);
+    assertEquals(0, javaOutput.exitCode);
+    ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+    // TODO(b/72858955): eventually, R8 should avoid this field resolution conflict.
     if (overloadaggressively) {
       assertNotEquals(0, artOutput.exitCode);
       assertTrue(artOutput.stderr.contains("ClassCastException"));
     } else {
-      assertEquals(0, javaOutput.exitCode);
+      assertEquals(0, artOutput.exitCode);
+      assertEquals(javaOutput.stdout.trim(), artOutput.stdout.trim());
+      // ART may dump its own debugging info through stderr.
+      // assertEquals(javaOutput.stderr.trim(), artOutput.stderr.trim());
+    }
+  }
+
+  @Test
+  public void fieldResolution() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    byte[][] classes = {
+        ToolHelper.getClassAsBytes(FieldResolution.class),
+        ToolHelper.getClassAsBytes(A.class),
+        ToolHelper.getClassAsBytes(B.class)
+    };
+    AndroidApp originalApp = buildAndroidApp(classes);
+    Path out = temp.getRoot().toPath();
+    AndroidApp processedApp = runR8(originalApp, FieldResolution.class, out);
+
+    DexInspector dexInspector = new DexInspector(processedApp);
+    ClassSubject a = dexInspector.clazz(A.class.getCanonicalName());
+    DexEncodedField f1 = a.field("int", "f1").getField();
+    assertNotNull(f1);
+    DexEncodedField f3 = a.field(B.class.getCanonicalName(), "f3").getField();
+    assertNotNull(f3);
+    // TODO(b/72858955): due to the potential reflective access, they should have different names
+    //   by R8's improved reflective access detection or via keep rules.
+    assertEquals(overloadaggressively, f1.field.name == f3.field.name);
+
+    String main = FieldResolution.class.getCanonicalName();
+    ProcessResult javaOutput = runOnJava(main, classes);
+    assertEquals(0, javaOutput.exitCode);
+    ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+    // TODO(b/72858955): R8 should avoid field resolution conflict even w/ -overloadaggressively.
+    if (overloadaggressively) {
+      assertNotEquals(0, artOutput.exitCode);
+      assertTrue(artOutput.stderr.contains("IllegalArgumentException"));
+    } else {
+      assertEquals(0, artOutput.exitCode);
+      assertEquals(javaOutput.stdout.trim(), artOutput.stdout.trim());
+      // ART may dump its own debugging info through stderr.
+      // assertEquals(javaOutput.stderr.trim(), artOutput.stderr.trim());
+    }
+  }
+
+  @Test
+  public void methodResolution() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    byte[][] classes = {
+        ToolHelper.getClassAsBytes(MethodResolution.class),
+        ToolHelper.getClassAsBytes(B.class)
+    };
+    AndroidApp originalApp = buildAndroidApp(classes);
+    Path out = temp.getRoot().toPath();
+    AndroidApp processedApp = runR8(originalApp, MethodResolution.class, out);
+
+    DexInspector dexInspector = new DexInspector(processedApp);
+    ClassSubject b = dexInspector.clazz(B.class.getCanonicalName());
+    DexEncodedMethod m1 =
+        b.method("int", "getF1", ImmutableList.of()).getMethod();
+    assertNotNull(m1);
+    DexEncodedMethod m2 =
+        b.method("java.lang.Object", "getF2", ImmutableList.of()).getMethod();
+    // TODO(b/72858955): due to the potential reflective access, they should have different names.
+    assertEquals(overloadaggressively, m1.method.name == m2.method.name);
+    DexEncodedMethod m3 =
+        b.method("java.lang.String", "getF3", ImmutableList.of()).getMethod();
+    assertNotNull(m3);
+    // TODO(b/72858955): ditto
+    assertEquals(overloadaggressively, m1.method.name == m3.method.name);
+    // TODO(b/72858955): ditto
+    assertEquals(overloadaggressively, m2.method.name == m3.method.name);
+
+    String main = MethodResolution.class.getCanonicalName();
+    ProcessResult javaOutput = runOnJava(main, classes);
+    assertEquals(0, javaOutput.exitCode);
+    ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+    // TODO(b/72858955): R8 should avoid method resolution conflict even w/ -overloadaggressively.
+    if (overloadaggressively) {
+      assertEquals(0, artOutput.exitCode);
+      assertNotEquals(javaOutput.stdout.trim(), artOutput.stdout.trim());
+    } else {
       assertEquals(0, artOutput.exitCode);
       assertEquals(javaOutput.stdout.trim(), artOutput.stdout.trim());
       // ART may dump its own debugging info through stderr.
diff --git a/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java b/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
index 0553fe4..f1d846e 100644
--- a/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69906048/Regress69906048Test.java
@@ -16,7 +16,7 @@
   public void buildWithD8AndRunWithDalvikOrArt() throws Exception {
     AndroidApp androidApp = compileWithR8(
         ImmutableList.of(ClassWithAnnotations.class, AnAnnotation.class),
-        options -> options.minApiLevel = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
+        options -> options.minApiLevel = ToolHelper.getMinApiLevelForDexVm().getLevel());
     String result = runOnArt(androidApp, ClassWithAnnotations.class);
     Assert.assertEquals("@" + AnAnnotation.class.getCanonicalName() + "()", result);
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
index 0584aa0..cf2be59 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
@@ -49,7 +49,7 @@
       allBytes.add(ToolHelper.getClassAsBytes(clazz));
     }
     ensureSameOutput(Main.class.getCanonicalName(),
-        ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()),
+        ToolHelper.getMinApiLevelForDexVm(),
         allBytes.toArray(new byte[allBytes.size()][]));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index f087d0d..95ecd4f 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.code.Const4;
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.Goto;
+import com.android.tools.r8.code.IfEqz;
 import com.android.tools.r8.code.IfNez;
 import com.android.tools.r8.code.Iget;
 import com.android.tools.r8.code.IgetBoolean;
@@ -1001,6 +1002,10 @@
       return instruction instanceof IfNez;
     }
 
+    boolean isIfEqz(Instruction instruction) {
+      return instruction instanceof IfEqz;
+    }
+
     boolean isFieldAccess(Instruction instruction) {
       return isInstanceGet(instruction)
           || isInstancePut(instruction)
@@ -1111,6 +1116,10 @@
       return factory.isIfNez(instruction);
     }
 
+    public boolean isIfEqz() {
+      return factory.isIfEqz(instruction);
+    }
+
     public boolean isReturnVoid() {
       return factory.isReturnVoid(instruction);
     }
diff --git a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java b/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
index b14ef8a..fb4fe70 100644
--- a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
@@ -61,7 +61,7 @@
 
   private void addAndroidJarsToCommandLine(List<String> args) {
     args.add("--lib");
-    args.add(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()).toAbsolutePath().toString());
+    args.add(ToolHelper.getAndroidJar(AndroidApiLevel.K).toAbsolutePath().toString());
   }
 
   // Add the jars used in the com.android.tools.r8.maindexlist.MainDexTracingTest test.
diff --git a/src/test/kotlinR8TestResources/dataclass/MainCopy.kt b/src/test/kotlinR8TestResources/dataclass/MainCopy.kt
index 2e4f047..2644302 100644
--- a/src/test/kotlinR8TestResources/dataclass/MainCopy.kt
+++ b/src/test/kotlinR8TestResources/dataclass/MainCopy.kt
@@ -16,7 +16,8 @@
 
 fun testDataClassCopy() {
     val albert = Person("Albert", 28)
-    val olderJonas = albert.copy("Jonas", albert.age + 10)
+    val youngerJonas = albert.copy("Jonas", albert.age - 10)
+    val olderJonas = youngerJonas.copy("Jonas", albert.age + 20)
     println("Name: ${olderJonas.name}")
     println("Age: ${olderJonas.age}")
 }
\ No newline at end of file
diff --git a/src/test/kotlinR8TestResources/dataclass/MainCopyWithDefault.kt b/src/test/kotlinR8TestResources/dataclass/MainCopyWithDefault.kt
deleted file mode 100644
index 2df19f7..0000000
--- a/src/test/kotlinR8TestResources/dataclass/MainCopyWithDefault.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2018, 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 dataclass
-
-/**
- * This is an example of copying an instance of a data class Person using its copy method and
- * relying on default values for some of its properties. Therefore the compiler will generate an
- * invoke to copy$default method which is a wrapper around copy and deals with default values.
- *
- * See https://kotlinlang.org/docs/reference/data-classes.html#copying.
- */
-fun main(args: Array<String>) {
-    testDataClassCopyWithDefault()
-}
-
-fun testDataClassCopyWithDefault() {
-    val albert = Person("Albert", 28)
-    // We don't pass a 'name', thus we copy the property value of the receiver. This will result
-    // in calling the copy$default method instead of the copy method.
-    val olderAlbert = albert.copy(age = albert.age + 10)
-    println("Name: ${olderAlbert.name}")
-    println("Age: ${olderAlbert.age}")
-}
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index 360a0ee..815e416 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -113,6 +113,14 @@
       print('Uploading %s to %s' % (tagged_jar, destination))
       utils.upload_file_to_cloud_storage(tagged_jar, destination)
       print('File available at: %s' % GetUrl(version, file_name, is_master))
+    # Upload extracted maven directory for easy testing in studio.
+    zip_ref = zipfile.ZipFile(utils.MAVEN_ZIP, 'r')
+    zip_ref.extractall(temp)
+    zip_ref.close()
+    utils.upload_dir_to_cloud_storage(
+        os.path.join(temp, 'com'),
+        GetUploadDestination(version, 'com', is_master))
+    print('Maven repo root available at: %s' % GetUrl(version, '', is_master))
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/test.py b/tools/test.py
index 58fc4eb..ed99c1d 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -85,7 +85,7 @@
   upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
   u_dir = uuid.uuid4()
   destination = 'gs://%s/%s' % (BUCKET, u_dir)
-  utils.upload_dir_to_cloud_storage(upload_dir, destination)
+  utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
   url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
   print 'Test results available at: %s' % url
   print '@@@STEP_LINK@Test failures@%s@@@' % url
@@ -166,4 +166,3 @@
   else:
     notify.notify("Tests passed.")
   sys.exit(return_code)
-
diff --git a/tools/utils.py b/tools/utils.py
index 47acca5..8716f65 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -73,10 +73,12 @@
     if not os.path.isdir(path):
         raise
 
-def upload_dir_to_cloud_storage(directory, destination):
+def upload_dir_to_cloud_storage(directory, destination, is_html=False):
   # Upload and make the content encoding right for viewing directly
-  cmd = ['gsutil.py', 'cp', '-z', 'html', '-a',
-         'public-read', '-R', directory, destination]
+  cmd = ['gsutil.py', 'cp']
+  if is_html:
+    cmd += ['-z', 'html']
+  cmd += ['-a', 'public-read', '-R', directory, destination]
   PrintCmd(cmd)
   subprocess.check_call(cmd)