summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/flags/art-flags.aconfig21
-rw-r--r--libartservice/service/api/system-server-current.txt1
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java28
-rw-r--r--libartservice/service/java/com/android/server/art/ArtShellCommand.java50
-rw-r--r--libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java10
-rw-r--r--libartservice/service/java/com/android/server/art/ReasonMapping.java8
-rw-r--r--libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java20
-rw-r--r--libartservice/service/java/com/android/server/art/model/DexoptParams.java23
-rw-r--r--libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java53
-rw-r--r--libartservice/service/java/com/android/server/art/prereboot/PreRebootManager.java35
-rw-r--r--libartservice/service/java/com/android/server/art/prereboot/PreRebootManagerInterface.java19
-rw-r--r--libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java27
-rw-r--r--libartservice/service/javatests/com/android/server/art/model/BatchDexoptParamsTest.java84
-rw-r--r--libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java58
-rw-r--r--libartservice/service/proto/batch_dexopt_params.proto45
15 files changed, 450 insertions, 32 deletions
diff --git a/build/flags/art-flags.aconfig b/build/flags/art-flags.aconfig
index 35ac49ed0b..05b6c55a52 100644
--- a/build/flags/art-flags.aconfig
+++ b/build/flags/art-flags.aconfig
@@ -36,10 +36,19 @@ flag {
}
flag {
- namespace: "art_performance"
- name: "executable_method_file_offsets"
- is_exported: true
- description: "This flag includes the API for getting the compiled native executable offset info for a java method"
- bug: "296108553"
- is_fixed_read_only: true
+ namespace: "art_performance"
+ name: "executable_method_file_offsets"
+ is_exported: true
+ description: "This flag includes the API for getting the compiled native executable offset info for a java method"
+ bug: "296108553"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "art_service_v3"
+ namespace: "art_mainline"
+ description: "Flag for all new ART Service APIs added in the 25Q2 version of the ART module."
+ bug: "373820009"
+ is_fixed_read_only: true
+ is_exported: true
}
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
index 05163ebc70..b563e7df5c 100644
--- a/libartservice/service/api/system-server-current.txt
+++ b/libartservice/service/api/system-server-current.txt
@@ -76,6 +76,7 @@ package com.android.server.art {
field public static final String REASON_INSTALL_BULK_SECONDARY = "install-bulk-secondary";
field public static final String REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = "install-bulk-secondary-downgraded";
field public static final String REASON_INSTALL_FAST = "install-fast";
+ field @FlaggedApi("com.android.art.flags.art_service_v3") public static final String REASON_PRE_REBOOT_DEXOPT = "ab-ota";
}
}
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index a23435041f..881a09f5ac 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -471,6 +471,17 @@ public final class ArtManagerLocal {
@NonNull CancellationSignal cancellationSignal,
@Nullable @CallbackExecutor Executor progressCallbackExecutor,
@Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks) {
+ return dexoptPackagesWithParams(snapshot, reason, cancellationSignal,
+ progressCallbackExecutor, progressCallbacks, null /* params */);
+ }
+
+ /** @hide */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @NonNull
+ public BatchDexoptParams getBatchDexoptParams(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull @BatchDexoptReason String reason,
+ @NonNull CancellationSignal cancellationSignal) {
List<String> defaultPackages =
Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build();
@@ -492,7 +503,22 @@ public final class ArtManagerLocal {
params = builder.setDexoptParams(dexoptParams.toBuilder().setSplitName(null).build())
.build();
}
+ return params;
+ }
+ /** @hide */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @NonNull
+ public Map<Integer, DexoptResult> dexoptPackagesWithParams(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull @BatchDexoptReason String reason,
+ @NonNull CancellationSignal cancellationSignal,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks,
+ @Nullable BatchDexoptParams params) {
+ if (params == null) {
+ params = getBatchDexoptParams(snapshot, reason, cancellationSignal);
+ }
ExecutorService dexoptExecutor =
Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason));
Map<Integer, DexoptResult> dexoptResults = new HashMap<>();
@@ -1653,7 +1679,7 @@ public final class ArtManagerLocal {
@NonNull
public synchronized PreRebootDexoptJob getPreRebootDexoptJob() {
if (mPrDexoptJob == null) {
- mPrDexoptJob = new PreRebootDexoptJob(mContext);
+ mPrDexoptJob = new PreRebootDexoptJob(mContext, mArtManagerLocal);
}
return mPrDexoptJob;
}
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
index f7d26cb36f..ca8f48a0ad 100644
--- a/libartservice/service/java/com/android/server/art/ArtShellCommand.java
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -193,6 +193,9 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
case "pr-dexopt-job": {
return handlePrDexoptJob(pw);
}
+ case "configure-batch-dexopt": {
+ return handleConfigureBatchDexopt(pw);
+ }
default:
pw.printf("Error: Unknown 'art' sub-command '%s'\n", subcmd);
pw.println("See 'pm help' for help");
@@ -841,6 +844,39 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
return 0;
}
+ private int handleConfigureBatchDexopt(@NonNull PrintWriter pw) {
+ String inputReason = null;
+ List<String> packages = new ArrayList<>();
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-r":
+ inputReason = getNextArgRequired();
+ break;
+ case "--package":
+ packages.add(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ // Variables used in lambda needs to be effectively final.
+ String finalInputReason = inputReason;
+ mArtManagerLocal.setBatchDexoptStartCallback(
+ Runnable::run, (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
+ if (reason.equals(finalInputReason)) {
+ if (!packages.isEmpty()) {
+ builder.setPackages(packages);
+ }
+ }
+ });
+
+ return 0;
+ }
+
@Override
public void onHelp() {
// No one should call this. The help text should be printed by the `onHelp` handler of `cmd
@@ -1029,6 +1065,20 @@ public final class ArtShellCommand extends BasicShellCommandHandler {
pw.println(" Options:");
pw.println(" --slot SLOT The slot that contains the OTA update, '_a' or '_b'. If not");
pw.println(" specified, the job is for a Mainline update");
+ pw.println();
+ pw.println(" configure-batch-dexopt -r REASON [--package PACKAGE_NAME]...");
+ pw.println(" Configure batch dexopt parameters to be applied when the given reason is");
+ pw.println(" used.");
+ pw.println(" Once called, this command overwrites any configuration done through");
+ pw.println(" 'ArtManagerLocal.setBatchDexoptStartCallback' or through this command for");
+ pw.println(" all reasons. In other words, configurations for other reasons are reset");
+ pw.println(" to the default.");
+ pw.println(" Valid values for REASON: 'first-boot', 'boot-after-ota',");
+ pw.println(" 'boot-after-mainline-update', 'bg-dexopt', 'ab-ota'");
+ pw.println(" Options:");
+ pw.println(" --package PACKAGE_NAME The package name to dexopt. This flag can be");
+ pw.println(" passed multiple times, to specify multiple packages. If not");
+ pw.println(" specified, the default package list will be used.");
}
private void enforceRootOrShell() {
diff --git a/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java b/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java
index 65062612a3..b7f47543fc 100644
--- a/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java
+++ b/libartservice/service/java/com/android/server/art/PreRebootDexoptJob.java
@@ -113,8 +113,8 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface {
// stats, should only be done when there is no job running and the `this` lock is held, or by
// the job itself.
- public PreRebootDexoptJob(@NonNull Context context) {
- this(new Injector(context));
+ public PreRebootDexoptJob(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal) {
+ this(new Injector(context, artManagerLocal));
}
@VisibleForTesting
@@ -514,9 +514,11 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface {
@VisibleForTesting
public static class Injector {
@NonNull private final Context mContext;
+ @NonNull private final ArtManagerLocal mArtManagerLocal;
- Injector(@NonNull Context context) {
+ Injector(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal) {
mContext = context;
+ mArtManagerLocal = artManagerLocal;
}
@NonNull
@@ -526,7 +528,7 @@ public class PreRebootDexoptJob implements ArtServiceJobInterface {
@NonNull
public PreRebootDriver getPreRebootDriver() {
- return new PreRebootDriver(mContext);
+ return new PreRebootDriver(mContext, mArtManagerLocal);
}
@NonNull
diff --git a/libartservice/service/java/com/android/server/art/ReasonMapping.java b/libartservice/service/java/com/android/server/art/ReasonMapping.java
index 9571ca20d6..e6296e02de 100644
--- a/libartservice/service/java/com/android/server/art/ReasonMapping.java
+++ b/libartservice/service/java/com/android/server/art/ReasonMapping.java
@@ -18,6 +18,7 @@ package com.android.server.art;
import static com.android.server.art.model.ArtFlags.PriorityClassApi;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.annotation.SystemApi;
@@ -27,6 +28,7 @@ import android.text.TextUtils;
import androidx.annotation.RequiresApi;
+import com.android.art.flags.Flags;
import com.android.server.art.model.ArtFlags;
import com.android.server.pm.PackageManagerLocal;
@@ -62,7 +64,11 @@ public class ReasonMapping {
public static final String REASON_CMDLINE = "cmdline";
/** Downgrading the compiler filter when an app is not used for a long time. */
public static final String REASON_INACTIVE = "inactive";
- /** @hide */
+ /**
+ * Dexopting apps before the reboot for an OTA or a mainline update, known as Pre-reboot
+ * Dexopt.
+ */
+ @FlaggedApi(Flags.FLAG_ART_SERVICE_V3)
public static final String REASON_PRE_REBOOT_DEXOPT = "ab-ota";
// Reasons for Play Install Hints (go/install-hints).
diff --git a/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java b/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java
index ffe5500b81..18ce4f8d54 100644
--- a/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java
+++ b/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java
@@ -18,8 +18,12 @@ package com.android.server.art.model;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
import com.android.internal.annotations.Immutable;
+import com.android.server.art.proto.BatchDexoptParamsProto;
import com.google.auto.value.AutoValue;
@@ -29,6 +33,7 @@ import java.util.List;
/** @hide */
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Immutable
@AutoValue
public abstract class BatchDexoptParams {
@@ -84,4 +89,19 @@ public abstract class BatchDexoptParams {
/** The params for dexopting each package. */
public abstract @NonNull DexoptParams getDexoptParams();
+
+ /** @hide */
+ public static @NonNull BatchDexoptParams fromProto(@NonNull BatchDexoptParamsProto proto) {
+ return new BatchDexoptParams
+ .Builder(proto.getPackageList(), DexoptParams.fromProto(proto.getDexoptParams()))
+ .build();
+ }
+
+ /** @hide */
+ public @NonNull BatchDexoptParamsProto toProto() {
+ return BatchDexoptParamsProto.newBuilder()
+ .addAllPackage(getPackages())
+ .setDexoptParams(getDexoptParams().toProto())
+ .build();
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptParams.java b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
index 455d4cdead..0c264d8266 100644
--- a/libartservice/service/java/com/android/server/art/model/DexoptParams.java
+++ b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
@@ -30,6 +30,7 @@ import com.android.internal.annotations.Immutable;
import com.android.server.art.ArtConstants;
import com.android.server.art.ReasonMapping;
import com.android.server.art.Utils;
+import com.android.server.art.proto.DexoptParamsProto;
/** @hide */
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@@ -231,4 +232,26 @@ public class DexoptParams {
.setPriorityClass(mPriorityClass)
.setSplitName(mSplitName);
}
+
+ /** @hide */
+ public static @NonNull DexoptParams fromProto(@NonNull DexoptParamsProto proto) {
+ return new DexoptParams.Builder(proto.getReason(), proto.getFlags())
+ .setCompilerFilter(proto.getCompilerFilter())
+ .setPriorityClass(proto.getPriorityClass())
+ .build();
+ }
+
+ /** @hide */
+ public @NonNull DexoptParamsProto toProto() {
+ // The split name is intentionally omitted from the proto because it's for batch dexopt
+ // only. Extend the proto when it's used for other purposes.
+ Utils.check(getSplitName() == null);
+
+ return DexoptParamsProto.newBuilder()
+ .setFlags(getFlags())
+ .setCompilerFilter(getCompilerFilter())
+ .setPriorityClass(getPriorityClass())
+ .setReason(getReason())
+ .build();
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java b/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java
index abcaa488f1..7c1a1c55fa 100644
--- a/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java
+++ b/libartservice/service/java/com/android/server/art/prereboot/PreRebootDriver.java
@@ -34,6 +34,7 @@ import android.system.Os;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
import com.android.server.art.ArtJni;
import com.android.server.art.ArtManagerLocal;
import com.android.server.art.ArtModuleServiceInitializer;
@@ -43,7 +44,10 @@ import com.android.server.art.GlobalInjector;
import com.android.server.art.IArtd;
import com.android.server.art.IDexoptChrootSetup;
import com.android.server.art.PreRebootDexoptJob;
+import com.android.server.art.ReasonMapping;
import com.android.server.art.Utils;
+import com.android.server.art.model.BatchDexoptParams;
+import com.android.server.pm.PackageManagerLocal;
import dalvik.system.DelegateLastClassLoader;
@@ -57,6 +61,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.Objects;
/**
* Drives Pre-reboot Dexopt, through reflection.
@@ -71,8 +76,8 @@ import java.nio.file.Paths;
public class PreRebootDriver {
@NonNull private final Injector mInjector;
- public PreRebootDriver(@NonNull Context context) {
- this(new Injector(context));
+ public PreRebootDriver(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal) {
+ this(new Injector(context, artManagerLocal));
}
@VisibleForTesting
@@ -95,8 +100,14 @@ public class PreRebootDriver {
boolean systemRequirementCheckFailed = false;
try {
statsReporter.recordJobStarted();
- setUp(otaSlot, mapSnapshotsForOta);
- runFromChroot(cancellationSignal);
+ try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
+ BatchDexoptParams params = mInjector.getArtManagerLocal().getBatchDexoptParams(
+ snapshot, ReasonMapping.REASON_PRE_REBOOT_DEXOPT, cancellationSignal);
+ if (!cancellationSignal.isCanceled()) {
+ setUp(otaSlot, mapSnapshotsForOta);
+ runFromChroot(cancellationSignal, snapshot, params);
+ }
+ }
success = true;
return true;
} catch (RemoteException e) {
@@ -201,8 +212,19 @@ public class PreRebootDriver {
mInjector.getDexoptChrootSetup().tearDown(false /* allowConcurrent */);
}
- private void runFromChroot(@NonNull CancellationSignal cancellationSignal)
+ private void runFromChroot(@NonNull CancellationSignal cancellationSignal,
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull BatchDexoptParams params)
throws ReflectiveOperationException, IOException, ErrnoException {
+ // Load the new `service-art.jar` on top of the current classloader, which has the old
+ // system server, framework, and Libcore.
+ // Note that the current classloader also includes the old `service-art.jar`, so this load
+ // inevitably introduces duplicate classes. We use `DelegateLastClassLoader` so that the
+ // classes in the new `service-art.jar` shadow the old ones, to make sure only new classes
+ // are used. Be careful not to pass an instance of a class between the old `service-art.jar`
+ // and the new `service-art.jar` (across the API boundary in `PreRebootManagerInterface`,
+ // either as a parameter or a return value).
+ // For this reason, a serialized protobuf is used for passing `BatchDexoptParams`.
String chrootArtDir = CHROOT_DIR + "/apex/com.android.art";
String dexPath = chrootArtDir + "/javalib/service-art.jar";
@@ -227,9 +249,11 @@ public class PreRebootDriver {
Object preRebootManager = preRebootManagerClass.getConstructor().newInstance();
preRebootManagerClass
.getMethod("run", ArtModuleServiceManager.class, Context.class,
- CancellationSignal.class)
+ CancellationSignal.class, PackageManagerLocal.FilteredSnapshot.class,
+ byte[].class)
.invoke(preRebootManager, ArtModuleServiceInitializer.getArtModuleServiceManager(),
- mInjector.getContext(), cancellationSignal);
+ mInjector.getContext(), cancellationSignal, snapshot,
+ params.toProto().toByteArray());
}
/**
@@ -240,9 +264,11 @@ public class PreRebootDriver {
@VisibleForTesting
public static class Injector {
@NonNull private final Context mContext;
+ @NonNull private final ArtManagerLocal mArtManagerLocal;
- Injector(@NonNull Context context) {
+ Injector(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal) {
mContext = context;
+ mArtManagerLocal = artManagerLocal;
}
@NonNull
@@ -259,5 +285,16 @@ public class PreRebootDriver {
public IArtd getArtd() {
return ArtdRefCache.getInstance().getArtd();
}
+
+ @NonNull
+ public ArtManagerLocal getArtManagerLocal() {
+ return mArtManagerLocal;
+ }
+
+ @NonNull
+ public PackageManagerLocal getPackageManagerLocal() {
+ return Objects.requireNonNull(
+ LocalManagerRegistry.getManager(PackageManagerLocal.class));
+ }
}
}
diff --git a/libartservice/service/java/com/android/server/art/prereboot/PreRebootManager.java b/libartservice/service/java/com/android/server/art/prereboot/PreRebootManager.java
index d4c4e570e2..5ffb2e0a0a 100644
--- a/libartservice/service/java/com/android/server/art/prereboot/PreRebootManager.java
+++ b/libartservice/service/java/com/android/server/art/prereboot/PreRebootManager.java
@@ -20,6 +20,7 @@ import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.art.proto.PreRebootStats.Status;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.ArtModuleServiceManager;
import android.os.Build;
@@ -34,10 +35,14 @@ import com.android.server.art.AsLog;
import com.android.server.art.ReasonMapping;
import com.android.server.art.Utils;
import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.BatchDexoptParams;
import com.android.server.art.model.DexoptResult;
import com.android.server.art.model.OperationProgress;
+import com.android.server.art.proto.BatchDexoptParamsProto;
import com.android.server.pm.PackageManagerLocal;
+import com.google.protobuf.InvalidProtocolBufferException;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -61,6 +66,18 @@ import java.util.function.Consumer;
public class PreRebootManager implements PreRebootManagerInterface {
public void run(@NonNull ArtModuleServiceManager artModuleServiceManager,
@NonNull Context context, @NonNull CancellationSignal cancellationSignal) {
+ PackageManagerLocal packageManagerLocal =
+ Objects.requireNonNull(LocalManagerRegistry.getManager(PackageManagerLocal.class));
+ try (var snapshot = packageManagerLocal.withFilteredSnapshot()) {
+ run(artModuleServiceManager, context, cancellationSignal, snapshot,
+ null /* batchDexoptParamsProto */);
+ }
+ }
+
+ public void run(@NonNull ArtModuleServiceManager artModuleServiceManager,
+ @NonNull Context context, @NonNull CancellationSignal cancellationSignal,
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @Nullable byte[] batchDexoptParamsProto) {
ExecutorService callbackExecutor = Executors.newSingleThreadExecutor();
try {
if (!PreRebootGlobalInjector.init(
@@ -68,8 +85,6 @@ public class PreRebootManager implements PreRebootManagerInterface {
return;
}
ArtManagerLocal artManagerLocal = new ArtManagerLocal(context);
- PackageManagerLocal packageManagerLocal = Objects.requireNonNull(
- LocalManagerRegistry.getManager(PackageManagerLocal.class));
var progressSession = new PreRebootStatsReporter().new ProgressSession();
@@ -109,11 +124,19 @@ public class PreRebootManager implements PreRebootManagerInterface {
progress.getTotal(), values.get(3));
};
- try (var snapshot = packageManagerLocal.withFilteredSnapshot()) {
- artManagerLocal.dexoptPackages(snapshot, ReasonMapping.REASON_PRE_REBOOT_DEXOPT,
- cancellationSignal, callbackExecutor,
- Map.of(ArtFlags.PASS_MAIN, progressCallback));
+ BatchDexoptParams params;
+ try {
+ params = batchDexoptParamsProto != null
+ ? BatchDexoptParams.fromProto(
+ BatchDexoptParamsProto.parseFrom(batchDexoptParamsProto))
+ : null;
+ } catch (InvalidProtocolBufferException e) {
+ throw new IllegalArgumentException(e);
}
+
+ artManagerLocal.dexoptPackagesWithParams(snapshot,
+ ReasonMapping.REASON_PRE_REBOOT_DEXOPT, cancellationSignal, callbackExecutor,
+ Map.of(ArtFlags.PASS_MAIN, progressCallback), params);
} finally {
ArtdRefCache.getInstance().reset();
callbackExecutor.shutdown();
diff --git a/libartservice/service/java/com/android/server/art/prereboot/PreRebootManagerInterface.java b/libartservice/service/java/com/android/server/art/prereboot/PreRebootManagerInterface.java
index 85f8b3f931..340e9a7d42 100644
--- a/libartservice/service/java/com/android/server/art/prereboot/PreRebootManagerInterface.java
+++ b/libartservice/service/java/com/android/server/art/prereboot/PreRebootManagerInterface.java
@@ -17,6 +17,7 @@
package com.android.server.art.prereboot;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.ArtModuleServiceManager;
import android.os.Build;
@@ -24,6 +25,9 @@ import android.os.CancellationSignal;
import androidx.annotation.RequiresApi;
+import com.android.server.art.proto.BatchDexoptParamsProto;
+import com.android.server.pm.PackageManagerLocal;
+
/**
* The interface for the entry point of Pre-reboot Dexopt, called through reflection from an old
* version of the ART module. This interface must be kept stable from one version of the ART module
@@ -37,13 +41,28 @@ import androidx.annotation.RequiresApi;
*
* During Pre-reboot Dexopt, the new version of this code is run.
*
+ * Methods in this interface must not take an instance of a class defined in `service-art.jar`. See
+ * comments in {@link PreRebootDriver#runFromChroot} for the reason.
+ *
* @hide
*/
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public interface PreRebootManagerInterface {
+ /** Since Android V. For backward compatibility only. */
void run(@NonNull ArtModuleServiceManager artModuleServiceManager, @NonNull Context context,
@NonNull CancellationSignal cancellationSignal) throws SystemRequirementException;
+ /**
+ * Since Android B. The current API.
+ *
+ * @param snapshot The snapshot that {@code batchDexoptParamsProto} is created with.
+ * @param batchDexoptParamsProto {@link BatchDexoptParamsProto}, in binary format.
+ */
+ void run(@NonNull ArtModuleServiceManager artModuleServiceManager, @NonNull Context context,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @Nullable byte[] batchDexoptParamsProto) throws SystemRequirementException;
+
public static class SystemRequirementException extends Exception {
public SystemRequirementException(@NonNull String message) {
super(message);
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
index 9deb3f1055..8841de7fed 100644
--- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -64,6 +64,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.ArtManagedFileStats;
+import com.android.server.art.model.BatchDexoptParams;
import com.android.server.art.model.Config;
import com.android.server.art.model.DeleteResult;
import com.android.server.art.model.DexoptParams;
@@ -875,6 +876,32 @@ public class ArtManagerLocalTest {
}
@Test
+ public void testDexoptPackagesWithParams() throws Exception {
+ var dexoptResult = DexoptResult.create();
+ var cancellationSignal = new CancellationSignal();
+
+ // It should only dexopt PKG_NAME_1 with "speed" as specified by the params.
+ doReturn(dexoptResult)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME_1)),
+ argThat(params
+ -> params.getReason().equals("ab-ota")
+ && params.getCompilerFilter().equals("speed")),
+ same(cancellationSignal), any(), any(), any());
+
+ BatchDexoptParams params = new BatchDexoptParams
+ .Builder(List.of(PKG_NAME_1),
+ new DexoptParams.Builder("ab-ota")
+ .setCompilerFilter("speed")
+ .build())
+ .build();
+ assertThat(
+ mArtManagerLocal.dexoptPackagesWithParams(mSnapshot, "ab-ota", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */, params))
+ .isEqualTo(Map.of(ArtFlags.PASS_MAIN, dexoptResult));
+ }
+
+ @Test
public void testSnapshotAppProfile() throws Exception {
var options = new MergeProfileOptions();
options.forceMerge = true;
diff --git a/libartservice/service/javatests/com/android/server/art/model/BatchDexoptParamsTest.java b/libartservice/service/javatests/com/android/server/art/model/BatchDexoptParamsTest.java
new file mode 100644
index 0000000000..32e4cd92d1
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/model/BatchDexoptParamsTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.proto.BatchDexoptParamsProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatchDexoptParamsTest {
+ @Test
+ public void testToProto() {
+ // Update this test with new fields if this assertion fails.
+ checkFieldCoverage();
+
+ BatchDexoptParams params = new BatchDexoptParams
+ .Builder(List.of("package_a", "package_b"),
+ new DexoptParams.Builder("install").build())
+ .build();
+
+ BatchDexoptParamsProto proto = params.toProto();
+
+ assertThat(proto.getPackageList())
+ .containsExactlyElementsIn(params.getPackages())
+ .inOrder();
+ assertThat(proto.getDexoptParams().getReason())
+ .isEqualTo(params.getDexoptParams().getReason());
+ }
+
+ @Test
+ public void testFromProto() {
+ // Update this test with new fields if this assertion fails.
+ checkFieldCoverage();
+
+ BatchDexoptParamsProto proto =
+ BatchDexoptParamsProto.newBuilder()
+ .addAllPackage(List.of("package_a", "package_b"))
+ .setDexoptParams(new DexoptParams.Builder("install").build().toProto())
+ .build();
+
+ BatchDexoptParams params = BatchDexoptParams.fromProto(proto);
+
+ assertThat(params.getPackages())
+ .containsExactlyElementsIn(proto.getPackageList())
+ .inOrder();
+ assertThat(params.getDexoptParams().getReason())
+ .isEqualTo(proto.getDexoptParams().getReason());
+ }
+
+ private void checkFieldCoverage() {
+ assertThat(Arrays.stream(BatchDexoptParams.class.getDeclaredMethods())
+ .filter(method -> Modifier.isAbstract(method.getModifiers()))
+ .map(Method::getName)
+ .collect(Collectors.toList()))
+ .containsExactly("getPackages", "getDexoptParams");
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
index d98340ee17..beadb314cf 100644
--- a/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
+++ b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
@@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.art.proto.DexoptParamsProto;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -117,12 +119,7 @@ public class DexoptParamsTest {
@Test
public void testToBuilder() {
// Update this test with new fields if this assertion fails.
- assertThat(Arrays.stream(DexoptParams.class.getDeclaredFields())
- .filter(field -> !Modifier.isStatic(field.getModifiers()))
- .map(Field::getName)
- .collect(Collectors.toList()))
- .containsExactly(
- "mFlags", "mCompilerFilter", "mPriorityClass", "mReason", "mSplitName");
+ checkFieldCoverage();
DexoptParams params1 =
new DexoptParams.Builder("install")
@@ -140,4 +137,53 @@ public class DexoptParamsTest {
assertThat(params1.getReason()).isEqualTo(params2.getReason());
assertThat(params1.getSplitName()).isEqualTo(params2.getSplitName());
}
+
+ @Test
+ public void testToProto() {
+ // Update this test with new fields if this assertion fails.
+ checkFieldCoverage();
+
+ DexoptParams params = new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX)
+ .setCompilerFilter("speed")
+ .setPriorityClass(90)
+ .build();
+
+ DexoptParamsProto proto = params.toProto();
+
+ assertThat(proto.getFlags()).isEqualTo(params.getFlags());
+ assertThat(proto.getCompilerFilter()).isEqualTo(params.getCompilerFilter());
+ assertThat(proto.getPriorityClass()).isEqualTo(params.getPriorityClass());
+ assertThat(proto.getReason()).isEqualTo(params.getReason());
+ }
+
+ @Test
+ public void testFromProto() {
+ // Update this test with new fields if this assertion fails.
+ checkFieldCoverage();
+
+ DexoptParamsProto proto = DexoptParamsProto.newBuilder()
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX)
+ .setCompilerFilter("speed")
+ .setPriorityClass(90)
+ .setReason("install")
+ .build();
+
+ DexoptParams params = DexoptParams.fromProto(proto);
+
+ assertThat(params.getFlags()).isEqualTo(proto.getFlags());
+ assertThat(params.getCompilerFilter()).isEqualTo(proto.getCompilerFilter());
+ assertThat(params.getPriorityClass()).isEqualTo(proto.getPriorityClass());
+ assertThat(params.getReason()).isEqualTo(proto.getReason());
+ assertThat(params.getSplitName()).isNull();
+ }
+
+ private void checkFieldCoverage() {
+ assertThat(Arrays.stream(DexoptParams.class.getDeclaredFields())
+ .filter(field -> !Modifier.isStatic(field.getModifiers()))
+ .map(Field::getName)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ "mFlags", "mCompilerFilter", "mPriorityClass", "mReason", "mSplitName");
+ }
}
diff --git a/libartservice/service/proto/batch_dexopt_params.proto b/libartservice/service/proto/batch_dexopt_params.proto
new file mode 100644
index 0000000000..ac3d20849a
--- /dev/null
+++ b/libartservice/service/proto/batch_dexopt_params.proto
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+syntax = "proto2";
+
+package com.android.server.art.proto;
+option java_multiple_files = true;
+
+// The protobuf representation of `BatchDexoptParams`. See classes in
+// java/com/android/server/art/model/BatchDexoptParams.java and
+// java/com/android/server/art/model/DexoptParams.java for details.
+// Fields added to classes after Android B must be optional in the protos.
+message BatchDexoptParamsProto {
+ // Required.
+ repeated string package = 1;
+ // Required.
+ optional DexoptParamsProto dexopt_params = 2;
+}
+
+// The protobuf representation of `DexoptParams`.
+// Note that this is only for batch dexopt. Particularly, it doesn't have a field for the split
+// name.
+message DexoptParamsProto {
+ // Required.
+ optional int32 flags = 1;
+ // Required.
+ optional string compiler_filter = 2;
+ // Required.
+ optional int32 priority_class = 3;
+ // Required.
+ optional string reason = 4;
+}