Add an API to handle boot events.
Bug: 260419279
Test: Presubmit
Ignore-AOSP-First: ART Services.
Change-Id: Id396a54ef616d4014873f6fe02077bb6d30d2359
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index 992c08c..de59623 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -13,6 +13,7 @@
method @NonNull public com.android.server.art.model.OptimizationStatus getOptimizationStatus(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String);
method @NonNull public com.android.server.art.model.OptimizationStatus getOptimizationStatus(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, int);
method public int handleShellCommand(@NonNull android.os.Binder, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull String[]);
+ method public void onBoot(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<com.android.server.art.model.OperationProgress>);
method @NonNull public com.android.server.art.model.OptimizeResult optimizePackage(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull com.android.server.art.model.OptimizeParams);
method @NonNull public com.android.server.art.model.OptimizeResult optimizePackage(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull com.android.server.art.model.OptimizeParams, @NonNull android.os.CancellationSignal);
method public void removeOptimizePackageDoneCallback(@NonNull com.android.server.art.ArtManagerLocal.OptimizePackageDoneCallback);
@@ -101,6 +102,10 @@
method public long getFreedBytes();
}
+ public abstract class OperationProgress {
+ method public int getPercentage();
+ }
+
public abstract class OptimizationStatus {
method @NonNull public abstract java.util.List<com.android.server.art.model.OptimizationStatus.DexContainerFileOptimizationStatus> getDexContainerFileOptimizationStatuses();
}
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index 847fb91..185d113 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -19,6 +19,7 @@
import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
import static com.android.server.art.ReasonMapping.BatchOptimizeReason;
+import static com.android.server.art.ReasonMapping.BootReason;
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;
@@ -49,9 +50,9 @@
import com.android.server.art.model.BatchOptimizeParams;
import com.android.server.art.model.Config;
import com.android.server.art.model.DeleteResult;
+import com.android.server.art.model.OperationProgress;
import com.android.server.art.model.OptimizationStatus;
import com.android.server.art.model.OptimizeParams;
-import com.android.server.art.model.OptimizeProgress;
import com.android.server.art.model.OptimizeResult;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -329,7 +330,7 @@
@NonNull @BatchOptimizeReason String reason,
@NonNull CancellationSignal cancellationSignal,
@Nullable @CallbackExecutor Executor processCallbackExecutor,
- @Nullable Consumer<OptimizeProgress> progressCallback) {
+ @Nullable Consumer<OperationProgress> progressCallback) {
List<String> defaultPackages =
Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
OptimizeParams defaultOptimizeParams = new OptimizeParams.Builder(reason).build();
@@ -590,6 +591,25 @@
}
/**
+ * Notifies ART Service that this is a boot that falls into one of the categories listed in
+ * {@link BootReason}. The current behavior is that ART Service goes through all recently used
+ * packages and optimizes those that are not optimized. This might change in the future.
+ *
+ * This method is blocking. It takes about 30 seconds to a few minutes. During execution, {@code
+ * progressCallback} is repeatedly called whenever there is an update on the progress.
+ *
+ * See {@link #optimizePackages} for how to customize the behavior.
+ */
+ public void onBoot(@NonNull @BootReason String bootReason,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
+ optimizePackages(snapshot, bootReason, new CancellationSignal(),
+ progressCallbackExecutor, progressCallback);
+ }
+ }
+
+ /**
* Should be used by {@link BackgroundDexOptJobService} ONLY.
*
* @hide
@@ -603,6 +623,7 @@
private List<String> getDefaultPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull @BatchOptimizeReason String reason) {
var packages = new ArrayList<String>();
+ // TODO(b/258818709): Filter packages by last active time.
snapshot.forAllPackageStates((pkgState) -> {
if (Utils.canOptimizePackage(pkgState, mInjector.getAppHibernationManager())) {
packages.add(pkgState.getPackageName());
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index 727803f..95996b3 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -38,6 +38,7 @@
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DeleteResult;
+import com.android.server.art.model.OperationProgress;
import com.android.server.art.model.OptimizationStatus;
import com.android.server.art.model.OptimizeParams;
import com.android.server.art.model.OptimizeResult;
@@ -164,9 +165,8 @@
try (var signal = new WithCancellationSignal(pw)) {
result = mArtManagerLocal.optimizePackages(snapshot, getNextArgRequired(),
signal.get(), executor, progress -> {
- pw.println(String.format("Optimizing packages: %d/%d",
- progress.getDonePackageCount(),
- progress.getTotalPackageCount()));
+ pw.println(String.format(
+ "Optimizing apps: %d%%", progress.getPercentage()));
pw.flush();
});
}
diff --git a/libartservice/service/java/com/android/server/art/DexOptHelper.java b/libartservice/service/java/com/android/server/art/DexOptHelper.java
index 5bd70f7..1370cf9 100644
--- a/libartservice/service/java/com/android/server/art/DexOptHelper.java
+++ b/libartservice/service/java/com/android/server/art/DexOptHelper.java
@@ -34,8 +34,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
+import com.android.server.art.model.OperationProgress;
import com.android.server.art.model.OptimizeParams;
-import com.android.server.art.model.OptimizeProgress;
import com.android.server.art.model.OptimizeResult;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -107,7 +107,7 @@
@NonNull List<String> packageNames, @NonNull OptimizeParams params,
@NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor,
@Nullable Executor progressCallbackExecutor,
- @Nullable Consumer<OptimizeProgress> progressCallback) {
+ @Nullable Consumer<OperationProgress> progressCallback) {
return dexoptPackages(
getPackageStates(snapshot, packageNames,
(params.getFlags() & ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES) != 0),
@@ -123,7 +123,7 @@
private OptimizeResult dexoptPackages(@NonNull List<PackageState> pkgStates,
@NonNull OptimizeParams params, @NonNull CancellationSignal cancellationSignal,
@NonNull Executor dexoptExecutor, @Nullable Executor progressCallbackExecutor,
- @Nullable Consumer<OptimizeProgress> progressCallback) {
+ @Nullable Consumer<OperationProgress> progressCallback) {
int callingUid = Binder.getCallingUid();
long identityToken = Binder.clearCallingIdentity();
PowerManager.WakeLock wakeLock = null;
@@ -144,13 +144,13 @@
if (progressCallback != null) {
CompletableFuture.runAsync(() -> {
progressCallback.accept(
- OptimizeProgress.create(0 /* donePackageCount */, futures.size()));
+ OperationProgress.create(0 /* current */, futures.size()));
}, progressCallbackExecutor);
- AtomicInteger donePackageCount = new AtomicInteger(0);
+ AtomicInteger current = new AtomicInteger(0);
for (CompletableFuture<PackageOptimizeResult> future : futures) {
future.thenRunAsync(() -> {
- progressCallback.accept(OptimizeProgress.create(
- donePackageCount.incrementAndGet(), futures.size()));
+ progressCallback.accept(OperationProgress.create(
+ current.incrementAndGet(), futures.size()));
}, progressCallbackExecutor);
}
}
diff --git a/libartservice/service/java/com/android/server/art/ReasonMapping.java b/libartservice/service/java/com/android/server/art/ReasonMapping.java
index d408759..c236e21 100644
--- a/libartservice/service/java/com/android/server/art/ReasonMapping.java
+++ b/libartservice/service/java/com/android/server/art/ReasonMapping.java
@@ -42,7 +42,7 @@
public class ReasonMapping {
private ReasonMapping() {}
- /** Optimizing apps on the first boot. */
+ /** Optimizing apps on the first boot after flashing or factory resetting the device. */
public static final String REASON_FIRST_BOOT = "first-boot";
/** Optimizing apps on the next boot after an OTA. */
public static final String REASON_BOOT_AFTER_OTA = "boot-after-ota";
@@ -84,6 +84,20 @@
public @interface BatchOptimizeReason {}
/**
+ * Reasons for {@link ArtManagerLocal#onBoot(String, Executor, Consumer<OperationProgress>)}.
+ *
+ * @hide
+ */
+ // clang-format off
+ @StringDef(prefix = "REASON_", value = {
+ REASON_FIRST_BOOT,
+ REASON_BOOT_AFTER_OTA,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BootReason {}
+
+ /**
* Loads the compiler filter from the system property for the given reason and checks for
* validity.
*
diff --git a/libartservice/service/java/com/android/server/art/model/OperationProgress.java b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
new file mode 100644
index 0000000..2e46f59
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class OperationProgress {
+ /** @hide */
+ protected OperationProgress() {}
+
+ /** @hide */
+ public static @NonNull OperationProgress create(int current, int total) {
+ return new AutoValue_OperationProgress(current, total);
+ }
+
+ /** The overall progress, in the range of [0, 100]. */
+ public int getPercentage() {
+ return 100 * getCurrent() / getTotal();
+ }
+
+ /**
+ * The number of processed items. Can be 0, which means the operation was just started.
+ *
+ * Currently, this is the number of packages, for which optimization has been done, regardless
+ * of the results (performed, failed, skipped, etc.).
+ *
+ * @hide
+ */
+ public abstract int getCurrent();
+
+ /**
+ * The total number of items. Stays constant during the operation.
+ *
+ * Currently, this is the total number of packages to optimize.
+ *
+ * @hide
+ */
+ public abstract int getTotal();
+}
diff --git a/libartservice/service/java/com/android/server/art/model/OptimizeProgress.java b/libartservice/service/java/com/android/server/art/model/OptimizeProgress.java
deleted file mode 100644
index 39310a5..0000000
--- a/libartservice/service/java/com/android/server/art/model/OptimizeProgress.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.art.model;
-
-import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.Immutable;
-
-import com.google.auto.value.AutoValue;
-
-/** @hide */
-@Immutable
-@AutoValue
-public abstract class OptimizeProgress {
- /** @hide */
- protected OptimizeProgress() {}
-
- /** @hide */
- public static @NonNull OptimizeProgress create(int donePackageCount, int totalPackageCount) {
- return new AutoValue_OptimizeProgress(donePackageCount, totalPackageCount);
- }
-
- /**
- * The number of packages, for which optimization has been done, regardless of the results
- * (performed, failed, skipped, etc.). Can be 0, which means the optimization was just started.
- */
- public abstract int getDonePackageCount();
-
- /**
- * The total number of packages to optimize. Stays constant during the operation.
- */
- public abstract int getTotalPackageCount();
-}
diff --git a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
index c090bd5..59af8be 100644
--- a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
+++ b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
@@ -45,8 +45,8 @@
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
+import com.android.server.art.model.OperationProgress;
import com.android.server.art.model.OptimizeParams;
-import com.android.server.art.model.OptimizeProgress;
import com.android.server.art.model.OptimizeResult;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -512,7 +512,7 @@
// Delay the executor to verify that the commands passed to the executor are not bound to
// changing variables.
var progressCallbackExecutor = new DelayedExecutor();
- Consumer<OptimizeProgress> progressCallback = mock(Consumer.class);
+ Consumer<OperationProgress> progressCallback = mock(Consumer.class);
mDexOptHelper.dexopt(mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor,
progressCallbackExecutor, progressCallback);
@@ -521,17 +521,13 @@
InOrder inOrder = inOrder(progressCallback);
inOrder.verify(progressCallback)
- .accept(eq(OptimizeProgress.create(
- 0 /* donePackageCount */, 3 /* totalPackageCount */)));
+ .accept(eq(OperationProgress.create(0 /* current */, 3 /* total */)));
inOrder.verify(progressCallback)
- .accept(eq(OptimizeProgress.create(
- 1 /* donePackageCount */, 3 /* totalPackageCount */)));
+ .accept(eq(OperationProgress.create(1 /* current */, 3 /* total */)));
inOrder.verify(progressCallback)
- .accept(eq(OptimizeProgress.create(
- 2 /* donePackageCount */, 3 /* totalPackageCount */)));
+ .accept(eq(OperationProgress.create(2 /* current */, 3 /* total */)));
inOrder.verify(progressCallback)
- .accept(eq(OptimizeProgress.create(
- 3 /* donePackageCount */, 3 /* totalPackageCount */)));
+ .accept(eq(OperationProgress.create(3 /* current */, 3 /* total */)));
}
private AndroidPackage createPackage(boolean multiSplit) {