Change `dexoptPackages` to support multiple passes.
This CL changes `dexoptPackages` so that it can report progress and
results for multiple passes. It enables us to add one more pass for
background dexopt and collect stats of the new pass.
This is generally a no-op change, except for that it changes the outputs
of the `pm art dexopt-packages` command.
Bug: 242170869
Test: atest ArtServiceTests
Test: adb shell pm art dexopt-packages -r bg-dexopt
Test: adb shell pm art dexopt-packages -r first-boot
Change-Id: I6197ec92caca0e30a4efbc394faf24c778ba0b8a
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index f693944..8d550b0 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -83,8 +83,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -464,7 +466,9 @@
* @param reason determines the default list of packages and options
* @param cancellationSignal provides the ability to cancel this operation
* @param processCallbackExecutor the executor to call {@code progressCallback}
- * @param progressCallback called repeatedly whenever there is an update on the progress
+ * @param progressCallbacks a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to
+ * the callback that is called repeatedly whenever there is an update on the progress
+ * @return a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to the dexopt result.
* @throws IllegalStateException if the operation encounters an error that should never happen
* (e.g., an internal logic error), or the callback set by {@link
* #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} provides invalid
@@ -474,11 +478,12 @@
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@NonNull
- public DexoptResult dexoptPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ public Map<Integer, DexoptResult> dexoptPackages(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull @BatchDexoptReason String reason,
@NonNull CancellationSignal cancellationSignal,
@Nullable @CallbackExecutor Executor progressCallbackExecutor,
- @Nullable Consumer<OperationProgress> progressCallback) {
+ @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks) {
List<String> defaultPackages =
Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build();
@@ -496,17 +501,26 @@
ExecutorService dexoptExecutor =
Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason));
+ Map<Integer, DexoptResult> dexoptResults = new HashMap<>();
try {
if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
- maybeDowngradePackages(snapshot,
+ DexoptResult downgradeResult = maybeDowngradePackages(snapshot,
new HashSet<>(params.getPackages()) /* excludedPackages */,
- cancellationSignal, dexoptExecutor);
+ cancellationSignal, dexoptExecutor, progressCallbackExecutor,
+ progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_DOWNGRADE)
+ : null);
+ if (downgradeResult != null) {
+ dexoptResults.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ }
}
Log.i(TAG,
"Dexopting " + params.getPackages().size() + " packages with reason=" + reason);
- return mInjector.getDexoptHelper().dexopt(snapshot, params.getPackages(),
- params.getDexoptParams(), cancellationSignal, dexoptExecutor,
- progressCallbackExecutor, progressCallback);
+ DexoptResult mainResult = mInjector.getDexoptHelper().dexopt(snapshot,
+ params.getPackages(), params.getDexoptParams(), cancellationSignal,
+ dexoptExecutor, progressCallbackExecutor,
+ progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_MAIN) : null);
+ dexoptResults.put(ArtFlags.PASS_MAIN, mainResult);
+ return dexoptResults;
} finally {
dexoptExecutor.shutdown();
}
@@ -865,7 +879,7 @@
@Nullable Consumer<OperationProgress> progressCallback) {
try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor,
- progressCallback);
+ Map.of(ArtFlags.PASS_MAIN, progressCallback));
}
}
@@ -1032,9 +1046,13 @@
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private void maybeDowngradePackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @Nullable
+ private DexoptResult maybeDowngradePackages(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
@NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal,
- @NonNull Executor executor) {
+ @NonNull Executor executor,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
if (shouldDowngrade()) {
List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE)
.stream()
@@ -1044,14 +1062,15 @@
Log.i(TAG, "Storage is low. Downgrading " + packages.size() + " inactive packages");
DexoptParams params =
new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build();
- mInjector.getDexoptHelper().dexopt(snapshot, packages, params, cancellationSignal,
- executor, null /* processCallbackExecutor */, null /* progressCallback */);
+ return mInjector.getDexoptHelper().dexopt(snapshot, packages, params,
+ cancellationSignal, executor, progressCallbackExecutor, progressCallback);
} else {
Log.i(TAG,
"Storage is low, but downgrading is disabled or there's nothing to "
+ "downgrade");
}
}
+ return null;
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index b83f4ab..fefcf8c 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -20,6 +20,8 @@
import static com.android.server.art.ArtManagerLocal.SnapshotProfileException;
import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+import static com.android.server.art.ReasonMapping.BatchDexoptReason;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
import static com.android.server.art.model.ArtFlags.DexoptFlags;
import static com.android.server.art.model.ArtFlags.PriorityClassApi;
import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
@@ -66,11 +68,14 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -382,8 +387,7 @@
BackgroundDexoptJob.Result result = Utils.getFuture(future);
if (result instanceof BackgroundDexoptJob.CompletedResult) {
var completedResult = (BackgroundDexoptJob.CompletedResult) result;
- if (completedResult.dexoptResult().getFinalStatus()
- == DexoptResult.DEXOPT_CANCELLED) {
+ if (completedResult.isCancelled()) {
pw.println("Job cancelled. See logs for details");
} else {
pw.println("Job finished. See logs for details");
@@ -584,20 +588,41 @@
ReasonMapping.BATCH_DEXOPT_REASONS);
return 1;
}
- DexoptResult result;
+
+ final String finalReason = reason;
+
+ // Create callbacks to print the progress.
+ Map<Integer, Consumer<OperationProgress>> progressCallbacks = new HashMap<>();
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ progressCallbacks.put(pass, progress -> {
+ pw.println(String.format(Locale.US, "%s: %d%%",
+ getProgressMessageForBatchDexoptPass(pass, finalReason),
+ progress.getPercentage()));
+ pw.flush();
+ });
+ }
+
ExecutorService progressCallbackExecutor = Executors.newSingleThreadExecutor();
try (var signal = new WithCancellationSignal(pw, true /* verbose */)) {
- result = mArtManagerLocal.dexoptPackages(
- snapshot, reason, signal.get(), progressCallbackExecutor, progress -> {
- pw.println(String.format("Dexopting apps: %d%%", progress.getPercentage()));
- pw.flush();
- });
+ Map<Integer, DexoptResult> results = mArtManagerLocal.dexoptPackages(snapshot,
+ finalReason, signal.get(), progressCallbackExecutor, progressCallbacks);
+
Utils.executeAndWait(progressCallbackExecutor, () -> {
- printDexoptResult(pw, result, true /* verbose */, true /* multiPackage */);
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ if (results.containsKey(pass)) {
+ pw.println("Result of "
+ + getProgressMessageForBatchDexoptPass(pass, finalReason)
+ .toLowerCase(Locale.US)
+ + ":");
+ printDexoptResult(
+ pw, results.get(pass), true /* verbose */, true /* multiPackage */);
+ }
+ }
});
} finally {
progressCallbackExecutor.shutdown();
}
+
return 0;
}
@@ -906,6 +931,19 @@
}
}
+ @NonNull
+ private String getProgressMessageForBatchDexoptPass(
+ @BatchDexoptPass int pass, @NonNull @BatchDexoptReason String reason) {
+ switch (pass) {
+ case ArtFlags.PASS_DOWNGRADE:
+ return "Downgrading apps";
+ case ArtFlags.PASS_MAIN:
+ return reason.equals(ReasonMapping.REASON_BG_DEXOPT) ? "Dexopting apps (main pass)"
+ : "Dexopting apps";
+ }
+ throw new IllegalArgumentException("Unknown batch dexopt pass " + pass);
+ }
+
private static class WithCancellationSignal implements AutoCloseable {
@NonNull private final CancellationSignal mSignal = new CancellationSignal();
@NonNull private final String mJobId;
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
index b7181ca..9aeb282 100644
--- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
@@ -17,6 +17,7 @@
package com.android.server.art;
import static com.android.server.art.ArtManagerLocal.ScheduleBackgroundDexoptJobCallback;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
import static com.android.server.art.model.ArtFlags.ScheduleStatus;
import static com.android.server.art.model.Config.Callback;
@@ -32,6 +33,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
+import android.util.Slog;
import androidx.annotation.RequiresApi;
@@ -41,14 +43,19 @@
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.OperationProgress;
import com.android.server.pm.PackageManagerLocal;
import com.google.auto.value.AutoValue;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/** @hide */
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -85,15 +92,20 @@
public boolean onStartJob(
@NonNull BackgroundDexoptJobService jobService, @NonNull JobParameters params) {
start().thenAcceptAsync(result -> {
- writeStats(result);
+ try {
+ writeStats(result);
+ } catch (RuntimeException e) {
+ // Not expected. Log wtf to surface it.
+ Slog.wtf(TAG, "Failed to write stats", e);
+ }
+
// This is a periodic job, where the interval is specified in the `JobInfo`. "true"
// means to execute again in the same interval with the default retry policy, while
// "false" means not to execute again in the same interval but to execute again in the
// next interval.
// This call will be ignored if `onStopJob` is called.
- boolean wantsReschedule = result instanceof CompletedResult
- && ((CompletedResult) result).dexoptResult().getFinalStatus()
- == DexoptResult.DEXOPT_CANCELLED;
+ boolean wantsReschedule =
+ result instanceof CompletedResult && ((CompletedResult) result).isCancelled();
jobService.jobFinished(params, wantsReschedule);
});
// "true" means the job will continue running until `jobFinished` is called.
@@ -201,12 +213,28 @@
@NonNull
private CompletedResult run(@NonNull CancellationSignal cancellationSignal) {
- long startTimeMs = SystemClock.uptimeMillis();
- DexoptResult dexoptResult;
+ // Create callbacks to time each pass.
+ Map<Integer, Long> startTimeMsByPass = new HashMap<>();
+ Map<Integer, Long> durationMsByPass = new HashMap<>();
+ Map<Integer, Consumer<OperationProgress>> progressCallbacks = new HashMap<>();
+ for (@BatchDexoptPass int pass : ArtFlags.BATCH_DEXOPT_PASSES) {
+ progressCallbacks.put(pass, progress -> {
+ if (progress.getTotal() == 0) {
+ durationMsByPass.put(pass, 0l);
+ } else if (progress.getCurrent() == 0) {
+ startTimeMsByPass.put(pass, SystemClock.uptimeMillis());
+ } else if (progress.getCurrent() == progress.getTotal()) {
+ durationMsByPass.put(
+ pass, SystemClock.uptimeMillis() - startTimeMsByPass.get(pass));
+ }
+ });
+ }
+
+ Map<Integer, DexoptResult> dexoptResultByPass;
try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
- dexoptResult = mInjector.getArtManagerLocal().dexoptPackages(snapshot,
- ReasonMapping.REASON_BG_DEXOPT, cancellationSignal,
- null /* processCallbackExecutor */, null /* processCallback */);
+ dexoptResultByPass = mInjector.getArtManagerLocal().dexoptPackages(snapshot,
+ ReasonMapping.REASON_BG_DEXOPT, cancellationSignal, Runnable::run,
+ progressCallbacks);
// For simplicity, we don't support cancelling the following operation in the middle.
// This is fine because it typically takes only a few seconds.
@@ -219,7 +247,7 @@
Log.i(TAG, String.format("Freed %d bytes", freedBytes));
}
}
- return CompletedResult.create(dexoptResult, SystemClock.uptimeMillis() - startTimeMs);
+ return CompletedResult.create(dexoptResultByPass, durationMsByPass);
}
private void writeStats(@NonNull Result result) {
@@ -238,13 +266,22 @@
static class FatalErrorResult extends Result {}
@AutoValue
+ @SuppressWarnings("AutoValueImmutableFields") // Can't use ImmutableMap because it's in Guava.
static abstract class CompletedResult extends Result {
- abstract @NonNull DexoptResult dexoptResult();
- abstract long durationMs();
+ abstract @NonNull Map<Integer, DexoptResult> dexoptResultByPass();
+ abstract @NonNull Map<Integer, Long> durationMsByPass();
@NonNull
- static CompletedResult create(@NonNull DexoptResult dexoptResult, long durationMs) {
- return new AutoValue_BackgroundDexoptJob_CompletedResult(dexoptResult, durationMs);
+ static CompletedResult create(@NonNull Map<Integer, DexoptResult> dexoptResultByPass,
+ @NonNull Map<Integer, Long> durationMsByPass) {
+ return new AutoValue_BackgroundDexoptJob_CompletedResult(
+ Collections.unmodifiableMap(dexoptResultByPass),
+ Collections.unmodifiableMap(durationMsByPass));
+ }
+
+ public boolean isCancelled() {
+ return dexoptResultByPass().values().stream().anyMatch(
+ result -> result.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED);
}
}
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
index c6cc73a..99b698c 100644
--- a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobStatsReporter.java
@@ -1,11 +1,14 @@
package com.android.server.art;
+import static com.android.server.art.model.ArtFlags.BatchDexoptPass;
+
import android.annotation.NonNull;
import android.app.job.JobParameters;
import android.os.Build;
import androidx.annotation.RequiresApi;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptResult;
import dalvik.system.DexFile;
@@ -31,22 +34,41 @@
public static void reportSuccess(@NonNull BackgroundDexoptJob.CompletedResult completedResult,
Optional<Integer> stopReason) {
+ for (var entry : completedResult.dexoptResultByPass().entrySet()) {
+ reportPass(entry.getKey(), entry.getValue(),
+ completedResult.durationMsByPass().getOrDefault(entry.getKey(), 0l),
+ stopReason);
+ }
+ }
+
+ public static void reportPass(@BatchDexoptPass int pass, @NonNull DexoptResult dexoptResult,
+ long durationMs, Optional<Integer> stopReason) {
+ // TODO(jiakaiz): Report all passes.
+ if (pass != ArtFlags.PASS_MAIN) {
+ return;
+ }
+
+ // The job contains multiple passes, so the stop reason may not be for the current pass. We
+ // shouldn't report the stop reason if the current pass finished before the job was
+ // cancelled.
+ int reportedStopReason = dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED
+ ? stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED)
+ : JobParameters.STOP_REASON_UNDEFINED;
+
List<DexoptResult.PackageDexoptResult> packageDexoptResults =
- getFilteredPackageResults(completedResult);
+ getFilteredPackageResults(dexoptResult);
+
ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
- getStatusForStats(completedResult, stopReason),
- stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED),
- completedResult.durationMs(), 0 /* deprecated */,
- getDexoptedPackagesCount(packageDexoptResults),
+ getStatusForStats(dexoptResult, stopReason), reportedStopReason, durationMs,
+ 0 /* deprecated */, getDexoptedPackagesCount(packageDexoptResults),
getPackagesDependingOnBootClasspathCount(packageDexoptResults),
packageDexoptResults.size());
}
@NonNull
private static List<DexoptResult.PackageDexoptResult> getFilteredPackageResults(
- @NonNull BackgroundDexoptJob.CompletedResult completedResult) {
- return completedResult.dexoptResult()
- .getPackageDexoptResults()
+ @NonNull DexoptResult dexoptResult) {
+ return dexoptResult.getPackageDexoptResults()
.stream()
.filter(packageResult
-> packageResult.getDexContainerFileDexoptResults().stream().anyMatch(
@@ -58,8 +80,8 @@
}
private static int getStatusForStats(
- @NonNull BackgroundDexoptJob.CompletedResult result, Optional<Integer> stopReason) {
- if (result.dexoptResult().getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) {
+ @NonNull DexoptResult dexoptResult, Optional<Integer> stopReason) {
+ if (dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) {
if (stopReason.isPresent()) {
return ArtStatsLog
.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_CANCELLATION;
@@ -69,8 +91,7 @@
}
boolean isSkippedDueToStorageLow =
- result.dexoptResult()
- .getPackageDexoptResults()
+ dexoptResult.getPackageDexoptResults()
.stream()
.flatMap(packageResult
-> packageResult.getDexContainerFileDexoptResults().stream())
diff --git a/libartservice/service/java/com/android/server/art/model/ArtFlags.java b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
index a310f2c..c1274b8 100644
--- a/libartservice/service/java/com/android/server/art/model/ArtFlags.java
+++ b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
@@ -29,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/** @hide */
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@@ -228,5 +229,36 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ScheduleStatus {}
+ /**
+ * The downgrade pass, run before the main pass, only applicable to bg-dexopt.
+ *
+ * @hide
+ */
+ public static final int PASS_DOWNGRADE = 0;
+
+ /**
+ * The main pass.
+ *
+ * @hide
+ */
+ public static final int PASS_MAIN = 1;
+
+ /**
+ * Indicates the pass of a batch dexopt run.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(prefix = "PASS_", value = {
+ PASS_DOWNGRADE,
+ PASS_MAIN,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BatchDexoptPass {}
+
+ /** @hide */
+ public static final List<Integer> BATCH_DEXOPT_PASSES = List.of(PASS_DOWNGRADE, PASS_MAIN);
+
private ArtFlags() {}
}
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 ff3399e..8dd75a1 100644
--- a/libartservice/service/java/com/android/server/art/model/DexoptResult.java
+++ b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
@@ -105,6 +105,13 @@
return new AutoValue_DexoptResult(requestedCompilerFilter, reason, packageDexoptResult);
}
+ /** @hide */
+ @VisibleForTesting
+ public static @NonNull DexoptResult create() {
+ return new AutoValue_DexoptResult(
+ "compiler-filter", "reason", List.of() /* packageDexoptResult */);
+ }
+
/**
* The requested compiler filter. Note that the compiler filter might be adjusted before the
* execution based on factors like dexopt flags, whether the profile is available, or whether
diff --git a/libartservice/service/java/com/android/server/art/model/OperationProgress.java b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
index a47a556..a0a7ddc 100644
--- a/libartservice/service/java/com/android/server/art/model/OperationProgress.java
+++ b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
@@ -38,7 +38,7 @@
/** The overall progress, in the range of [0, 100]. */
public int getPercentage() {
- return 100 * getCurrent() / getTotal();
+ return getTotal() == 0 ? 100 : 100 * getCurrent() / getTotal();
}
/**
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index b85af84..bd978ee 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -58,6 +58,7 @@
import androidx.test.filters.SmallTest;
import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.Config;
import com.android.server.art.model.DeleteResult;
import com.android.server.art.model.DexoptParams;
@@ -442,7 +443,7 @@
@Test
public void testDexoptPackage() throws Exception {
var params = new DexoptParams.Builder("install").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME_1)), same(params),
@@ -456,7 +457,7 @@
@Test
public void testResetDexoptStatus() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexoptHelper.dexopt(
@@ -502,7 +503,7 @@
@Test
public void testDexoptPackages() throws Exception {
- var dexoptResult = mock(DexoptResult.class);
+ var dexoptResult = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_2)).thenReturn(CURRENT_TIME_MS);
simulateStorageLow();
@@ -517,7 +518,7 @@
assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
null /* processCallbackExecutor */, null /* processCallback */))
- .isSameInstanceAs(dexoptResult);
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, dexoptResult));
// Nothing to downgrade.
verify(mDexoptHelper, never())
@@ -533,7 +534,7 @@
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(0l);
simulateStorageLow();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should be dexopted.
@@ -560,25 +561,28 @@
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
simulateStorageLow();
- var result = mock(DexoptResult.class);
+ var mainResult = DexoptResult.create();
+ var downgradeResult = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should not be dexopted.
- doReturn(result)
+ doReturn(mainResult)
.when(mDexoptHelper)
.dexopt(any(), deepEq(List.of(PKG_NAME_2)),
argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
any(), any());
// PKG_NAME_1 should be downgraded.
- doReturn(result)
+ doReturn(downgradeResult)
.when(mDexoptHelper)
.dexopt(any(), deepEq(List.of(PKG_NAME_1)),
argThat(params -> params.getReason().equals("inactive")), any(), any(),
any(), any());
- mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
- null /* processCallbackExecutor */, null /* processCallback */);
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */))
+ .isEqualTo(Map.of(
+ ArtFlags.PASS_DOWNGRADE, downgradeResult, ArtFlags.PASS_MAIN, mainResult));
}
@Test
@@ -588,7 +592,7 @@
when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_1)).thenReturn(NOT_RECENT_TIME_MS);
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 should not be dexopted.
@@ -609,7 +613,7 @@
@Test
public void testDexoptPackagesBootAfterMainlineUpdate() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
lenient().when(mInjector.isSystemUiPackage(PKG_NAME_1)).thenReturn(true);
@@ -626,7 +630,7 @@
@Test
public void testDexoptPackagesBootAfterMainlineUpdatePackagesNotFound() throws Exception {
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
// PKG_NAME_1 is neither recently installed nor recently used.
PackageUserState userState = mPkgState1.getStateForUser(UserHandle.of(1));
@@ -658,7 +662,7 @@
simulateStorageLow();
var params = new DexoptParams.Builder("bg-dexopt").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
@@ -688,7 +692,7 @@
@Test
public void testDexoptPackagesOverrideCleared() throws Exception {
var params = new DexoptParams.Builder("bg-dexopt").build();
- var result = mock(DexoptResult.class);
+ var result = DexoptResult.create();
var cancellationSignal = new CancellationSignal();
mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
@@ -704,7 +708,7 @@
assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
null /* processCallbackExecutor */, null /* processCallback */))
- .isSameInstanceAs(result);
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, result));
}
@Test(expected = IllegalStateException.class)
diff --git a/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
index 3528caf..c61461d 100644
--- a/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
+++ b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
@@ -17,6 +17,8 @@
package com.android.server.art;
import static com.android.server.art.model.Config.Callback;
+import static com.android.server.art.model.DexoptResult.DexoptResultStatus;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +27,7 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.times;
@@ -56,6 +59,9 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -74,12 +80,12 @@
@Mock private PackageManagerLocal mPackageManagerLocal;
@Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
@Mock private JobScheduler mJobScheduler;
- @Mock private DexoptResult mDexoptResult;
@Mock private BackgroundDexoptJobService mJobService;
@Mock private JobParameters mJobParameters;
private Config mConfig;
private BackgroundDexoptJob mBackgroundDexoptJob;
private Semaphore mJobFinishedCalled = new Semaphore(0);
+ private Map<Integer, DexoptResult> mDexoptResultByPass;
@Before
public void setUp() throws Exception {
@@ -110,17 +116,19 @@
lenient()
.when(mJobParameters.getStopReason())
.thenReturn(JobParameters.STOP_REASON_UNDEFINED);
+
+ mDexoptResultByPass = new HashMap<>();
}
@Test
public void testStart() {
when(mArtManagerLocal.dexoptPackages(
same(mSnapshot), eq(ReasonMapping.REASON_BG_DEXOPT), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
Result result = Utils.getFuture(mBackgroundDexoptJob.start());
assertThat(result).isInstanceOf(CompletedResult.class);
- assertThat(((CompletedResult) result).dexoptResult()).isSameInstanceAs(mDexoptResult);
+ assertThat(((CompletedResult) result).dexoptResultByPass()).isEqualTo(mDexoptResultByPass);
verify(mArtManagerLocal).cleanup(same(mSnapshot));
}
@@ -131,7 +139,7 @@
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
.thenAnswer(invocation -> {
assertThat(dexoptDone.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
- return mDexoptResult;
+ return mDexoptResultByPass;
});
Future<Result> future1 = mBackgroundDexoptJob.start();
@@ -147,7 +155,7 @@
@Test
public void testStartAnother() {
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
Future<Result> future1 = mBackgroundDexoptJob.start();
Utils.getFuture(future1);
@@ -172,7 +180,7 @@
.thenReturn(true);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
// The `start` method should ignore the system property. The system property is for
// `schedule`.
@@ -187,7 +195,7 @@
assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
var cancellationSignal = invocation.<CancellationSignal>getArgument(2);
assertThat(cancellationSignal.isCanceled()).isTrue();
- return mDexoptResult;
+ return mDexoptResultByPass;
});
Future<Result> future = mBackgroundDexoptJob.start();
@@ -273,9 +281,12 @@
@Test
public void testWantsRescheduleFalsePerformed() throws Exception {
- when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_PERFORMED);
+ DexoptResult downgradeResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ DexoptResult mainResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_MAIN, mainResult);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
@@ -296,13 +307,22 @@
@Test
public void testWantsRescheduleTrue() throws Exception {
- when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_CANCELLED);
+ DexoptResult downgradeResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_PERFORMED);
+ mDexoptResultByPass.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
+ DexoptResult mainResult = createDexoptResultWithStatus(DexoptResult.DEXOPT_CANCELLED);
+ mDexoptResultByPass.put(ArtFlags.PASS_MAIN, mainResult);
when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
- .thenReturn(mDexoptResult);
+ .thenReturn(mDexoptResultByPass);
mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
verify(mJobService).jobFinished(any(), eq(true) /* wantsReschedule */);
}
+
+ private DexoptResult createDexoptResultWithStatus(@DexoptResultStatus int status) {
+ return DexoptResult.create("compiler-filter", "reason",
+ List.of(PackageDexoptResult.create(
+ "package-name", List.of() /* dexContainerFileDexoptResults */, status)));
+ }
}