Address all API design review feedback.

See the latest design doc at go/art-services-apis.

Bug: 242990781
Test: atest ArtServiceTests
Ignore-AOSP-First: ART Services.
Change-Id: I5afb0142427e31dc1bc95206abea2c970a1981cf
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index cef4365..d22a8c3 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -51,15 +51,16 @@
   }
 
   public class OptimizationStatus {
-    method @NonNull public java.util.List<com.android.server.art.model.OptimizationStatus.DexFileOptimizationStatus> getDexFileOptimizationStatuses();
+    method @NonNull public java.util.List<com.android.server.art.model.OptimizationStatus.DexContainerFileOptimizationStatus> getDexContainerFileOptimizationStatuses();
   }
 
-  public static class OptimizationStatus.DexFileOptimizationStatus {
+  public static class OptimizationStatus.DexContainerFileOptimizationStatus {
+    method @NonNull public String getAbi();
     method @NonNull public String getCompilationReason();
     method @NonNull public String getCompilerFilter();
-    method @NonNull public String getDexFile();
-    method @NonNull public String getInstructionSet();
+    method @NonNull public String getDexContainerFile();
     method @NonNull public String getLocationDebugString();
+    method public boolean isPrimaryAbi();
   }
 
   public class OptimizeParams {
@@ -78,7 +79,6 @@
     method @NonNull public com.android.server.art.model.OptimizeParams.Builder setFlags(int);
     method @NonNull public com.android.server.art.model.OptimizeParams.Builder setFlags(int, int);
     method @NonNull public com.android.server.art.model.OptimizeParams.Builder setPriorityClass(int);
-    method @NonNull public com.android.server.art.model.OptimizeParams.Builder setReason(@NonNull String);
   }
 
   public class OptimizeResult {
@@ -92,17 +92,18 @@
     field public static final int OPTIMIZE_SKIPPED = 10; // 0xa
   }
 
-  public static class OptimizeResult.DexFileOptimizeResult {
+  public static class OptimizeResult.DexContainerFileOptimizeResult {
+    method @NonNull public String getAbi();
     method @NonNull public String getActualCompilerFilter();
     method public long getDex2oatCpuTimeMillis();
     method public long getDex2oatWallTimeMillis();
-    method @NonNull public String getDexFile();
-    method @NonNull public String getInstructionSet();
+    method @NonNull public String getDexContainerFile();
     method public int getStatus();
+    method public boolean isPrimaryAbi();
   }
 
   public static class OptimizeResult.PackageOptimizeResult {
-    method @NonNull public java.util.List<com.android.server.art.model.OptimizeResult.DexFileOptimizeResult> getDexFileOptimizeResults();
+    method @NonNull public java.util.List<com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult> getDexContainerFileOptimizeResults();
     method @NonNull public String getPackageName();
     method public int getStatus();
   }
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index d4659ff..b67fb98 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -18,9 +18,10 @@
 
 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
 import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+import static com.android.server.art.Utils.Abi;
 import static com.android.server.art.model.ArtFlags.DeleteFlags;
 import static com.android.server.art.model.ArtFlags.GetStatusFlags;
-import static com.android.server.art.model.OptimizationStatus.DexFileOptimizationStatus;
+import static com.android.server.art.model.OptimizationStatus.DexContainerFileOptimizationStatus;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -142,10 +143,10 @@
                     if (!dexInfo.hasCode()) {
                         continue;
                     }
-                    for (String isa : Utils.getAllIsas(pkgState)) {
+                    for (Abi abi : Utils.getAllAbis(pkgState)) {
                         freedBytes +=
                                 mInjector.getArtd().deleteArtifacts(AidlUtils.buildArtifactsPath(
-                                        dexInfo.dexPath(), isa, isInDalvikCache));
+                                        dexInfo.dexPath(), abi.isa(), isInDalvikCache));
                     }
                 }
             }
