diff options
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; +} |