Update usage of D8 API in DexFileMerger.

PiperOrigin-RevId: 553539967
Change-Id: I5509d3e6752819ebf7d8cbc8979cf315ec2673a6
diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/DexFileMerger.java b/src/tools/android/java/com/google/devtools/build/android/r8/DexFileMerger.java
index 82f4ba2..4b9abbd 100644
--- a/src/tools/android/java/com/google/devtools/build/android/r8/DexFileMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/r8/DexFileMerger.java
@@ -36,11 +36,11 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -68,6 +68,8 @@
 
   private static final boolean PRINT_ARGS = false;
 
+  private static final int NATIVE_MULTIDEX_API_LEVEL = 21;
+
   /** Strategies for outputting multiple {@code .dex} files supported by {@link DexFileMerger}. */
   public enum MultidexStrategy {
     /** Create exactly one .dex file. The operation will fail if .dex limits are exceeded. */
@@ -296,6 +298,17 @@
     return shard;
   }
 
+  private static int getInputNumber(
+      Origin origin, Map<String, Integer> inputOrdering, DiagnosticsHandler handler) {
+    Integer number = inputOrdering.get(origin.parent().part());
+    if (number == null) {
+      String message = "Class parent path not found among input paths: " + origin;
+      handler.error(new StringDiagnostic(message, origin));
+      throw new IllegalStateException(message);
+    }
+    return number;
+  }
+
   public static void run(String[] args) throws CompilationFailedException, IOException {
     Options options = parseArguments(args);
 
@@ -318,13 +331,39 @@
 
     D8Command.Builder builder = D8Command.builder();
 
+    // If multidex is enabled but no main-dex list given then the build must be targeting
+    // devices with native multidex support.
+    if (options.multidexMode.isMultidexAllowed() && options.mainDexListFile == null) {
+      builder.setMinApiLevel(NATIVE_MULTIDEX_API_LEVEL);
+    }
+
+    // The merge step assumes that no further desugaring is needed.
+    builder = builder.setDisableDesugaring(true);
+
+    // D8 does not allow duplicate classes. Resolve conflicts based on input order.
     Map<String, Integer> inputOrdering =
         Maps.newHashMapWithExpectedSize(options.inputArchives.size());
     int sequenceNumber = 0;
     for (Path s : options.inputArchives) {
-      builder.addProgramFiles(s);
+      builder = builder.addProgramFiles(s);
       inputOrdering.put(s.toString(), sequenceNumber++);
     }
+    builder =
+        builder.setClassConflictResolver(
+            (reference, origins, handler) -> {
+              Iterator<Origin> it = origins.iterator();
+              Origin first = it.next();
+              int firstNumber = getInputNumber(first, inputOrdering, handler);
+              while (it.hasNext()) {
+                Origin next = it.next();
+                int nextNumber = getInputNumber(next, inputOrdering, handler);
+                if (firstNumber > nextNumber) {
+                  first = next;
+                  firstNumber = nextNumber;
+                }
+              }
+              return first;
+            });
 
     // Determine enabling multidexing and file indexing.
     Integer singleFixedFileIndex = null;
@@ -345,31 +384,15 @@
         break;
     }
 
-    if (options.mainDexListFile != null) {
-      builder.addMainDexListFiles(options.mainDexListFile);
+    if (builder.getMinApiLevel() < NATIVE_MULTIDEX_API_LEVEL && options.mainDexListFile != null) {
+      builder = builder.addMainDexListFiles(options.mainDexListFile);
     }
 
     ArchiveConsumer consumer =
         new ArchiveConsumer(options.outputArchive, options.dexPrefix, singleFixedFileIndex);
-    builder.setProgramConsumer(consumer);
+    builder = builder.setProgramConsumer(consumer);
 
-    // Try to run through com.android.tools.r8.DexFileMergerHelper.run. If not found, which
-    // can happen when bazel use a d8.jar from a Platform SDK, fall back to plain D8 execution.
-    try {
-      Class<?> dexFileMergerHelper = Class.forName("com.android.tools.r8.DexFileMergerHelper");
-      try {
-        Method run =
-            dexFileMergerHelper.getDeclaredMethod("run", D8Command.class, Boolean.class, Map.class);
-        // DexFileMergerHelper.run(builder.build(), false, inputOrdering);
-        run.invoke(null, builder.build(), false, inputOrdering);
-      } catch (NoSuchMethodException e) {
-        D8.run(builder.build());
-      } catch (ReflectiveOperationException e) {
-        throw new AssertionError("Unable to invoke run in DexFileMergerHelper", e);
-      }
-    } catch (ClassNotFoundException e) {
-      D8.run(builder.build());
-    }
+    D8.run(builder.build());
 
     // If input was empty we still need to write out an empty zip.
     if (!consumer.hasWrittenSomething()) {