Expose extra status codes and external profile errors as APIs.

Bug: 278080573
Test: m
Change-Id: Ib0ee9d2407a86c4f32b3b0ad2f8b18ac1f1d9004
Merged-In: Ib0ee9d2407a86c4f32b3b0ad2f8b18ac1f1d9004
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index a9cd65a..f885980 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -142,6 +142,9 @@
     field public static final int DEXOPT_FAILED = 30; // 0x1e
     field public static final int DEXOPT_PERFORMED = 20; // 0x14
     field public static final int DEXOPT_SKIPPED = 10; // 0xa
+    field public static final int EXTRA_BAD_EXTERNAL_PROFILE = 4; // 0x4
+    field public static final int EXTRA_SKIPPED_NO_DEX_CODE = 2; // 0x2
+    field public static final int EXTRA_SKIPPED_STORAGE_LOW = 1; // 0x1
   }
 
   public abstract static class DexoptResult.DexContainerFileDexoptResult {
@@ -150,6 +153,8 @@
     method public abstract long getDex2oatCpuTimeMillis();
     method public abstract long getDex2oatWallTimeMillis();
     method @NonNull public abstract String getDexContainerFile();
+    method @NonNull public abstract java.util.List<java.lang.String> getExternalProfileErrors();
+    method public abstract int getExtraStatuses();
     method public abstract long getSizeBeforeBytes();
     method public abstract long getSizeBytes();
     method public abstract int getStatus();
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
index 533e340..c87e9f7 100644
--- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
@@ -51,7 +51,7 @@
                 .filter(packageResult
                         -> packageResult.getDexContainerFileDexoptResults().stream().anyMatch(
                                 fileResult
-                                -> (fileResult.getExtraStatus()
+                                -> (fileResult.getExtraStatuses()
                                            & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
                                         == 0))
                 .collect(Collectors.toList());
@@ -75,7 +75,7 @@
                         .flatMap(packageResult
                                 -> packageResult.getDexContainerFileDexoptResults().stream())
                         .anyMatch(fileResult
-                                -> (fileResult.getExtraStatus()
+                                -> (fileResult.getExtraStatuses()
                                            & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
                                         != 0);
         if (isSkippedDueToStorageLow) {
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptResult.java b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
index 55298af..45704e8 100644
--- a/libartservice/service/java/com/android/server/art/model/DexoptResult.java
+++ b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
@@ -63,11 +63,26 @@
     public @interface DexoptResultStatus {}
 
     // Possible values of {@link #DexoptResultExtraStatus}.
-    /** @hide */
+    /** Dexopt is skipped because the remaining storage space is low. */
     public static final int EXTRA_SKIPPED_STORAGE_LOW = 1 << 0;
-    /** @hide */
+    /**
+     * Dexopt is skipped because the dex container file has no dex code while the manifest declares
+     * that it does.
+     *
+     * Note that this flag doesn't apply to dex container files that are not declared to have code.
+     * Instead, those files are not listed in {@link
+     * PackageDexoptResult#getDexContainerFileDexoptResults} in the first place.
+     */
     public static final int EXTRA_SKIPPED_NO_DEX_CODE = 1 << 1;
-    /** @hide */
+    /**
+     * Dexopt encountered errors when processing the profiles that are external to the device,
+     * including the profile in the DM file and the profile embedded in the dex container file.
+     * Details of the errors can be found in {@link
+     * DexContainerFileDexoptResult#getExternalProfileErrors}.
+     *
+     * This is not a critical error. Dexopt may still have succeeded after ignoring the bad external
+     * profiles.
+     */
     public static final int EXTRA_BAD_EXTERNAL_PROFILE = 1 << 2;
 
     /** @hide */
@@ -301,10 +316,26 @@
          */
         public abstract long getSizeBeforeBytes();
 
-        /** @hide */
-        public abstract @DexoptResultExtraStatus int getExtraStatus();
+        /**
+         * A bitfield of the extended status flags.
+         *
+         * Flags that starts with `EXTRA_SKIPPED_` are a subset of the reasons why dexopt is
+         * skipped. Note that they don't cover all possible reasons. At most one `EXTRA_SKIPPED_`
+         * flag will be set, even if the situation meets multiple `EXTRA_SKIPPED_` flags. The order
+         * of precedence of those flags is undefined.
+         */
+        public abstract @DexoptResultExtraStatus int getExtraStatuses();
 
-        /** @hide */
+        /**
+         * Details of errors occurred when processing external profiles, one error per profile file
+         * that the dexopter tried to read.
+         *
+         * If the same dex container file is dexopted for multiple ABIs, the same profile errors
+         * will be repeated for each ABI in the {@link DexContainerFileDexoptResult}s of the same
+         * dex container file.
+         *
+         * @see #EXTRA_BAD_EXTERNAL_PROFILE.
+         */
         public abstract @NonNull List<String> getExternalProfileErrors();
 
         @Override
@@ -325,7 +356,7 @@
                     DexoptResult.dexoptResultStatusToString(getStatus()),
                     getDex2oatWallTimeMillis(), getDex2oatCpuTimeMillis(), getSizeBytes(),
                     getSizeBeforeBytes(),
-                    DexoptResult.dexoptResultExtraStatusToString(getExtraStatus()));
+                    DexoptResult.dexoptResultExtraStatusToString(getExtraStatuses()));
         }
     }
 }
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
index aa5203d..3fce031 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
@@ -401,11 +401,11 @@
         List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
 
         assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-        assertThat(results.get(0).getExtraStatus() & DexoptResult.EXTRA_BAD_EXTERNAL_PROFILE)
+        assertThat(results.get(0).getExtraStatuses() & DexoptResult.EXTRA_BAD_EXTERNAL_PROFILE)
                 .isNotEqualTo(0);
         assertThat(results.get(0).getExternalProfileErrors()).containsExactly("error_msg");
         assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-        assertThat(results.get(1).getExtraStatus() & DexoptResult.EXTRA_BAD_EXTERNAL_PROFILE)
+        assertThat(results.get(1).getExtraStatuses() & DexoptResult.EXTRA_BAD_EXTERNAL_PROFILE)
                 .isNotEqualTo(0);
         assertThat(results.get(1).getExternalProfileErrors()).containsExactly("error_msg");
     }