@@ -193,7 +194,7 @@
         AndroidPackageApi pkg = getPackageOrThrow(pkgState);
 
         try {
-            List<DexFileOptimizationStatus> statuses = new ArrayList<>();
+            List<DexContainerFileOptimizationStatus> statuses = new ArrayList<>();
 
             if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0) {
                 for (DetailedPrimaryDexInfo dexInfo :
@@ -201,17 +202,18 @@
                     if (!dexInfo.hasCode()) {
                         continue;
                     }
-                    for (String isa : Utils.getAllIsas(pkgState)) {
+                    for (Abi abi : Utils.getAllAbis(pkgState)) {
                         try {
                             GetOptimizationStatusResult result =
-                                    mInjector.getArtd().getOptimizationStatus(
-                                            dexInfo.dexPath(), isa, dexInfo.classLoaderContext());
-                            statuses.add(new DexFileOptimizationStatus(dexInfo.dexPath(), isa,
-                                    result.compilerFilter, result.compilationReason,
-                                    result.locationDebugString));
+                                    mInjector.getArtd().getOptimizationStatus(dexInfo.dexPath(),
+                                            abi.isa(), dexInfo.classLoaderContext());
+                            statuses.add(new DexContainerFileOptimizationStatus(dexInfo.dexPath(),
+                                    abi.isPrimaryAbi(), abi.name(), result.compilerFilter,
+                                    result.compilationReason, result.locationDebugString));
                         } catch (ServiceSpecificException e) {
-                            statuses.add(new DexFileOptimizationStatus(
-                                    dexInfo.dexPath(), isa, "error", "error", e.getMessage()));
+                            statuses.add(new DexContainerFileOptimizationStatus(dexInfo.dexPath(),
+                                    abi.isPrimaryAbi(), abi.name(), "error", "error",
+                                    e.getMessage()));
                         }
                     }
                 }
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index def1949..5f6d6d4 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -17,8 +17,8 @@
 package com.android.server.art;
 
 import static com.android.server.art.model.ArtFlags.OptimizeFlags;
-import static com.android.server.art.model.OptimizationStatus.DexFileOptimizationStatus;
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizationStatus.DexContainerFileOptimizationStatus;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.model.OptimizeResult.OptimizeStatus;
 import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
 
@@ -75,11 +75,12 @@
             case "get-optimization-status": {
                 OptimizationStatus optimizationStatus = mArtManagerLocal.getOptimizationStatus(
                         snapshot, getNextArgRequired(), ArtFlags.defaultGetStatusFlags());
-                for (DexFileOptimizationStatus status :
-                        optimizationStatus.getDexFileOptimizationStatuses()) {
-                    pw.printf("dexFile = %s, instructionSet = %s, compilerFilter = %s, "
-                                    + "compilationReason = %s, locationDebugString = %s\n",
-                            status.getDexFile(), status.getInstructionSet(),
+                for (DexContainerFileOptimizationStatus status :
+                        optimizationStatus.getDexContainerFileOptimizationStatuses()) {
+                    pw.printf("dexContainerFile = %s, isPrimaryAbi = %b, abi = %s, "
+                                    + "compilerFilter = %s, compilationReason = %s, "
+                                    + "locationDebugString = %s\n",
+                            status.getDexContainerFile(), status.isPrimaryAbi(), status.getAbi(),
                             status.getCompilerFilter(), status.getCompilationReason(),
                             status.getLocationDebugString());
                 }
@@ -124,16 +125,16 @@
                 pw.println(optimizeStatusToString(result.getFinalStatus()));
                 for (PackageOptimizeResult packageResult : result.getPackageOptimizeResults()) {
                     pw.printf("[%s]\n", packageResult.getPackageName());
-                    for (DexFileOptimizeResult dexFileResult :
-                            packageResult.getDexFileOptimizeResults()) {
-                        pw.printf("dexFile = %s, instructionSet = %s, compilerFilter = %s, "
-                                        + "status = %s, dex2oatWallTimeMillis = %d, "
-                                        + "dex2oatCpuTimeMillis = %d\n",
-                                dexFileResult.getDexFile(), dexFileResult.getInstructionSet(),
-                                dexFileResult.getActualCompilerFilter(),
-                                optimizeStatusToString(dexFileResult.getStatus()),
-                                dexFileResult.getDex2oatWallTimeMillis(),
-                                dexFileResult.getDex2oatCpuTimeMillis());
+                    for (DexContainerFileOptimizeResult fileResult :
+                            packageResult.getDexContainerFileOptimizeResults()) {
+                        pw.printf("dexContainerFile = %s, isPrimaryAbi = %b, abi = %s, "
+                                        + "compilerFilter = %s, status = %s, "
+                                        + "dex2oatWallTimeMillis = %d, dex2oatCpuTimeMillis = %d\n",
+                                fileResult.getDexContainerFile(), fileResult.isPrimaryAbi(),
+                                fileResult.getAbi(), fileResult.getActualCompilerFilter(),
+                                optimizeStatusToString(fileResult.getStatus()),
+                                fileResult.getDex2oatWallTimeMillis(),
+                                fileResult.getDex2oatCpuTimeMillis());
                     }
                 }
                 return 0;
diff --git a/libartservice/service/java/com/android/server/art/DexOptHelper.java b/libartservice/service/java/com/android/server/art/DexOptHelper.java
index de15dd4..e09bcf8 100644
--- a/libartservice/service/java/com/android/server/art/DexOptHelper.java
+++ b/libartservice/service/java/com/android/server/art/DexOptHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.server.art;
 
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
 
 import android.annotation.NonNull;
@@ -77,7 +77,7 @@
             @NonNull PackageState pkgState, @NonNull AndroidPackageApi pkg,
             @NonNull OptimizeParams params, @NonNull CancellationSignal cancellationSignal)
             throws RemoteException {
-        List<DexFileOptimizeResult> results = new ArrayList<>();
+        List<DexContainerFileOptimizeResult> results = new ArrayList<>();
         Supplier<OptimizeResult> createResult = ()
                 -> new OptimizeResult(params.getCompilerFilter(), params.getReason(),
                         List.of(new PackageOptimizeResult(pkgState.getPackageName(), results)));
diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java b/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
index 54a181a..f30fe0b 100644
--- a/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
+++ b/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
@@ -22,8 +22,9 @@
 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
 import static com.android.server.art.ProfilePath.RefProfilePath;
 import static com.android.server.art.ProfilePath.TmpRefProfilePath;
+import static com.android.server.art.Utils.Abi;
 import static com.android.server.art.model.ArtFlags.OptimizeFlags;
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 
 import android.R;
 import android.annotation.NonNull;
@@ -72,10 +73,10 @@
      * ArtManagerLocal#optimizePackage(PackageDataSnapshot, String, OptimizeParams)}.
      */
     @NonNull
-    public List<DexFileOptimizeResult> dexopt(@NonNull PackageState pkgState,
+    public List<DexContainerFileOptimizeResult> dexopt(@NonNull PackageState pkgState,
             @NonNull AndroidPackageApi pkg, @NonNull OptimizeParams params,
             @NonNull CancellationSignal cancellationSignal) throws RemoteException {
-        List<DexFileOptimizeResult> results = new ArrayList<>();
+        List<DexContainerFileOptimizeResult> results = new ArrayList<>();
 
         int uid = pkg.getUid();
         if (uid < 0) {
@@ -143,14 +144,14 @@
                 DexoptOptions dexoptOptions =
                         getDexoptOptions(pkgState, pkg, params, isProfileGuidedCompilerFilter);
 
-                for (String isa : Utils.getAllIsas(pkgState)) {
+                for (Abi abi : Utils.getAllAbis(pkgState)) {
                     @OptimizeResult.OptimizeStatus int status = OptimizeResult.OPTIMIZE_SKIPPED;
                     long wallTimeMs = 0;
                     long cpuTimeMs = 0;
                     try {
                         DexoptTarget target = DexoptTarget.builder()
                                                       .setDexInfo(dexInfo)
-                                                      .setIsa(isa)
+                                                      .setIsa(abi.isa())
                                                       .setIsInDalvikCache(isInDalvikCache)
                                                       .setCompilerFilter(compilerFilter)
                                                       .build();
@@ -199,13 +200,14 @@
                         Log.e(TAG,
                                 String.format("Failed to dexopt [packageName = %s, dexPath = %s, "
                                                 + "isa = %s, classLoaderContext = %s]",
-                                        pkgState.getPackageName(), dexInfo.dexPath(), isa,
+                                        pkgState.getPackageName(), dexInfo.dexPath(), abi.isa(),
                                         dexInfo.classLoaderContext()),
                                 e);
                         status = OptimizeResult.OPTIMIZE_FAILED;
                     } finally {
-                        results.add(new DexFileOptimizeResult(dexInfo.dexPath(), isa,
-                                compilerFilter, status, wallTimeMs, cpuTimeMs));
+                        results.add(new DexContainerFileOptimizeResult(dexInfo.dexPath(),
+                                abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs,
+                                cpuTimeMs));
                         if (status != OptimizeResult.OPTIMIZE_SKIPPED
                                 && status != OptimizeResult.OPTIMIZE_PERFORMED) {
                             succeeded = false;
diff --git a/libartservice/service/java/com/android/server/art/Utils.java b/libartservice/service/java/com/android/server/art/Utils.java
index 8c1cfbe..134dc9f 100644
--- a/libartservice/service/java/com/android/server/art/Utils.java
+++ b/libartservice/service/java/com/android/server/art/Utils.java
@@ -26,9 +26,12 @@
 import com.android.server.art.wrapper.PackageManagerLocal;
 import com.android.server.art.wrapper.PackageState;
 
+import com.google.auto.value.AutoValue;
+
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -58,17 +61,25 @@
     }
 
     @NonNull
-    public static List<String> getAllIsas(@NonNull PackageState pkgState) {
+    public static List<Abi> getAllAbis(@NonNull PackageState pkgState) {
+        List<Abi> abis = new ArrayList<>();
         String primaryCpuAbi = pkgState.getPrimaryCpuAbi();
-        String secondaryCpuAbi = pkgState.getSecondaryCpuAbi();
         if (primaryCpuAbi != null) {
-            if (secondaryCpuAbi != null) {
-                return List.of(VMRuntime.getInstructionSet(primaryCpuAbi),
-                        VMRuntime.getInstructionSet(secondaryCpuAbi));
-            }
-            return List.of(VMRuntime.getInstructionSet(primaryCpuAbi));
+            abis.add(Abi.create(primaryCpuAbi, VMRuntime.getInstructionSet(primaryCpuAbi),
+                    true /* isPrimaryAbi */));
         }
-        return List.of();
+        String secondaryCpuAbi = pkgState.getSecondaryCpuAbi();
+        if (secondaryCpuAbi != null) {
+            abis.add(Abi.create(secondaryCpuAbi, VMRuntime.getInstructionSet(secondaryCpuAbi),
+                    false /* isPrimaryAbi */));
+        }
+        // Primary and secondary ABIs are guaranteed to have different ISAs.
+        if (abis.size() == 2 && abis.get(0).isa().equals(abis.get(1).isa())) {
+            throw new IllegalStateException(
+                    String.format("Duplicate ISA: primary ABI '%s', secondary ABI '%s'",
+                            primaryCpuAbi, secondaryCpuAbi));
+        }
+        return abis;
     }
 
     public static boolean isInDalvikCache(@NonNull PackageState pkg) {
@@ -95,4 +106,20 @@
     public static boolean implies(boolean cond1, boolean cond2) {
         return cond1 ? cond2 : true;
     }
+
+    @AutoValue
+    public abstract static class Abi {
+        static @NonNull Abi create(
+                @NonNull String name, @NonNull String isa, boolean isPrimaryAbi) {
+            return new AutoValue_Utils_Abi(name, isa, isPrimaryAbi);
+        }
+
+        // The ABI name. E.g., "arm64-v8a".
+        abstract @NonNull String name();
+
+        // The instruction set name. E.g., "arm64".
+        abstract @NonNull String isa();
+
+        abstract boolean isPrimaryAbi();
+    }
 }
diff --git a/libartservice/service/java/com/android/server/art/model/OptimizationStatus.java b/libartservice/service/java/com/android/server/art/model/OptimizationStatus.java
index 724b0dd..f156e97 100644
--- a/libartservice/service/java/com/android/server/art/model/OptimizationStatus.java
+++ b/libartservice/service/java/com/android/server/art/model/OptimizationStatus.java
@@ -32,60 +32,79 @@
 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 @Immutable
 public class OptimizationStatus {
-    private final @NonNull List<DexFileOptimizationStatus> mDexFileOptimizationStatuses;
+    private final
+            @NonNull List<DexContainerFileOptimizationStatus> mDexContainerFileOptimizationStatuses;
 
     /** @hide */
-    public OptimizationStatus(
-            @NonNull List<DexFileOptimizationStatus> dexFileOptimizationStatuses) {
-        mDexFileOptimizationStatuses = dexFileOptimizationStatuses;
+    public OptimizationStatus(@NonNull List<DexContainerFileOptimizationStatus>
+                    dexContainerFileOptimizationStatuses) {
+        mDexContainerFileOptimizationStatuses = dexContainerFileOptimizationStatuses;
     }
 
-    /** The optimization status of each individual dex file. */
+    /**
+     * The statuses of the dex container file optimizations. Note that there can be
+     * multiple entries for the same dex container file, but for different ABIs.
+     */
     @NonNull
-    public List<DexFileOptimizationStatus> getDexFileOptimizationStatuses() {
-        return mDexFileOptimizationStatuses;
+    public List<DexContainerFileOptimizationStatus> getDexContainerFileOptimizationStatuses() {
+        return mDexContainerFileOptimizationStatuses;
     }
 
-    /** Describes the optimization status of a dex file. */
+    /** Describes the optimization status of a dex container file. */
     @Immutable
-    public static class DexFileOptimizationStatus {
-        private final @NonNull String mDexFile;
-        private final @NonNull String mInstructionSet;
+    public static class DexContainerFileOptimizationStatus {
+        private final @NonNull String mDexContainerFile;
+        private final @NonNull boolean mIsPrimaryAbi;
+        private final @NonNull String mAbi;
         private final @NonNull String mCompilerFilter;
         private final @NonNull String mCompilationReason;
         private final @NonNull String mLocationDebugString;
 
         /** @hide */
-        public DexFileOptimizationStatus(@NonNull String dexFile, @NonNull String instructionSet,
-                @NonNull String compilerFilter, @NonNull String compilationReason,
-                @NonNull String locationDebugString) {
-            mDexFile = dexFile;
-            mInstructionSet = instructionSet;
+        public DexContainerFileOptimizationStatus(@NonNull String dexContainerFile,
+                boolean isPrimaryAbi, @NonNull String abi, @NonNull String compilerFilter,
+                @NonNull String compilationReason, @NonNull String locationDebugString) {
+            mDexContainerFile = dexContainerFile;
+            mIsPrimaryAbi = isPrimaryAbi;
+            mAbi = abi;
             mCompilerFilter = compilerFilter;
             mCompilationReason = compilationReason;
             mLocationDebugString = locationDebugString;
         }
 
-        /** The absolute path to the dex file. */
-        public @NonNull String getDexFile() {
-            return mDexFile;
-        }
-
-        /** The instruction set. */
-        public @NonNull String getInstructionSet() {
-            return mInstructionSet;
+        /** The absolute path to the dex container file. */
+        public @NonNull String getDexContainerFile() {
+            return mDexContainerFile;
         }
 
         /**
-         * A string that describes the compiler filter.
+         * If true, the optimization is for the primary ABI of the package (the ABI that the
+         * application is launched with). Otherwise, the optimization is for an ABI that other
+         * applications might be launched with when using this application's code.
+         */
+        public boolean isPrimaryAbi() {
+            return mIsPrimaryAbi;
+        }
+
+        /**
+         * Returns the ABI that the optimization is for. Possible values are documented at
+         * https://developer.android.com/ndk/guides/abis#sa.
+         */
+        public @NonNull String getAbi() {
+            return mAbi;
+        }
+
+        /**
+         * A human-readable string that describes the compiler filter.
          *
          * Possible values are:
          * <ul>
          *   <li>A valid value of the {@code --compiler-filer} option passed to {@code dex2oat}, if
-         *     the optimized artifacts are valid.
+         *     the optimized artifacts are valid. See
+         *     https://source.android.com/docs/core/dalvik/configure#compilation_options.
          *   <li>{@code "run-from-apk"}, if the optimized artifacts do not exist.
          *   <li>{@code "run-from-apk-fallback"}, if the optimized artifacts exist but are invalid
-         *     because the dex file has changed.
+         *     because the dex container file has changed.
          *   <li>{@code "error"}, if an unexpected error occurs.
          * </ul>
          */
diff --git a/libartservice/service/java/com/android/server/art/model/OptimizeParams.java b/libartservice/service/java/com/android/server/art/model/OptimizeParams.java
index fdfb6b8..e8d1554 100644
--- a/libartservice/service/java/com/android/server/art/model/OptimizeParams.java
+++ b/libartservice/service/java/com/android/server/art/model/OptimizeParams.java
@@ -38,7 +38,11 @@
          *
          * Uses default flags ({@link ArtFlags#defaultOptimizeFlags()}).
          *
-         * @param reason See {@link #setReason(String)}.
+         * @param reason Compilation reason. Can be a string defined in {@link ReasonMapping} or a
+         *         custom string. If the value is a string defined in {@link ReasonMapping}, it
+         *         determines the compiler filter and/or the priority class, if those values are not
+         *         explicitly set. If the value is a custom string, the priority class and the
+         *         compiler filter must be explicitly set.
          */
         public Builder(@NonNull String reason) {
             this(reason, ArtFlags.defaultOptimizeFlags());
@@ -48,7 +52,7 @@
          * Same as above, but allows to specify flags.
          */
         public Builder(@NonNull String reason, @OptimizeFlags int flags) {
-            setReason(reason);
+            mParams.mReason = reason;
             setFlags(flags);
         }
 
@@ -94,21 +98,6 @@
         }
 
         /**
-         * Compilation reason. Can be a string defined in {@link ReasonMapping} or a custom string.
-         *
-         * If the value is a string defined in {@link ReasonMapping}, it determines the compiler
-         * filter and/or the priority class, if those values are not explicitly set.
-         *
-         * If the value is a custom string, the priority class and the compiler filter must be
-         * explicitly set.
-         */
-        @NonNull
-        public Builder setReason(@NonNull String value) {
-            mParams.mReason = value;
-            return this;
-        }
-
-        /**
          * Returns the built object.
          *
          * @throws IllegalArgumentException if the built options would be invalid
diff --git a/libartservice/service/java/com/android/server/art/model/OptimizeResult.java b/libartservice/service/java/com/android/server/art/model/OptimizeResult.java
index 8a4449b..5ee5016 100644
--- a/libartservice/service/java/com/android/server/art/model/OptimizeResult.java
+++ b/libartservice/service/java/com/android/server/art/model/OptimizeResult.java
@@ -32,8 +32,8 @@
 @Immutable
 public class OptimizeResult {
     // Possible values of {@link #OptimizeStatus}.
-    // A larger number means a higher priority. If multiple dex files are processed, the final
-    // status will be the one with the highest priority.
+    // A larger number means a higher priority. If multiple dex container files are processed, the
+    // final status will be the one with the highest priority.
     public static final int OPTIMIZE_SKIPPED = 10;
     public static final int OPTIMIZE_PERFORMED = 20;
     public static final int OPTIMIZE_FAILED = 30;
@@ -68,7 +68,8 @@
      * execution based on factors like whether the profile is available or whether the app is
      * used by other apps.
      *
-     * @see DexFileOptimizeResult#getActualCompilerFilter.
+     * @see OptimizeParams.Builder#setCompilerFilter(String)
+     * @see DexContainerFileOptimizeResult#getActualCompilerFilter()
      */
     public @NonNull String getRequestedCompilerFilter() {
         return mRequestedCompilerFilter;
@@ -109,13 +110,14 @@
     @Immutable
     public static class PackageOptimizeResult {
         private final @NonNull String mPackageName;
-        private final @NonNull List<DexFileOptimizeResult> mDexFileOptimizeResults;
+        private final
+                @NonNull List<DexContainerFileOptimizeResult> mDexContainerFileOptimizeResults;
 
         /** @hide */
         public PackageOptimizeResult(@NonNull String packageName,
-                @NonNull List<DexFileOptimizeResult> dexFileOptimizeResults) {
+                @NonNull List<DexContainerFileOptimizeResult> dexContainerFileOptimizeResults) {
             mPackageName = packageName;
-            mDexFileOptimizeResults = dexFileOptimizeResults;
+            mDexContainerFileOptimizeResults = dexContainerFileOptimizeResults;
         }
 
         /** The package name. */
@@ -123,59 +125,80 @@
             return mPackageName;
         }
 
-        /** The result of each individual dex file. */
+        /**
+         * The results of optimizing dex container files. Note that there can be multiple entries
+         * for the same dex container file, but for different ABIs.
+         */
         @NonNull
-        public List<DexFileOptimizeResult> getDexFileOptimizeResults() {
-            return mDexFileOptimizeResults;
+        public List<DexContainerFileOptimizeResult> getDexContainerFileOptimizeResults() {
+            return mDexContainerFileOptimizeResults;
         }
 
         /** The overall status of the package. */
         public @OptimizeStatus int getStatus() {
-            return mDexFileOptimizeResults.stream()
+            return mDexContainerFileOptimizeResults.stream()
                     .mapToInt(result -> result.getStatus())
                     .max()
                     .orElse(OPTIMIZE_SKIPPED);
         }
     }
 
-    /** Describes the result of a dex file. */
+    /** Describes the result of optimizing a dex container file. */
     @Immutable
-    public static class DexFileOptimizeResult {
-        private final @NonNull String mDexFile;
-        private final @NonNull String mInstructionSet;
+    public static class DexContainerFileOptimizeResult {
+        private final @NonNull String mDexContainerFile;
+        private final boolean mIsPrimaryAbi;
+        private final @NonNull String mAbi;
         private final @NonNull String mActualCompilerFilter;
         private final @OptimizeStatus int mStatus;
         private final long mDex2oatWallTimeMillis;
         private final long mDex2oatCpuTimeMillis;
 
         /** @hide */
-        public DexFileOptimizeResult(@NonNull String dexFile, @NonNull String instructionSet,
-                @NonNull String compilerFilter, @OptimizeStatus int status,
-                long dex2oatWallTimeMillis, long dex2oatCpuTimeMillis) {
-            mDexFile = dexFile;
-            mInstructionSet = instructionSet;
+        public DexContainerFileOptimizeResult(@NonNull String dexContainerFile,
+                boolean isPrimaryAbi, @NonNull String abi, @NonNull String compilerFilter,
+                @OptimizeStatus int status, long dex2oatWallTimeMillis, long dex2oatCpuTimeMillis) {
+            mDexContainerFile = dexContainerFile;
+            mIsPrimaryAbi = isPrimaryAbi;
+            mAbi = abi;
             mActualCompilerFilter = compilerFilter;
             mStatus = status;
             mDex2oatWallTimeMillis = dex2oatWallTimeMillis;
             mDex2oatCpuTimeMillis = dex2oatCpuTimeMillis;
         }
 
-        /** The absolute path to the dex file. */
-        public @NonNull String getDexFile() {
-            return mDexFile;
+        /** The absolute path to the dex container file. */
+        public @NonNull String getDexContainerFile() {
+            return mDexContainerFile;
         }
 
-        /** The instruction set. */
-        public @NonNull String getInstructionSet() {
-            return mInstructionSet;
+        /**
+         * If true, the optimization is for the primary ABI of the package (the ABI that the
+         * application is launched with). Otherwise, the optimization is for an ABI that other
+         * applications might be launched with when using this application's code.
+         */
+        public boolean isPrimaryAbi() {
+            return mIsPrimaryAbi;
         }
 
-        /** The actual compiler filter. */
+        /**
+         * Returns the ABI that the optimization is for. Possible values are documented at
+         * https://developer.android.com/ndk/guides/abis#sa.
+         */
+        public @NonNull String getAbi() {
+            return mAbi;
+        }
+
+        /**
+         * The actual compiler filter.
+         *
+         * @see OptimizeParams.Builder#setCompilerFilter(String)
+         */
         public @NonNull String getActualCompilerFilter() {
             return mActualCompilerFilter;
         }
 
-        /** The status of optimizing this dex file. */
+        /** The status of optimizing this dex container file. */
         public @OptimizeStatus int getStatus() {
             return mStatus;
         }
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index f534509..b10e31b 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.art;
 
-import static com.android.server.art.model.OptimizationStatus.DexFileOptimizationStatus;
+import static com.android.server.art.model.OptimizationStatus.DexContainerFileOptimizationStatus;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -158,29 +158,34 @@
         OptimizationStatus result =
                 mArtManagerLocal.getOptimizationStatus(mock(PackageDataSnapshot.class), PKG_NAME);
 
-        List<DexFileOptimizationStatus> statuses = result.getDexFileOptimizationStatuses();
+        List<DexContainerFileOptimizationStatus> statuses =
+                result.getDexContainerFileOptimizationStatuses();
         assertThat(statuses.size()).isEqualTo(4);
 
-        assertThat(statuses.get(0).getDexFile()).isEqualTo("/data/app/foo/base.apk");
-        assertThat(statuses.get(0).getInstructionSet()).isEqualTo("arm64");
+        assertThat(statuses.get(0).getDexContainerFile()).isEqualTo("/data/app/foo/base.apk");
+        assertThat(statuses.get(0).isPrimaryAbi()).isEqualTo(true);
+        assertThat(statuses.get(0).getAbi()).isEqualTo("arm64-v8a");
         assertThat(statuses.get(0).getCompilerFilter()).isEqualTo("speed");
         assertThat(statuses.get(0).getCompilationReason()).isEqualTo("compilation-reason-0");
         assertThat(statuses.get(0).getLocationDebugString()).isEqualTo("location-debug-string-0");
 
-        assertThat(statuses.get(1).getDexFile()).isEqualTo("/data/app/foo/base.apk");
-        assertThat(statuses.get(1).getInstructionSet()).isEqualTo("arm");
+        assertThat(statuses.get(1).getDexContainerFile()).isEqualTo("/data/app/foo/base.apk");
+        assertThat(statuses.get(1).isPrimaryAbi()).isEqualTo(false);
+        assertThat(statuses.get(1).getAbi()).isEqualTo("armeabi-v7a");
         assertThat(statuses.get(1).getCompilerFilter()).isEqualTo("speed-profile");
         assertThat(statuses.get(1).getCompilationReason()).isEqualTo("compilation-reason-1");
         assertThat(statuses.get(1).getLocationDebugString()).isEqualTo("location-debug-string-1");
 
-        assertThat(statuses.get(2).getDexFile()).isEqualTo("/data/app/foo/split_0.apk");
-        assertThat(statuses.get(2).getInstructionSet()).isEqualTo("arm64");
+        assertThat(statuses.get(2).getDexContainerFile()).isEqualTo("/data/app/foo/split_0.apk");
+        assertThat(statuses.get(2).isPrimaryAbi()).isEqualTo(true);
+        assertThat(statuses.get(2).getAbi()).isEqualTo("arm64-v8a");
         assertThat(statuses.get(2).getCompilerFilter()).isEqualTo("verify");
         assertThat(statuses.get(2).getCompilationReason()).isEqualTo("compilation-reason-2");
         assertThat(statuses.get(2).getLocationDebugString()).isEqualTo("location-debug-string-2");
 
-        assertThat(statuses.get(3).getDexFile()).isEqualTo("/data/app/foo/split_0.apk");
-        assertThat(statuses.get(3).getInstructionSet()).isEqualTo("arm");
+        assertThat(statuses.get(3).getDexContainerFile()).isEqualTo("/data/app/foo/split_0.apk");
+        assertThat(statuses.get(3).isPrimaryAbi()).isEqualTo(false);
+        assertThat(statuses.get(3).getAbi()).isEqualTo("armeabi-v7a");
         assertThat(statuses.get(3).getCompilerFilter()).isEqualTo("extract");
         assertThat(statuses.get(3).getCompilationReason()).isEqualTo("compilation-reason-3");
         assertThat(statuses.get(3).getLocationDebugString()).isEqualTo("location-debug-string-3");
@@ -208,10 +213,11 @@
         OptimizationStatus result =
                 mArtManagerLocal.getOptimizationStatus(mock(PackageDataSnapshot.class), PKG_NAME);
 
-        List<DexFileOptimizationStatus> statuses = result.getDexFileOptimizationStatuses();
+        List<DexContainerFileOptimizationStatus> statuses =
+                result.getDexContainerFileOptimizationStatuses();
         assertThat(statuses.size()).isEqualTo(4);
 
-        for (DexFileOptimizationStatus status : statuses) {
+        for (DexContainerFileOptimizationStatus status : statuses) {
             assertThat(status.getCompilerFilter()).isEqualTo("error");
             assertThat(status.getCompilationReason()).isEqualTo("error");
             assertThat(status.getLocationDebugString()).isEqualTo("some error message");
diff --git a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
index 5e2314e..095d828 100644
--- a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
+++ b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.art;
 
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -77,13 +77,13 @@
 
     private final OptimizeParams mParams =
             new OptimizeParams.Builder("install").setCompilerFilter("speed-profile").build();
-    private final List<DexFileOptimizeResult> mPrimaryResults =
-            List.of(new DexFileOptimizeResult("/data/app/foo/base.apk", "arm64", "verify",
-                            OptimizeResult.OPTIMIZE_PERFORMED, 100 /* dex2oatWallTimeMillis */,
-                            400 /* dex2oatCpuTimeMillis */),
-                    new DexFileOptimizeResult("/data/app/foo/base.apk", "arm", "verify",
-                            OptimizeResult.OPTIMIZE_FAILED, 100 /* dex2oatWallTimeMillis */,
-                            400 /* dex2oatCpuTimeMillis */));
+    private final List<DexContainerFileOptimizeResult> mPrimaryResults = List.of(
+            new DexContainerFileOptimizeResult("/data/app/foo/base.apk", true /* isPrimaryAbi */,
+                    "arm64-v8a", "verify", OptimizeResult.OPTIMIZE_PERFORMED,
+                    100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */),
+            new DexContainerFileOptimizeResult("/data/app/foo/base.apk", false /* isPrimaryAbi */,
+                    "armeabi-v7a", "verify", OptimizeResult.OPTIMIZE_FAILED,
+                    100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */));
 
     private DexOptHelper mDexOptHelper;
 
@@ -123,7 +123,7 @@
 
         PackageOptimizeResult packageResult = result.getPackageOptimizeResults().get(0);
         assertThat(packageResult.getPackageName()).isEqualTo(PKG_NAME);
-        assertThat(packageResult.getDexFileOptimizeResults())
+        assertThat(packageResult.getDexContainerFileOptimizeResults())
                 .containsExactlyElementsIn(mPrimaryResults);
 
         InOrder inOrder = inOrder(mPrimaryDexOptimizer, mWakeLock);
@@ -140,7 +140,8 @@
                 mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
 
         assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
-        assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults()).isEmpty();
+        assertThat(result.getPackageOptimizeResults().get(0).getDexContainerFileOptimizeResults())
+                .isEmpty();
     }
 
     @Test
@@ -151,7 +152,8 @@
                 mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
 
         assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
-        assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults()).isEmpty();
+        assertThat(result.getPackageOptimizeResults().get(0).getDexContainerFileOptimizeResults())
+                .isEmpty();
     }
 
     @Test
@@ -166,7 +168,7 @@
         OptimizeResult result = mDexOptHelper.dexopt(
                 mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
 
-        assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults())
+        assertThat(result.getPackageOptimizeResults().get(0).getDexContainerFileOptimizeResults())
                 .containsExactlyElementsIn(mPrimaryResults);
     }
 
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerParameterizedTest.java
index 0038456..000858c 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerParameterizedTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerParameterizedTest.java
@@ -20,7 +20,7 @@
 import static com.android.server.art.AidlUtils.buildOutputArtifacts;
 import static com.android.server.art.AidlUtils.buildPermissionSettings;
 import static com.android.server.art.OutputArtifacts.PermissionSettings;
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.testing.TestingUtils.deepEq;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -257,18 +257,22 @@
 
         assertThat(
                 mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal))
-                .comparingElementsUsing(TestingUtils.<DexFileOptimizeResult>deepEquality())
+                .comparingElementsUsing(TestingUtils.<DexContainerFileOptimizeResult>deepEquality())
                 .containsExactly(
-                        new DexFileOptimizeResult("/data/app/foo/base.apk", "arm64",
+                        new DexContainerFileOptimizeResult("/data/app/foo/base.apk",
+                                true /* isPrimaryAbi */, "arm64-v8a",
                                 mParams.mExpectedCompilerFilter, OptimizeResult.OPTIMIZE_PERFORMED,
                                 100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */),
-                        new DexFileOptimizeResult("/data/app/foo/base.apk", "arm",
+                        new DexContainerFileOptimizeResult("/data/app/foo/base.apk",
+                                false /* isPrimaryAbi */, "armeabi-v7a",
                                 mParams.mExpectedCompilerFilter, OptimizeResult.OPTIMIZE_FAILED,
                                 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */),
-                        new DexFileOptimizeResult("/data/app/foo/split_0.apk", "arm64",
+                        new DexContainerFileOptimizeResult("/data/app/foo/split_0.apk",
+                                true /* isPrimaryAbi */, "arm64-v8a",
                                 mParams.mExpectedCompilerFilter, OptimizeResult.OPTIMIZE_SKIPPED,
                                 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */),
-                        new DexFileOptimizeResult("/data/app/foo/split_0.apk", "arm",
+                        new DexContainerFileOptimizeResult("/data/app/foo/split_0.apk",
+                                false /* isPrimaryAbi */, "armeabi-v7a",
                                 mParams.mExpectedCompilerFilter, OptimizeResult.OPTIMIZE_PERFORMED,
                                 200 /* dex2oatWallTimeMillis */, 200 /* dex2oatCpuTimeMillis */));
     }
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
index 470fa6f..8eb0bfe 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
@@ -17,7 +17,7 @@
 package com.android.server.art;
 
 import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
-import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
+import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.testing.TestingUtils.deepEq;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -377,7 +377,7 @@
         assertThat(
                 mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal)
                         .stream()
-                        .map(DexFileOptimizeResult::getStatus)
+                        .map(DexContainerFileOptimizeResult::getStatus)
                         .collect(Collectors.toList()))
                 .containsExactly(OptimizeResult.OPTIMIZE_CANCELLED);
 
@@ -412,7 +412,7 @@
                 .when(artdCancellationSignal)
                 .cancel();
 
-        Future<List<DexFileOptimizeResult>> results =
+        Future<List<DexContainerFileOptimizeResult>> results =
                 Executors.newSingleThreadExecutor().submit(() -> {
                     return mPrimaryDexOptimizer.dexopt(
                             mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
@@ -424,7 +424,7 @@
 
         assertThat(results.get()
                            .stream()
-                           .map(DexFileOptimizeResult::getStatus)
+                           .map(DexContainerFileOptimizeResult::getStatus)
                            .collect(Collectors.toList()))
                 .containsExactly(OptimizeResult.OPTIMIZE_CANCELLED);