Use ART Service for boot compilation when it is enabled.

Test: Boot with dalvik.vm.useartservice=true
Bug: 251903639
Change-Id: I8367bc556bc3c57d24679a8940c7cfa7f93f353e
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 5dd6fe7..923c64d 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -60,6 +60,7 @@
 import com.android.server.LocalManagerRegistry;
 import com.android.server.art.ArtManagerLocal;
 import com.android.server.art.DexUseManagerLocal;
+import com.android.server.art.ReasonMapping;
 import com.android.server.art.model.ArtFlags;
 import com.android.server.art.model.DexoptParams;
 import com.android.server.art.model.DexoptResult;
@@ -84,6 +85,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Predicate;
 
 /**
@@ -94,6 +96,10 @@
 
     private final PackageManagerService mPm;
 
+    // Start time for the boot dexopt in performPackageDexOptUpgradeIfNeeded when ART Service is
+    // used, to make it available to the onDexoptDone callback.
+    private volatile long mBootDexoptStartTime;
+
     DexOptHelper(PackageManagerService pm) {
         mPm = pm;
     }
@@ -210,6 +216,7 @@
                 pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
             }
 
+            // TODO(b/251903639): Do this when ART Service is used, or remove it from here.
             if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
                 mPm.mArtManagerService.compileLayouts(packageState, pkg);
             }
@@ -317,7 +324,7 @@
      * Called during startup to do any boot time dexopting. This can occasionally be time consuming
      * (30+ seconds) and the function will block until it is complete.
      */
-    public void performPackageDexOptUpgradeIfNeeded() throws LegacyDexoptDisabledException {
+    public void performPackageDexOptUpgradeIfNeeded() {
         PackageManagerServiceUtils.enforceSystemOrRoot(
                 "Only the system can request package update");
 
@@ -332,29 +339,49 @@
             return;
         }
 
-        // System UI is important to user experience, so we check it after a mainline update
-        // or an OTA. It may need to be re-compiled in these cases.
-        checkAndDexOptSystemUi(reason);
-
-        if (reason != REASON_BOOT_AFTER_OTA && reason != REASON_FIRST_BOOT) {
-            return;
-        }
-
-        final Computer snapshot = mPm.snapshotComputer();
-        List<PackageStateInternal> pkgSettings =
-                getPackagesForDexopt(snapshot.getPackageStates().values(), mPm);
-
         final long startTime = System.nanoTime();
-        final int[] stats = performDexOptUpgrade(pkgSettings, reason, false /* bootComplete */);
 
+        if (useArtService()) {
+            mBootDexoptStartTime = startTime;
+            getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason),
+                    null /* progressCallbackExecutor */, null /* progressCallback */);
+        } else {
+            try {
+                // System UI is important to user experience, so we check it after a mainline update
+                // or an OTA. It may need to be re-compiled in these cases.
+                checkAndDexOptSystemUi(reason);
+
+                if (reason != REASON_BOOT_AFTER_OTA && reason != REASON_FIRST_BOOT) {
+                    return;
+                }
+
+                final Computer snapshot = mPm.snapshotComputer();
+
+                // TODO(b/251903639): Align this with how ART Service selects packages for boot
+                // compilation.
+                List<PackageStateInternal> pkgSettings =
+                        getPackagesForDexopt(snapshot.getPackageStates().values(), mPm);
+
+                final int[] stats =
+                        performDexOptUpgrade(pkgSettings, reason, false /* bootComplete */);
+                reportBootDexopt(startTime, stats[0], stats[1], stats[2]);
+            } catch (LegacyDexoptDisabledException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private void reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed) {
         final int elapsedTimeSeconds =
                 (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
 
         final Computer newSnapshot = mPm.snapshotComputer();
 
-        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", stats[0]);
-        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", stats[1]);
-        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", stats[2]);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", numDexopted);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", numSkipped);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", numFailed);
+        // TODO(b/251903639): getOptimizablePackages calls PackageDexOptimizer.canOptimizePackage
+        // which duplicates logic in ART Service (com.android.server.art.Utils.canDexoptPackage).
         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_total",
                 getOptimizablePackages(newSnapshot).size());
         MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
@@ -918,6 +945,32 @@
          */
         @Override
         public void onDexoptDone(@NonNull DexoptResult result) {
+            switch (result.getReason()) {
+                case ReasonMapping.REASON_FIRST_BOOT:
+                case ReasonMapping.REASON_BOOT_AFTER_OTA:
+                case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
+                    int numDexopted = 0;
+                    int numSkipped = 0;
+                    int numFailed = 0;
+                    for (DexoptResult.PackageDexoptResult pkgRes :
+                            result.getPackageDexoptResults()) {
+                        switch (pkgRes.getStatus()) {
+                            case DexoptResult.DEXOPT_PERFORMED:
+                                numDexopted += 1;
+                                break;
+                            case DexoptResult.DEXOPT_SKIPPED:
+                                numSkipped += 1;
+                                break;
+                            case DexoptResult.DEXOPT_FAILED:
+                                numFailed += 1;
+                                break;
+                        }
+                    }
+
+                    reportBootDexopt(mBootDexoptStartTime, numDexopted, numSkipped, numFailed);
+                    break;
+            }
+
             for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
                 CompilerStats.PackageStats stats =
                         mPm.getOrCreateCompilerPackageStats(pkgRes.getPackageName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0cd4988..f316d21 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2969,12 +2969,7 @@
     }
 
     public void updatePackagesIfNeeded() {
-        // TODO(b/251903639): Call into ART Service.
-        try {
-            mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
-        } catch (LegacyDexoptDisabledException e) {
-            throw new RuntimeException(e);
-        }
+        mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
     }
 
     private void notifyPackageUseInternal(String packageName, int reason) {