@@ -648,16 +648,16 @@
 
         List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
         assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-        assertThat(results.get(0).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
+        assertThat(results.get(0).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
                 .isEqualTo(0);
         assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
-        assertThat(results.get(1).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
+        assertThat(results.get(1).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
                 .isNotEqualTo(0);
         assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
-        assertThat(results.get(2).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
+        assertThat(results.get(2).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
                 .isNotEqualTo(0);
         assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-        assertThat(results.get(3).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
+        assertThat(results.get(3).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_STORAGE_LOW)
                 .isEqualTo(0);
 
         verify(mArtd, times(2))
@@ -682,16 +682,16 @@
 
         List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
         assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
-        assertThat(results.get(0).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
+        assertThat(results.get(0).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
                 .isNotEqualTo(0);
         assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
-        assertThat(results.get(1).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
+        assertThat(results.get(1).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
                 .isNotEqualTo(0);
         assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
-        assertThat(results.get(2).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
+        assertThat(results.get(2).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
                 .isEqualTo(0);
         assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-        assertThat(results.get(3).getExtraStatus() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
+        assertThat(results.get(3).getExtraStatuses() & DexoptResult.EXTRA_SKIPPED_NO_DEX_CODE)
                 .isEqualTo(0);
     }
 
@@ -744,7 +744,7 @@
     private void verifyStatusAllOk(List<DexContainerFileDexoptResult> results) {
         for (DexContainerFileDexoptResult result : results) {
             assertThat(result.getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
-            assertThat(result.getExtraStatus()).isEqualTo(0);
+            assertThat(result.getExtraStatuses()).isEqualTo(0);
             assertThat(result.getExternalProfileErrors()).isEmpty();
         }
     }