1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.art;
18 
19 import static com.android.server.art.ArtFileManager.ProfileLists;
20 import static com.android.server.art.ArtFileManager.UsableArtifactLists;
21 import static com.android.server.art.ArtFileManager.WritableArtifactLists;
22 import static com.android.server.art.DexMetadataHelper.DexMetadataInfo;
23 import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
24 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
25 import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
26 import static com.android.server.art.ProfilePath.WritableProfilePath;
27 import static com.android.server.art.ReasonMapping.BatchDexoptReason;
28 import static com.android.server.art.ReasonMapping.BootReason;
29 import static com.android.server.art.Utils.Abi;
30 import static com.android.server.art.Utils.InitProfileResult;
31 import static com.android.server.art.model.ArtFlags.GetStatusFlags;
32 import static com.android.server.art.model.ArtFlags.ScheduleStatus;
33 import static com.android.server.art.model.Config.Callback;
34 import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
35 
36 import android.annotation.CallbackExecutor;
37 import android.annotation.NonNull;
38 import android.annotation.Nullable;
39 import android.annotation.SuppressLint;
40 import android.annotation.SystemApi;
41 import android.annotation.SystemService;
42 import android.app.job.JobInfo;
43 import android.apphibernation.AppHibernationManager;
44 import android.content.BroadcastReceiver;
45 import android.content.Context;
46 import android.content.Intent;
47 import android.content.IntentFilter;
48 import android.os.Binder;
49 import android.os.Build;
50 import android.os.CancellationSignal;
51 import android.os.ParcelFileDescriptor;
52 import android.os.Process;
53 import android.os.RemoteException;
54 import android.os.ServiceSpecificException;
55 import android.os.SystemProperties;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.os.storage.StorageManager;
59 import android.text.TextUtils;
60 import android.util.Pair;
61 
62 import androidx.annotation.RequiresApi;
63 
64 import com.android.internal.annotations.VisibleForTesting;
65 import com.android.modules.utils.build.SdkLevel;
66 import com.android.server.LocalManagerRegistry;
67 import com.android.server.art.model.ArtFlags;
68 import com.android.server.art.model.ArtManagedFileStats;
69 import com.android.server.art.model.BatchDexoptParams;
70 import com.android.server.art.model.Config;
71 import com.android.server.art.model.DeleteResult;
72 import com.android.server.art.model.DetailedDexInfo;
73 import com.android.server.art.model.DexoptParams;
74 import com.android.server.art.model.DexoptResult;
75 import com.android.server.art.model.DexoptStatus;
76 import com.android.server.art.model.OperationProgress;
77 import com.android.server.art.prereboot.PreRebootStatsReporter;
78 import com.android.server.pm.PackageManagerLocal;
79 import com.android.server.pm.pkg.AndroidPackage;
80 import com.android.server.pm.pkg.AndroidPackageSplit;
81 import com.android.server.pm.pkg.PackageState;
82 
83 import dalvik.system.DexFile;
84 
85 import java.io.File;
86 import java.io.FileNotFoundException;
87 import java.io.IOException;
88 import java.io.PrintWriter;
89 import java.nio.file.Files;
90 import java.nio.file.Path;
91 import java.nio.file.Paths;
92 import java.util.ArrayList;
93 import java.util.Arrays;
94 import java.util.Collections;
95 import java.util.Comparator;
96 import java.util.HashMap;
97 import java.util.HashSet;
98 import java.util.List;
99 import java.util.Map;
100 import java.util.Objects;
101 import java.util.Set;
102 import java.util.concurrent.CompletableFuture;
103 import java.util.concurrent.Executor;
104 import java.util.concurrent.ExecutorService;
105 import java.util.concurrent.Executors;
106 import java.util.concurrent.TimeUnit;
107 import java.util.concurrent.locks.ReentrantReadWriteLock;
108 import java.util.function.Consumer;
109 import java.util.stream.Collectors;
110 import java.util.stream.Stream;
111 
112 /**
113  * This class provides a system API for functionality provided by the ART module.
114  *
115  * Note: Although this class is the entry point of ART services, this class is not a {@link
116  * SystemService}, and it does not publish a binder. Instead, it is a module loaded by the
117  * system_server process, registered in {@link LocalManagerRegistry}. {@link LocalManagerRegistry}
118  * specifies that in-process module interfaces should be named with the suffix {@code ManagerLocal}
119  * for consistency.
120  *
121  * @hide
122  */
123 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
124 public final class ArtManagerLocal {
125     private static final String[] CLASSPATHS_FOR_BOOT_IMAGE_PROFILE = {
126             "BOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"};
127 
128     /** @hide */
129     @VisibleForTesting public static final long DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES = 500_000_000;
130 
131     @NonNull private final Injector mInjector;
132 
133     private boolean mShouldCommitPreRebootStagedFiles = false;
134 
135     // A temporary object for holding stats while staged files are being committed, used in two
136     // places: `onBoot` and the `BroadcastReceiver` of `ACTION_BOOT_COMPLETED`.
137     @Nullable private PreRebootStatsReporter.AfterRebootSession mStatsAfterRebootSession = null;
138 
139     // A lock that prevents the cleanup from cleaning up dexopt temp files while dexopt is running.
140     // The method that does the cleanup should acquire a write lock; the methods that do dexopt
141     // should acquire a read lock.
142     @NonNull private ReentrantReadWriteLock mCleanupLock = new ReentrantReadWriteLock();
143 
144     @Deprecated
ArtManagerLocal()145     public ArtManagerLocal() {
146         mInjector = new Injector();
147     }
148 
149     /**
150      * Creates an instance.
151      *
152      * Only {@code SystemServer} should create an instance and register it in {@link
153      * LocalManagerRegistry}. Other API users should obtain the instance from {@link
154      * LocalManagerRegistry}.
155      *
156      * @param context the system server context
157      * @throws NullPointerException if required dependencies are missing
158      */
159     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
ArtManagerLocal(@onNull Context context)160     public ArtManagerLocal(@NonNull Context context) {
161         mInjector = new Injector(this, context);
162     }
163 
164     /** @hide */
165     @VisibleForTesting
ArtManagerLocal(@onNull Injector injector)166     public ArtManagerLocal(@NonNull Injector injector) {
167         mInjector = injector;
168     }
169 
170     /**
171      * Handles ART Service commands, which is a subset of `cmd package` commands.
172      *
173      * Note: This method is not an override of {@link Binder#handleShellCommand} because ART
174      * services does not publish a binder. Instead, it handles the commands forwarded by the
175      * `package` service. The semantics of the parameters are the same as {@link
176      * Binder#handleShellCommand}.
177      *
178      * @return zero on success, non-zero on internal error (e.g., I/O error)
179      * @throws SecurityException if the caller is not root
180      * @throws IllegalArgumentException if the arguments are illegal
181      * @see ArtShellCommand#printHelp(PrintWriter)
182      */
183     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
handleShellCommand(@onNull Binder target, @NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)184     public int handleShellCommand(@NonNull Binder target, @NonNull ParcelFileDescriptor in,
185             @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
186             @NonNull String[] args) {
187         return new ArtShellCommand(this, mInjector.getPackageManagerLocal(), mInjector.getContext())
188                 .exec(target, in.getFileDescriptor(), out.getFileDescriptor(),
189                         err.getFileDescriptor(), args);
190     }
191 
192     /** Prints ART Service shell command help. */
193     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
printShellCommandHelp(@onNull PrintWriter pw)194     public void printShellCommandHelp(@NonNull PrintWriter pw) {
195         ArtShellCommand.printHelp(pw);
196     }
197 
198     /**
199      * Deletes dexopt artifacts of a package, including the artifacts for primary dex files and the
200      * ones for secondary dex files. This includes VDEX, ODEX, and ART files.
201      *
202      * Also deletes runtime artifacts of the package, though they are not dexopt artifacts.
203      *
204      * @throws IllegalArgumentException if the package is not found or the flags are illegal
205      * @throws IllegalStateException if the operation encounters an error that should never happen
206      *         (e.g., an internal logic error).
207      */
208     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
209     @NonNull
deleteDexoptArtifacts( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)210     public DeleteResult deleteDexoptArtifacts(
211             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
212         PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
213         AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
214 
215         try (var pin = mInjector.createArtdPin()) {
216             long freedBytes = 0;
217             WritableArtifactLists list =
218                     mInjector.getArtFileManager().getWritableArtifacts(pkgState, pkg,
219                             ArtFileManager.Options.builder()
220                                     .setForPrimaryDex(true)
221                                     .setForSecondaryDex(true)
222                                     .build());
223             for (ArtifactsPath artifacts : list.artifacts()) {
224                 freedBytes += mInjector.getArtd().deleteArtifacts(artifacts);
225             }
226             for (RuntimeArtifactsPath runtimeArtifacts : list.runtimeArtifacts()) {
227                 freedBytes += mInjector.getArtd().deleteRuntimeArtifacts(runtimeArtifacts);
228             }
229             return DeleteResult.create(freedBytes);
230         } catch (RemoteException e) {
231             Utils.logArtdException(e);
232             return DeleteResult.create(0 /* freedBytes */);
233         }
234     }
235 
236     /**
237      * Returns the dexopt status of all known dex container files of a package, even if some of them
238      * aren't readable.
239      *
240      * Uses the default flags ({@link ArtFlags#defaultGetStatusFlags()}).
241      *
242      * @throws IllegalArgumentException if the package is not found or the flags are illegal
243      * @throws IllegalStateException if the operation encounters an error that should never happen
244      *         (e.g., an internal logic error).
245      */
246     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
247     @NonNull
getDexoptStatus( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)248     public DexoptStatus getDexoptStatus(
249             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
250         return getDexoptStatus(snapshot, packageName, ArtFlags.defaultGetStatusFlags());
251     }
252 
253     /**
254      * Same as above, but allows to specify flags.
255      *
256      * @see #getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String)
257      */
258     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
259     @NonNull
getDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @GetStatusFlags int flags)260     public DexoptStatus getDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
261             @NonNull String packageName, @GetStatusFlags int flags) {
262         if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0
263                 && (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) == 0) {
264             throw new IllegalArgumentException("Nothing to check");
265         }
266 
267         PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
268         AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
269         List<Pair<DetailedDexInfo, Abi>> dexAndAbis =
270                 mInjector.getArtFileManager().getDexAndAbis(pkgState, pkg,
271                         ArtFileManager.Options.builder()
272                                 .setForPrimaryDex((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0)
273                                 .setForSecondaryDex((flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0)
274                                 .build());
275 
276         try (var pin = mInjector.createArtdPin()) {
277             List<DexContainerFileDexoptStatus> statuses = new ArrayList<>();
278 
279             for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) {
280                 DetailedDexInfo dexInfo = pair.first;
281                 Abi abi = pair.second;
282                 try {
283                     GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus(
284                             dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext());
285                     statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
286                             dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(),
287                             abi.name(), result.compilerFilter, result.compilationReason,
288                             result.locationDebugString));
289                 } catch (ServiceSpecificException e) {
290                     statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
291                             dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(),
292                             abi.name(), "error", "error", e.getMessage()));
293                 }
294             }
295 
296             return DexoptStatus.create(statuses);
297         } catch (RemoteException e) {
298             Utils.logArtdException(e);
299             List<DexContainerFileDexoptStatus> statuses = new ArrayList<>();
300             for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) {
301                 DetailedDexInfo dexInfo = pair.first;
302                 Abi abi = pair.second;
303                 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
304                         dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), abi.name(),
305                         "error", "error", e.getMessage()));
306             }
307             return DexoptStatus.create(statuses);
308         }
309     }
310 
311     /**
312      * Clear the profiles that are collected locally for the given package, including the profiles
313      * for primary and secondary dex files. More specifically, it clears reference profiles and
314      * current profiles. External profiles (e.g., cloud profiles) will be kept.
315      *
316      * @throws IllegalArgumentException if the package is not found or the flags are illegal
317      * @throws IllegalStateException if the operation encounters an error that should never happen
318      *         (e.g., an internal logic error).
319      */
320     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
321     @NonNull
clearAppProfiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)322     public void clearAppProfiles(
323             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
324         PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
325         AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
326 
327         try (var pin = mInjector.createArtdPin()) {
328             // We want to delete as many profiles as possible, so this deletes profiles of all known
329             // secondary dex files. If there are unknown secondary dex files, their profiles will be
330             // deleted by `cleanup`.
331             ProfileLists list = mInjector.getArtFileManager().getProfiles(pkgState, pkg,
332                     ArtFileManager.Options.builder()
333                             .setForPrimaryDex(true)
334                             .setForSecondaryDex(true)
335                             .build());
336             for (ProfilePath profile : list.allProfiles()) {
337                 mInjector.getArtd().deleteProfile(profile);
338             }
339         } catch (RemoteException e) {
340             Utils.logArtdException(e);
341         }
342     }
343 
344     /**
345      * Dexopts a package. The time this operation takes ranges from a few milliseconds to several
346      * minutes, depending on the params and the code size of the package.
347      *
348      * When dexopt is successfully performed for a dex container file, this operation also deletes
349      * the corresponding runtime artifacts (the ART files in the package's data directory, which are
350      * generated by the runtime, not by dexopt).
351      *
352      * When this operation ends (either completed or cancelled), callbacks added by {@link
353      * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called.
354      *
355      * @throws IllegalArgumentException if the package is not found or the params are illegal
356      * @throws IllegalStateException if the operation encounters an error that should never happen
357      *         (e.g., an internal logic error).
358      */
359     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
360     @NonNull
dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params)361     public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
362             @NonNull String packageName, @NonNull DexoptParams params) {
363         var cancellationSignal = new CancellationSignal();
364         return dexoptPackage(snapshot, packageName, params, cancellationSignal);
365     }
366 
367     /**
368      * Same as above, but supports cancellation.
369      *
370      * @see #dexoptPackage(PackageManagerLocal.FilteredSnapshot, String, DexoptParams)
371      */
372     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
373     @NonNull
dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal)374     public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
375             @NonNull String packageName, @NonNull DexoptParams params,
376             @NonNull CancellationSignal cancellationSignal) {
377         mCleanupLock.readLock().lock();
378         try (var pin = mInjector.createArtdPin()) {
379             return mInjector.getDexoptHelper().dexopt(
380                     snapshot, List.of(packageName), params, cancellationSignal, Runnable::run);
381         } finally {
382             mCleanupLock.readLock().unlock();
383         }
384     }
385 
386     /**
387      * Resets the dexopt state of the package as if the package is newly installed.
388      *
389      * More specifically, it clears reference profiles, current profiles, any code compiled from
390      * those local profiles, and runtime artifacts. If there is an external profile (e.g., a cloud
391      * profile), the code compiled from that profile will be kept.
392      *
393      * For secondary dex files, it also clears all dexopt artifacts.
394      *
395      * @hide
396      */
397     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
398     @NonNull
resetDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull CancellationSignal cancellationSignal)399     public DexoptResult resetDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
400             @NonNull String packageName, @NonNull CancellationSignal cancellationSignal) {
401         // We must delete the artifacts for primary dex files beforehand rather than relying on
402         // `dexoptPackage` to replace them because:
403         // - If dexopt is not needed after the deletion, then we shouldn't run dexopt at all. For
404         //   example, when we have a DM file that contains a VDEX file but doesn't contain a cloud
405         //   profile, this happens. Note that this is more about correctness rather than
406         //   performance.
407         // - We don't want the existing artifacts to affect dexopt. For example, the existing VDEX
408         //   file should not be an input VDEX.
409         //
410         // We delete the artifacts for secondary dex files and `dexoptPackage` won't re-generate
411         // them because `dexoptPackage` for `REASON_INSTALL` is for primary dex only. This is
412         // intentional because secondary dex files are supposed to be unknown at install time.
413         deleteDexoptArtifacts(snapshot, packageName);
414         clearAppProfiles(snapshot, packageName);
415 
416         // Re-generate artifacts for primary dex files if needed.
417         return dexoptPackage(snapshot, packageName,
418                 new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(), cancellationSignal);
419     }
420 
421     /**
422      * Runs batch dexopt for the given reason.
423      *
424      * This is called by ART Service automatically during boot / background dexopt.
425      *
426      * The list of packages and options are determined by {@code reason}, and can be overridden by
427      * {@link #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}.
428      *
429      * The dexopt is done in a thread pool. The number of packages being dexopted
430      * simultaneously can be configured by system property {@code pm.dexopt.<reason>.concurrency}
431      * (e.g., {@code pm.dexopt.bg-dexopt.concurrency=4}), and the number of threads for each {@code
432      * dex2oat} invocation can be configured by system property {@code dalvik.vm.*dex2oat-threads}
433      * (e.g., {@code dalvik.vm.background-dex2oat-threads=4}). I.e., the maximum number of
434      * concurrent threads is the product of the two system properties. Note that the physical core
435      * usage is always bound by {@code dalvik.vm.*dex2oat-cpu-set} regardless of the number of
436      * threads.
437      *
438      * When this operation ends (either completed or cancelled), callbacks added by {@link
439      * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called.
440      *
441      * If the storage is nearly low, and {@code reason} is {@link ReasonMapping#REASON_BG_DEXOPT},
442      * it may also downgrade some inactive packages to a less optimized compiler filter, specified
443      * by the system property {@code pm.dexopt.inactive} (typically "verify"), to free up some
444      * space. This feature is only enabled when the system property {@code
445      * pm.dexopt.downgrade_after_inactive_days} is set. The space threshold to trigger this feature
446      * is the Storage Manager's low space threshold plus {@link
447      * #DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES}. The concurrency can be configured by system property
448      * {@code pm.dexopt.bg-dexopt.concurrency}. The packages in the list provided by
449      * {@link BatchDexoptStartCallback} for {@link ReasonMapping#REASON_BG_DEXOPT} are never
450      * downgraded.
451      *
452      * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
453      * @param reason determines the default list of packages and options
454      * @param cancellationSignal provides the ability to cancel this operation
455      * @param processCallbackExecutor the executor to call {@code progressCallback}
456      * @param progressCallbacks a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to
457      *         the callback that is called repeatedly whenever there is an update on the progress
458      * @return a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to the dexopt result.
459      * @throws IllegalStateException if the operation encounters an error that should never happen
460      *         (e.g., an internal logic error), or the callback set by {@link
461      *         #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} provides invalid
462      *         params.
463      *
464      * @hide
465      */
466     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
467     @NonNull
dexoptPackages( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks)468     public Map<Integer, DexoptResult> dexoptPackages(
469             @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
470             @NonNull @BatchDexoptReason String reason,
471             @NonNull CancellationSignal cancellationSignal,
472             @Nullable @CallbackExecutor Executor progressCallbackExecutor,
473             @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks) {
474         List<String> defaultPackages =
475                 Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
476         DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build();
477         var builder = new BatchDexoptParams.Builder(defaultPackages, defaultDexoptParams);
478         Callback<BatchDexoptStartCallback, Void> callback =
479                 mInjector.getConfig().getBatchDexoptStartCallback();
480         if (callback != null) {
481             Utils.executeAndWait(callback.executor(), () -> {
482                 callback.get().onBatchDexoptStart(
483                         snapshot, reason, defaultPackages, builder, cancellationSignal);
484             });
485         }
486         BatchDexoptParams params = builder.build();
487         Utils.check(params.getDexoptParams().getReason().equals(reason));
488 
489         ExecutorService dexoptExecutor =
490                 Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason));
491         Map<Integer, DexoptResult> dexoptResults = new HashMap<>();
492         mCleanupLock.readLock().lock();
493         try (var pin = mInjector.createArtdPin()) {
494             if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
495                 DexoptResult downgradeResult = maybeDowngradePackages(snapshot,
496                         new HashSet<>(params.getPackages()) /* excludedPackages */,
497                         cancellationSignal, dexoptExecutor, progressCallbackExecutor,
498                         progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_DOWNGRADE)
499                                                   : null);
500                 if (downgradeResult != null) {
501                     dexoptResults.put(ArtFlags.PASS_DOWNGRADE, downgradeResult);
502                 }
503             }
504             AsLog.i("Dexopting " + params.getPackages().size() + " packages with reason=" + reason);
505             DexoptResult mainResult = mInjector.getDexoptHelper().dexopt(snapshot,
506                     params.getPackages(), params.getDexoptParams(), cancellationSignal,
507                     dexoptExecutor, progressCallbackExecutor,
508                     progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_MAIN) : null);
509             dexoptResults.put(ArtFlags.PASS_MAIN, mainResult);
510             if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
511                 DexoptResult supplementaryResult = maybeDexoptPackagesSupplementaryPass(snapshot,
512                         mainResult, params.getDexoptParams(), cancellationSignal, dexoptExecutor,
513                         progressCallbackExecutor,
514                         progressCallbacks != null
515                                 ? progressCallbacks.get(ArtFlags.PASS_SUPPLEMENTARY)
516                                 : null);
517                 if (supplementaryResult != null) {
518                     dexoptResults.put(ArtFlags.PASS_SUPPLEMENTARY, supplementaryResult);
519                 }
520             }
521             return dexoptResults;
522         } finally {
523             mCleanupLock.readLock().unlock();
524             dexoptExecutor.shutdown();
525         }
526     }
527 
528     /**
529      * Overrides the default params for {@link #dexoptPackages}. This method is thread-safe.
530      *
531      * This method gives users the opportunity to change the behavior of {@link #dexoptPackages},
532      * which is called by ART Service automatically during boot / background dexopt.
533      *
534      * If this method is not called, the default list of packages and options determined by {@code
535      * reason} will be used.
536      */
537     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setBatchDexoptStartCallback(@onNull @allbackExecutor Executor executor, @NonNull BatchDexoptStartCallback callback)538     public void setBatchDexoptStartCallback(@NonNull @CallbackExecutor Executor executor,
539             @NonNull BatchDexoptStartCallback callback) {
540         mInjector.getConfig().setBatchDexoptStartCallback(executor, callback);
541     }
542 
543     /**
544      * Clears the callback set by {@link
545      * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. This method is
546      * thread-safe.
547      */
548     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
clearBatchDexoptStartCallback()549     public void clearBatchDexoptStartCallback() {
550         mInjector.getConfig().clearBatchDexoptStartCallback();
551     }
552 
553     /**
554      * Schedules a background dexopt job. Does nothing if the job is already scheduled.
555      *
556      * Use this method if you want the system to automatically determine the best time to run
557      * dexopt.
558      *
559      * The job will be run by the job scheduler. The job scheduling configuration can be overridden
560      * by {@link
561      * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. By
562      * default, it runs periodically (at most once a day) when all the following constraints are
563      * meet.
564      *
565      * <ul>
566      *   <li>The device is idling. (see {@link JobInfo.Builder#setRequiresDeviceIdle(boolean)})
567      *   <li>The device is charging. (see {@link JobInfo.Builder#setRequiresCharging(boolean)})
568      *   <li>The battery level is not low.
569      *     (see {@link JobInfo.Builder#setRequiresBatteryNotLow(boolean)})
570      * </ul>
571      *
572      * When the job is running, it may be cancelled by the job scheduler immediately whenever one of
573      * the constraints above is no longer met or cancelled by the {@link
574      * #cancelBackgroundDexoptJob()} API. The job scheduler retries it with the default retry policy
575      * (30 seconds, exponential, capped at 5hrs).
576      *
577      * See {@link #dexoptPackages} for how to customize the behavior of the job.
578      *
579      * When the job ends (either completed or cancelled), the result is sent to the callbacks added
580      * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the
581      * reason {@link ReasonMapping#REASON_BG_DEXOPT}.
582      *
583      * @throws RuntimeException if called during boot before the job scheduler service has started.
584      */
585     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
scheduleBackgroundDexoptJob()586     public @ScheduleStatus int scheduleBackgroundDexoptJob() {
587         return mInjector.getBackgroundDexoptJob().schedule();
588     }
589 
590     /**
591      * Unschedules the background dexopt job scheduled by {@link #scheduleBackgroundDexoptJob()}.
592      * Does nothing if the job is not scheduled.
593      *
594      * Use this method if you no longer want the system to automatically run dexopt.
595      *
596      * If the job is already started by the job scheduler and is running, it will be cancelled
597      * immediately, and the result sent to the callbacks added by {@link
598      * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link
599      * DexoptResult#DEXOPT_CANCELLED}. Note that a job started by {@link
600      * #startBackgroundDexoptJob()} will not be cancelled by this method.
601      */
602     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
unscheduleBackgroundDexoptJob()603     public void unscheduleBackgroundDexoptJob() {
604         mInjector.getBackgroundDexoptJob().unschedule();
605     }
606 
607     /**
608      * Overrides the configuration of the background dexopt job. This method is thread-safe.
609      */
610     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setScheduleBackgroundDexoptJobCallback(@onNull @allbackExecutor Executor executor, @NonNull ScheduleBackgroundDexoptJobCallback callback)611     public void setScheduleBackgroundDexoptJobCallback(@NonNull @CallbackExecutor Executor executor,
612             @NonNull ScheduleBackgroundDexoptJobCallback callback) {
613         mInjector.getConfig().setScheduleBackgroundDexoptJobCallback(executor, callback);
614     }
615 
616     /**
617      * Clears the callback set by {@link
618      * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. This
619      * method is thread-safe.
620      */
621     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
clearScheduleBackgroundDexoptJobCallback()622     public void clearScheduleBackgroundDexoptJobCallback() {
623         mInjector.getConfig().clearScheduleBackgroundDexoptJobCallback();
624     }
625 
626     /**
627      * Manually starts a background dexopt job. Does nothing if a job is already started by this
628      * method or by the job scheduler. This method is not blocking.
629      *
630      * Unlike the job started by job scheduler, the job started by this method does not respect
631      * constraints described in {@link #scheduleBackgroundDexoptJob()}, and hence will not be
632      * cancelled when they aren't met.
633      *
634      * See {@link #dexoptPackages} for how to customize the behavior of the job.
635      *
636      * When the job ends (either completed or cancelled), the result is sent to the callbacks added
637      * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the
638      * reason {@link ReasonMapping#REASON_BG_DEXOPT}.
639      */
640     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
startBackgroundDexoptJob()641     public void startBackgroundDexoptJob() {
642         mInjector.getBackgroundDexoptJob().start();
643     }
644 
645     /**
646      * Same as above, but also returns a {@link CompletableFuture}.
647      *
648      * @hide
649      */
650     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
651     @NonNull
startBackgroundDexoptJobAndReturnFuture()652     public CompletableFuture<BackgroundDexoptJob.Result> startBackgroundDexoptJobAndReturnFuture() {
653         return mInjector.getBackgroundDexoptJob().start();
654     }
655 
656     /**
657      * Returns the running background dexopt job, or null of no background dexopt job is running.
658      *
659      * @hide
660      */
661     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
662     @Nullable
getRunningBackgroundDexoptJob()663     public CompletableFuture<BackgroundDexoptJob.Result> getRunningBackgroundDexoptJob() {
664         return mInjector.getBackgroundDexoptJob().get();
665     }
666 
667     /**
668      * Cancels the running background dexopt job started by the job scheduler or by {@link
669      * #startBackgroundDexoptJob()}. Does nothing if the job is not running. This method is not
670      * blocking.
671      *
672      * The result sent to the callbacks added by {@link
673      * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link
674      * DexoptResult#DEXOPT_CANCELLED}.
675      */
676     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
cancelBackgroundDexoptJob()677     public void cancelBackgroundDexoptJob() {
678         mInjector.getBackgroundDexoptJob().cancel();
679     }
680 
681     /**
682      * Adds a global listener that listens to any result of dexopting package(s), no matter run
683      * manually or automatically. Calling this method multiple times with different callbacks is
684      * allowed. Callbacks are executed in the same order as the one in which they were added. This
685      * method is thread-safe.
686      *
687      * @param onlyIncludeUpdates if true, the results passed to the callback will only contain
688      *         packages that have any update, and the callback won't be called with results that
689      *         don't have any update.
690      * @throws IllegalStateException if the same callback instance is already added
691      */
692     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
addDexoptDoneCallback(boolean onlyIncludeUpdates, @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback)693     public void addDexoptDoneCallback(boolean onlyIncludeUpdates,
694             @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback) {
695         mInjector.getConfig().addDexoptDoneCallback(onlyIncludeUpdates, executor, callback);
696     }
697 
698     /**
699      * Removes the listener added by {@link
700      * #addDexoptDoneCallback(Executor, DexoptDoneCallback)}. Does nothing if the
701      * callback was not added. This method is thread-safe.
702      */
703     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
removeDexoptDoneCallback(@onNull DexoptDoneCallback callback)704     public void removeDexoptDoneCallback(@NonNull DexoptDoneCallback callback) {
705         mInjector.getConfig().removeDexoptDoneCallback(callback);
706     }
707 
708     /**
709      * Snapshots the profile of the given app split. The profile snapshot is the aggregation of all
710      * existing profiles of the app split (all current user profiles and the reference profile).
711      *
712      * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
713      * @param packageName the name of the app that owns the profile
714      * @param splitName see {@link AndroidPackageSplit#getName()}
715      * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The
716      *         caller is responsible for closing it. Note that the content may be empty.
717      * @throws IllegalArgumentException if the package or the split is not found
718      * @throws IllegalStateException if the operation encounters an error that should never happen
719      *         (e.g., an internal logic error).
720      * @throws SnapshotProfileException if the operation encounters an error that the caller should
721      *         handle (e.g., an I/O error, a sub-process crash).
722      */
723     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
724     @NonNull
snapshotAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName)725     public ParcelFileDescriptor snapshotAppProfile(
726             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
727             @Nullable String splitName) throws SnapshotProfileException {
728         var options = new MergeProfileOptions();
729         options.forceMerge = true;
730         return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options);
731     }
732 
733     /**
734      * Same as above, but outputs in text format.
735      *
736      * @hide
737      */
738     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
739     @NonNull
dumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, boolean dumpClassesAndMethods)740     public ParcelFileDescriptor dumpAppProfile(
741             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
742             @Nullable String splitName, boolean dumpClassesAndMethods)
743             throws SnapshotProfileException {
744         var options = new MergeProfileOptions();
745         options.dumpOnly = !dumpClassesAndMethods;
746         options.dumpClassesAndMethods = dumpClassesAndMethods;
747         return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options);
748     }
749 
750     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
751     @NonNull
snapshotOrDumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, @NonNull MergeProfileOptions options)752     private ParcelFileDescriptor snapshotOrDumpAppProfile(
753             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
754             @Nullable String splitName, @NonNull MergeProfileOptions options)
755             throws SnapshotProfileException {
756         try (var pin = mInjector.createArtdPin()) {
757             PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
758             AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
759             PrimaryDexInfo dexInfo = PrimaryDexUtils.getDexInfoBySplitName(pkg, splitName);
760             DexMetadataPath dmPath = AidlUtils.buildDexMetadataPath(dexInfo.dexPath());
761             DexMetadataInfo dmInfo = mInjector.getDexMetadataHelper().getDexMetadataInfo(dmPath);
762 
763             List<ProfilePath> profiles = new ArrayList<>();
764 
765             // Doesn't support Pre-reboot.
766             InitProfileResult result = Utils.getOrInitReferenceProfile(mInjector.getArtd(),
767                     dexInfo.dexPath(),
768                     PrimaryDexUtils.buildRefProfilePathAsInput(pkgState, dexInfo),
769                     PrimaryDexUtils.getExternalProfiles(dexInfo),
770                     dmInfo.config().getEnableEmbeddedProfile(),
771                     PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID,
772                             Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */));
773             if (!result.externalProfileErrors().isEmpty()) {
774                 AsLog.e("Error occurred when initializing from external profiles: "
775                         + result.externalProfileErrors());
776             }
777 
778             ProfilePath refProfile = result.profile();
779 
780             if (refProfile != null) {
781                 profiles.add(refProfile);
782             }
783 
784             profiles.addAll(
785                     PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo));
786 
787             // Doesn't support Pre-reboot.
788             OutputProfile output =
789                     PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID,
790                             Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */);
791 
792             try {
793                 return mergeProfilesAndGetFd(profiles, output, List.of(dexInfo.dexPath()), options);
794             } finally {
795                 if (refProfile != null && refProfile.getTag() == ProfilePath.tmpProfilePath) {
796                     mInjector.getArtd().deleteProfile(refProfile);
797                 }
798             }
799         } catch (RemoteException e) {
800             throw new SnapshotProfileException(e);
801         }
802     }
803 
804     /**
805      * Snapshots the boot image profile
806      * (https://source.android.com/docs/core/bootloader/boot-image-profiles). The profile snapshot
807      * is the aggregation of all existing profiles on the device (all current user profiles and
808      * reference profiles) of all apps and the system server filtered by applicable classpaths.
809      *
810      * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
811      * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The
812      *         caller is responsible for closing it. Note that the content may be empty.
813      * @throws IllegalStateException if the operation encounters an error that should never happen
814      *         (e.g., an internal logic error).
815      * @throws SnapshotProfileException if the operation encounters an error that the caller should
816      *         handle (e.g., an I/O error, a sub-process crash).
817      */
818     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
819     @NonNull
snapshotBootImageProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot)820     public ParcelFileDescriptor snapshotBootImageProfile(
821             @NonNull PackageManagerLocal.FilteredSnapshot snapshot)
822             throws SnapshotProfileException {
823         if (!Constants.isBootImageProfilingEnabled()) {
824             throw new SnapshotProfileException("Boot image profiling not enabled");
825         }
826 
827         List<ProfilePath> profiles = new ArrayList<>();
828 
829         // System server profiles.
830         profiles.add(AidlUtils.buildProfilePathForPrimaryRefAsInput(
831                 Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY));
832         for (UserHandle handle :
833                 mInjector.getUserManager().getUserHandles(true /* excludeDying */)) {
834             profiles.add(AidlUtils.buildProfilePathForPrimaryCur(handle.getIdentifier(),
835                     Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY));
836         }
837 
838         // App profiles.
839         snapshot.getPackageStates().forEach((packageName, appPkgState) -> {
840             // Hibernating apps can still provide useful profile contents, so skip the hibernation
841             // check.
842             if (Utils.canDexoptPackage(appPkgState, null /* appHibernationManager */)) {
843                 AndroidPackage appPkg = Utils.getPackageOrThrow(appPkgState);
844                 ProfileLists list = mInjector.getArtFileManager().getProfiles(appPkgState, appPkg,
845                         ArtFileManager.Options.builder().setForPrimaryDex(true).build());
846                 profiles.addAll(list.allProfiles());
847             }
848         });
849 
850         // Doesn't support Pre-reboot.
851         OutputProfile output = AidlUtils.buildOutputProfileForPrimary(Utils.PLATFORM_PACKAGE_NAME,
852                 PrimaryDexUtils.PROFILE_PRIMARY, Process.SYSTEM_UID, Process.SYSTEM_UID,
853                 false /* isPublic */, false /* isPreReboot */);
854 
855         List<String> dexPaths = Arrays.stream(CLASSPATHS_FOR_BOOT_IMAGE_PROFILE)
856                                         .map(envVar -> Constants.getenv(envVar))
857                                         .filter(classpath -> !TextUtils.isEmpty(classpath))
858                                         .flatMap(classpath -> Arrays.stream(classpath.split(":")))
859                                         .collect(Collectors.toList());
860 
861         var options = new MergeProfileOptions();
862         options.forceMerge = true;
863         options.forBootImage = true;
864 
865         try (var pin = mInjector.createArtdPin()) {
866             return mergeProfilesAndGetFd(profiles, output, dexPaths, options);
867         }
868     }
869 
870     /**
871      * Notifies ART Service that this is a boot that falls into one of the categories listed in
872      * {@link BootReason}. The current behavior is that ART Service goes through all recently used
873      * packages and dexopts those that are not dexopted. This might change in the future.
874      *
875      * This method is blocking. It takes about 30 seconds to a few minutes. During execution, {@code
876      * progressCallback} is repeatedly called whenever there is an update on the progress.
877      *
878      * See {@link #dexoptPackages} for how to customize the behavior.
879      */
880     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
onBoot(@onNull @ootReason String bootReason, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)881     public void onBoot(@NonNull @BootReason String bootReason,
882             @Nullable @CallbackExecutor Executor progressCallbackExecutor,
883             @Nullable Consumer<OperationProgress> progressCallback) {
884         try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
885             if ((bootReason.equals(ReasonMapping.REASON_BOOT_AFTER_OTA)
886                         || bootReason.equals(ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE))
887                     && SdkLevel.isAtLeastV()) {
888                 // The staged files have to be committed in two phases, one during boot, for primary
889                 // dex files, and another after boot complete, for secondary dex files. We need to
890                 // commit files for primary dex files early because apps will start using them as
891                 // soon as the package manager is initialized. We need to wait until boot complete
892                 // to commit files for secondary dex files because they are not decrypted before
893                 // then.
894                 mShouldCommitPreRebootStagedFiles = true;
895                 mStatsAfterRebootSession =
896                         mInjector.getPreRebootStatsReporter().new AfterRebootSession();
897                 commitPreRebootStagedFiles(snapshot, false /* forSecondary */);
898             }
899             dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor,
900                     progressCallback != null ? Map.of(ArtFlags.PASS_MAIN, progressCallback) : null);
901         }
902     }
903 
904     /**
905      * Notifies this class that {@link Context#registerReceiver} is ready for use.
906      *
907      * Should be used by {@link DexUseManagerLocal} ONLY.
908      *
909      * @hide
910      */
911     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
systemReady()912     void systemReady() {
913         if (mShouldCommitPreRebootStagedFiles) {
914             mInjector.getContext().registerReceiver(new BroadcastReceiver() {
915                 @Override
916                 public void onReceive(Context context, Intent intent) {
917                     context.unregisterReceiver(this);
918                     if (!SdkLevel.isAtLeastV()) {
919                         throw new IllegalStateException("Broadcast receiver unexpectedly called");
920                     }
921                     try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
922                         commitPreRebootStagedFiles(snapshot, true /* forSecondary */);
923                     }
924                     mStatsAfterRebootSession.reportAsync();
925                     mStatsAfterRebootSession = null;
926                 }
927             }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
928         }
929     }
930 
931     /**
932      * Notifies ART Service that there are apexes staged for installation on next reboot (see
933      * <a href="https://source.android.com/docs/core/ota/apex#apex-manager">the update sequence of
934      * an APEX</a>). ART Service may use this to schedule a pre-reboot dexopt job. This might change
935      * in the future.
936      *
937      * This immediately returns after scheduling the job and doesn't wait for the job to run.
938      *
939      * @param stagedApexModuleNames The <b>module names</b> of the staged apexes, corresponding to
940      *         the directory beneath /apex, e.g., {@code com.android.art} (not the <b>package
941      *         names</b>, e.g., {@code com.google.android.art}).
942      */
943     @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
944     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
onApexStaged(@onNull String[] stagedApexModuleNames)945     public void onApexStaged(@NonNull String[] stagedApexModuleNames) {
946         mInjector.getPreRebootDexoptJob().onUpdateReady(null /* otaSlot */);
947     }
948 
949     /**
950      * Dumps the dexopt state of all packages in text format for debugging purposes.
951      *
952      * There are no stability guarantees for the output format.
953      *
954      * @throws IllegalStateException if the operation encounters an error that should never happen
955      *         (e.g., an internal logic error).
956      */
957     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
dump( @onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot)958     public void dump(
959             @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) {
960         try (var pin = mInjector.createArtdPin()) {
961             new DumpHelper(this).dump(pw, snapshot);
962         }
963     }
964 
965     /**
966      * Dumps the dexopt state of the given package in text format for debugging purposes.
967      *
968      * There are no stability guarantees for the output format.
969      *
970      * @throws IllegalArgumentException if the package is not found
971      * @throws IllegalStateException if the operation encounters an error that should never happen
972      *         (e.g., an internal logic error).
973      */
974     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
dumpPackage(@onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)975     public void dumpPackage(@NonNull PrintWriter pw,
976             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
977         try (var pin = mInjector.createArtdPin()) {
978             new DumpHelper(this).dumpPackage(
979                     pw, snapshot, Utils.getPackageStateOrThrow(snapshot, packageName));
980         }
981     }
982 
983     /**
984      * Returns the statistics of the files managed by ART of a package.
985      *
986      * @throws IllegalArgumentException if the package is not found
987      * @throws IllegalStateException if the operation encounters an error that should never happen
988      *         (e.g., an internal logic error).
989      */
990     @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
991     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
992     @NonNull
getArtManagedFileStats( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)993     public ArtManagedFileStats getArtManagedFileStats(
994             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
995         PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
996         AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
997 
998         try (var pin = mInjector.createArtdPin()) {
999             long artifactsSize = 0;
1000             long refProfilesSize = 0;
1001             long curProfilesSize = 0;
1002             IArtd artd = mInjector.getArtd();
1003 
1004             UsableArtifactLists artifactLists =
1005                     mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg);
1006             for (ArtifactsPath artifacts : artifactLists.artifacts()) {
1007                 artifactsSize += artd.getArtifactsSize(artifacts);
1008             }
1009             for (VdexPath vdexFile : artifactLists.vdexFiles()) {
1010                 artifactsSize += artd.getVdexFileSize(vdexFile);
1011             }
1012             for (RuntimeArtifactsPath runtimeArtifacts : artifactLists.runtimeArtifacts()) {
1013                 artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts);
1014             }
1015 
1016             ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg,
1017                     ArtFileManager.Options.builder()
1018                             .setForPrimaryDex(true)
1019                             .setForSecondaryDex(true)
1020                             .setExcludeForObsoleteDexesAndLoaders(true)
1021                             .build());
1022             for (ProfilePath profile : profileLists.refProfiles()) {
1023                 refProfilesSize += artd.getProfileSize(profile);
1024             }
1025             for (ProfilePath profile : profileLists.curProfiles()) {
1026                 curProfilesSize += artd.getProfileSize(profile);
1027             }
1028 
1029             return new ArtManagedFileStats(artifactsSize, refProfilesSize, curProfilesSize);
1030         } catch (RemoteException e) {
1031             Utils.logArtdException(e);
1032             return new ArtManagedFileStats(
1033                     0 /* artifactsSize */, 0 /* refProfilesSize */, 0 /* curProfilesSize */);
1034         }
1035     }
1036 
1037     /**
1038      * Overrides the compiler filter of a package. The callback is called whenever a package is
1039      * going to be dexopted. This method is thread-safe.
1040      */
1041     @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
1042     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setAdjustCompilerFilterCallback(@onNull @allbackExecutor Executor executor, @NonNull AdjustCompilerFilterCallback callback)1043     public void setAdjustCompilerFilterCallback(@NonNull @CallbackExecutor Executor executor,
1044             @NonNull AdjustCompilerFilterCallback callback) {
1045         mInjector.getConfig().setAdjustCompilerFilterCallback(executor, callback);
1046     }
1047 
1048     /**
1049      * Clears the callback set by {@link
1050      * #setAdjustCompilerFilterCallback(Executor, AdjustCompilerFilterCallback)}. This
1051      * method is thread-safe.
1052      */
1053     @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
1054     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
clearAdjustCompilerFilterCallback()1055     public void clearAdjustCompilerFilterCallback() {
1056         mInjector.getConfig().clearAdjustCompilerFilterCallback();
1057     }
1058 
1059     /**
1060      * Cleans up obsolete profiles and artifacts.
1061      *
1062      * This is done in a mark-and-sweep approach.
1063      *
1064      * @return The amount of the disk space freed by the cleanup, in bytes.
1065      * @hide
1066      */
1067     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
cleanup(@onNull PackageManagerLocal.FilteredSnapshot snapshot)1068     public long cleanup(@NonNull PackageManagerLocal.FilteredSnapshot snapshot) {
1069         mCleanupLock.writeLock().lock();
1070         try (var pin = mInjector.createArtdPin()) {
1071             mInjector.getDexUseManager().cleanup();
1072 
1073             // For every primary dex container file or secondary dex container file of every app, if
1074             // it has code, we keep the following types of files:
1075             // - The reference profile and the current profiles, regardless of the hibernation state
1076             //   of the app.
1077             // - The dexopt artifacts, if they are up-to-date and the app is not hibernating.
1078             // - Only the VDEX part of the dexopt artifacts, if the dexopt artifacts are outdated
1079             //   but the VDEX part is still usable and the app is not hibernating.
1080             // - The runtime artifacts, if dexopt artifacts are fully or partially usable and the
1081             //   usable parts don't contain AOT-compiled code. (This logic must be aligned with the
1082             //   one that determines when runtime images can be loaded in
1083             //   `OatFileManager::OpenDexFilesFromOat` in `art/runtime/oat_file_manager.cc`.)
1084             List<ProfilePath> profilesToKeep = new ArrayList<>();
1085             List<ArtifactsPath> artifactsToKeep = new ArrayList<>();
1086             List<VdexPath> vdexFilesToKeep = new ArrayList<>();
1087             List<RuntimeArtifactsPath> runtimeArtifactsToKeep = new ArrayList<>();
1088 
1089             for (PackageState pkgState : snapshot.getPackageStates().values()) {
1090                 if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) {
1091                     continue;
1092                 }
1093                 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
1094                 ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg,
1095                         ArtFileManager.Options.builder()
1096                                 .setForPrimaryDex(true)
1097                                 .setForSecondaryDex(true)
1098                                 .setExcludeForObsoleteDexesAndLoaders(true)
1099                                 .build());
1100                 profilesToKeep.addAll(profileLists.allProfiles());
1101                 if (!Utils.shouldSkipDexoptDueToHibernation(
1102                             pkgState, mInjector.getAppHibernationManager())) {
1103                     UsableArtifactLists artifactLists =
1104                             mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg);
1105                     artifactsToKeep.addAll(artifactLists.artifacts());
1106                     vdexFilesToKeep.addAll(artifactLists.vdexFiles());
1107                     runtimeArtifactsToKeep.addAll(artifactLists.runtimeArtifacts());
1108                 }
1109             }
1110             return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep,
1111                     runtimeArtifactsToKeep,
1112                     SdkLevel.isAtLeastV() && mInjector.getPreRebootDexoptJob().hasStarted());
1113         } catch (RemoteException e) {
1114             Utils.logArtdException(e);
1115             return 0;
1116         } finally {
1117             mCleanupLock.writeLock().unlock();
1118         }
1119     }
1120 
1121     /** @param forSecondary true for secondary dex files; false for primary dex files. */
1122     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
commitPreRebootStagedFiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, boolean forSecondary)1123     private void commitPreRebootStagedFiles(
1124             @NonNull PackageManagerLocal.FilteredSnapshot snapshot, boolean forSecondary) {
1125         try (var pin = mInjector.createArtdPin()) {
1126             // Because we don't know for which packages the Pre-reboot Dexopt job has generated
1127             // staged files, we call artd for all dexoptable packages, which is a superset of the
1128             // packages that we actually expect to have staged files.
1129             for (PackageState pkgState : snapshot.getPackageStates().values()) {
1130                 if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) {
1131                     continue;
1132                 }
1133                 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
1134                 var options = ArtFileManager.Options.builder()
1135                                       .setForPrimaryDex(!forSecondary)
1136                                       .setForSecondaryDex(forSecondary)
1137                                       .setExcludeForObsoleteDexesAndLoaders(true)
1138                                       .build();
1139                 List<ArtifactsPath> artifacts =
1140                         mInjector.getArtFileManager()
1141                                 .getWritableArtifacts(pkgState, pkg, options)
1142                                 .artifacts();
1143                 List<WritableProfilePath> profiles = mInjector.getArtFileManager()
1144                                                              .getProfiles(pkgState, pkg, options)
1145                                                              .refProfiles()
1146                                                              .stream()
1147                                                              .map(AidlUtils::toWritableProfilePath)
1148                                                              .collect(Collectors.toList());
1149                 try {
1150                     // The artd method commits all files somewhat transactionally. Here, we are
1151                     // committing files transactionally at the package level just for simplicity. In
1152                     // fact, we only need transaction on the split level: the artifacts and the
1153                     // profile of the same split must be committed transactionally. Consider the
1154                     // case where the staged artifacts and profile have less methods than the active
1155                     // ones generated by background dexopt, committing the artifacts while failing
1156                     // to commit the profile can potentially cause a permanent performance
1157                     // regression.
1158                     if (mInjector.getArtd().commitPreRebootStagedFiles(artifacts, profiles)) {
1159                         mStatsAfterRebootSession.recordPackageWithArtifacts(
1160                                 pkgState.getPackageName());
1161                     }
1162                 } catch (ServiceSpecificException e) {
1163                     AsLog.e("Failed to commit Pre-reboot staged files for package '"
1164                                     + pkgState.getPackageName() + "'",
1165                             e);
1166                 }
1167             }
1168         } catch (RemoteException e) {
1169             Utils.logArtdException(e);
1170         }
1171     }
1172 
1173     /**
1174      * Should be used by {@link BackgroundDexoptJobService} ONLY.
1175      *
1176      * @hide
1177      */
1178     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1179     @NonNull
getBackgroundDexoptJob()1180     BackgroundDexoptJob getBackgroundDexoptJob() {
1181         return mInjector.getBackgroundDexoptJob();
1182     }
1183 
1184     /**
1185      * Should be used by {@link BackgroundDexoptJobService} ONLY.
1186      *
1187      * @hide
1188      */
1189     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
1190     @NonNull
getPreRebootDexoptJob()1191     PreRebootDexoptJob getPreRebootDexoptJob() {
1192         return mInjector.getPreRebootDexoptJob();
1193     }
1194 
1195     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1196     @Nullable
maybeDowngradePackages( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal, @NonNull Executor executor, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)1197     private DexoptResult maybeDowngradePackages(
1198             @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
1199             @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal,
1200             @NonNull Executor executor,
1201             @Nullable @CallbackExecutor Executor progressCallbackExecutor,
1202             @Nullable Consumer<OperationProgress> progressCallback) {
1203         if (shouldDowngrade()) {
1204             List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE)
1205                                             .stream()
1206                                             .filter(pkg -> !excludedPackages.contains(pkg))
1207                                             .collect(Collectors.toList());
1208             if (!packages.isEmpty()) {
1209                 AsLog.i("Storage is low. Downgrading " + packages.size() + " inactive packages");
1210                 DexoptParams params =
1211                         new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build();
1212                 return mInjector.getDexoptHelper().dexopt(snapshot, packages, params,
1213                         cancellationSignal, executor, progressCallbackExecutor, progressCallback);
1214             } else {
1215                 AsLog.i("Storage is low, but downgrading is disabled or there's nothing to "
1216                         + "downgrade");
1217             }
1218         }
1219         return null;
1220     }
1221 
1222     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
shouldDowngrade()1223     private boolean shouldDowngrade() {
1224         try {
1225             return mInjector.getStorageManager().getAllocatableBytes(StorageManager.UUID_DEFAULT)
1226                     < DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES;
1227         } catch (IOException e) {
1228             AsLog.e("Failed to check storage. Assuming storage not low", e);
1229             return false;
1230         }
1231     }
1232 
1233     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1234     @Nullable
maybeDexoptPackagesSupplementaryPass( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull DexoptResult mainResult, @NonNull DexoptParams mainParams, @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)1235     private DexoptResult maybeDexoptPackagesSupplementaryPass(
1236             @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
1237             @NonNull DexoptResult mainResult, @NonNull DexoptParams mainParams,
1238             @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor,
1239             @Nullable @CallbackExecutor Executor progressCallbackExecutor,
1240             @Nullable Consumer<OperationProgress> progressCallback) {
1241         if ((mainParams.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) != 0) {
1242             return null;
1243         }
1244 
1245         // Only pick packages that used a profile-guided filter and were skipped in the main pass.
1246         // This is a very coarse filter to reduce unnecessary iterations on a best-effort basis.
1247         // Packages included in the list may still be skipped by dexopter if the profiles don't have
1248         // any change.
1249         List<String> packageNames =
1250                 mainResult.getPackageDexoptResults()
1251                         .stream()
1252                         .filter(packageResult
1253                                 -> packageResult.getDexContainerFileDexoptResults()
1254                                            .stream()
1255                                            .anyMatch(fileResult
1256                                                    -> DexFile.isProfileGuidedCompilerFilter(
1257                                                               fileResult.getActualCompilerFilter())
1258                                                            && fileResult.getStatus()
1259                                                                    == DexoptResult.DEXOPT_SKIPPED))
1260                         .map(packageResult -> packageResult.getPackageName())
1261                         .collect(Collectors.toList());
1262 
1263         DexoptParams dexoptParams = mainParams.toBuilder()
1264                                             .setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE,
1265                                                     ArtFlags.FLAG_FORCE_MERGE_PROFILE)
1266                                             .build();
1267 
1268         AsLog.i("Dexopting " + packageNames.size()
1269                 + " packages with reason=" + dexoptParams.getReason() + " (supplementary pass)");
1270         return mInjector.getDexoptHelper().dexopt(snapshot, packageNames, dexoptParams,
1271                 cancellationSignal, dexoptExecutor, progressCallbackExecutor, progressCallback);
1272     }
1273 
1274     /** Returns the list of packages to process for the given reason. */
1275     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1276     @NonNull
getDefaultPackages(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String reason)1277     private List<String> getDefaultPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
1278             @NonNull /* @BatchDexoptReason|REASON_INACTIVE */ String reason) {
1279         var appHibernationManager = mInjector.getAppHibernationManager();
1280 
1281         // Filter out hibernating packages even if the reason is REASON_INACTIVE. This is because
1282         // artifacts for hibernating packages are already deleted.
1283         Stream<PackageState> packages = snapshot.getPackageStates().values().stream().filter(
1284                 pkgState -> Utils.canDexoptPackage(pkgState, appHibernationManager));
1285 
1286         switch (reason) {
1287             case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
1288                 packages = packages.filter(pkgState
1289                         -> mInjector.isSystemUiPackage(pkgState.getPackageName())
1290                                 || mInjector.isLauncherPackage(pkgState.getPackageName()));
1291                 break;
1292             case ReasonMapping.REASON_INACTIVE:
1293                 packages = filterAndSortByLastActiveTime(
1294                         packages, false /* keepRecent */, false /* descending */);
1295                 break;
1296             default:
1297                 // Actually, the sorting is only needed for background dexopt, but we do it for all
1298                 // cases for simplicity.
1299                 packages = filterAndSortByLastActiveTime(
1300                         packages, true /* keepRecent */, true /* descending */);
1301         }
1302 
1303         return packages.map(PackageState::getPackageName).collect(Collectors.toList());
1304     }
1305 
1306     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1307     @NonNull
filterAndSortByLastActiveTime( @onNull Stream<PackageState> packages, boolean keepRecent, boolean descending)1308     private Stream<PackageState> filterAndSortByLastActiveTime(
1309             @NonNull Stream<PackageState> packages, boolean keepRecent, boolean descending) {
1310         // "pm.dexopt.downgrade_after_inactive_days" is repurposed to also determine whether to
1311         // dexopt a package.
1312         long inactiveMs = TimeUnit.DAYS.toMillis(SystemProperties.getInt(
1313                 "pm.dexopt.downgrade_after_inactive_days", Integer.MAX_VALUE /* def */));
1314         long currentTimeMs = mInjector.getCurrentTimeMillis();
1315         long thresholdTimeMs = currentTimeMs - inactiveMs;
1316         return packages
1317                 .map(pkgState
1318                         -> Pair.create(pkgState,
1319                                 Utils.getPackageLastActiveTime(pkgState,
1320                                         mInjector.getDexUseManager(), mInjector.getUserManager())))
1321                 .filter(keepRecent ? (pair -> pair.second > thresholdTimeMs)
1322                                    : (pair -> pair.second <= thresholdTimeMs))
1323                 .sorted(descending ? Comparator.comparingLong(pair -> - pair.second)
1324                                    : Comparator.comparingLong(pair -> pair.second))
1325                 .map(pair -> pair.first);
1326     }
1327 
1328     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1329     @NonNull
mergeProfilesAndGetFd(@onNull List<ProfilePath> profiles, @NonNull OutputProfile output, @NonNull List<String> dexPaths, @NonNull MergeProfileOptions options)1330     private ParcelFileDescriptor mergeProfilesAndGetFd(@NonNull List<ProfilePath> profiles,
1331             @NonNull OutputProfile output, @NonNull List<String> dexPaths,
1332             @NonNull MergeProfileOptions options) throws SnapshotProfileException {
1333         try {
1334             boolean hasContent = false;
1335             try {
1336                 hasContent = mInjector.getArtd().mergeProfiles(
1337                         profiles, null /* referenceProfile */, output, dexPaths, options);
1338             } catch (ServiceSpecificException e) {
1339                 throw new SnapshotProfileException(e);
1340             }
1341 
1342             String path;
1343             Path emptyFile = null;
1344             if (hasContent) {
1345                 path = output.profilePath.tmpPath;
1346             } else {
1347                 // We cannot use /dev/null because `ParcelFileDescriptor` have an API `getStatSize`,
1348                 // which expects the file to be a regular file or a link, and apps may call that
1349                 // API.
1350                 emptyFile =
1351                         Files.createTempFile(Paths.get(mInjector.getTempDir()), "empty", ".tmp");
1352                 path = emptyFile.toString();
1353             }
1354             ParcelFileDescriptor fd;
1355             try {
1356                 fd = ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY);
1357             } catch (FileNotFoundException e) {
1358                 throw new IllegalStateException(
1359                         String.format("Failed to open profile snapshot '%s'", path), e);
1360             }
1361 
1362             // The deletion is done on the open file so that only the FD keeps a reference to the
1363             // file.
1364             if (hasContent) {
1365                 mInjector.getArtd().deleteProfile(ProfilePath.tmpProfilePath(output.profilePath));
1366             } else {
1367                 Files.delete(emptyFile);
1368             }
1369 
1370             return fd;
1371         } catch (IOException | RemoteException e) {
1372             throw new SnapshotProfileException(e);
1373         }
1374     }
1375 
1376     /** @hide */
1377     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
1378     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1379     public interface BatchDexoptStartCallback {
1380         /**
1381          * Mutates {@code builder} to override the default params for {@link #dexoptPackages}. It
1382          * must ignore unknown reasons because more reasons may be added in the future.
1383          *
1384          * This is called before the start of any automatic package dexopt (i.e., not
1385          * including package dexopt initiated by the {@link #dexoptPackage} API call).
1386          *
1387          * If {@code builder.setPackages} is not called, {@code defaultPackages} will be used as the
1388          * list of packages to dexopt.
1389          *
1390          * If {@code builder.setDexoptParams} is not called, the default params built from {@code
1391          * new DexoptParams.Builder(reason)} will to used as the params for dexopting each
1392          * package.
1393          *
1394          * Additionally, {@code cancellationSignal.cancel()} can be called to cancel this operation.
1395          * If this operation is initiated by the job scheduler and the {@code reason} is {@link
1396          * ReasonMapping#REASON_BG_DEXOPT}, the job will be retried with the default retry policy
1397          * (30 seconds, exponential, capped at 5hrs).
1398          *
1399          * Changing the reason is not allowed. Doing so will result in {@link IllegalStateException}
1400          * when {@link #dexoptPackages} is called.
1401          */
onBatchDexoptStart(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages, @NonNull BatchDexoptParams.Builder builder, @NonNull CancellationSignal cancellationSignal)1402         void onBatchDexoptStart(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
1403                 @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages,
1404                 @NonNull BatchDexoptParams.Builder builder,
1405                 @NonNull CancellationSignal cancellationSignal);
1406     }
1407 
1408     /** @hide */
1409     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
1410     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1411     public interface ScheduleBackgroundDexoptJobCallback {
1412         /**
1413          * Mutates {@code builder} to override the configuration of the background dexopt job.
1414          *
1415          * The default configuration described in {@link
1416          * ArtManagerLocal#scheduleBackgroundDexoptJob()} is passed to the callback as the {@code
1417          * builder} argument.
1418          *
1419          * Setting {@link JobInfo.Builder#setBackoffCriteria} is not allowed. Doing so will result
1420          * in {@link IllegalArgumentException} when {@link #scheduleBackgroundDexoptJob()} is
1421          * called. The job is retried with the default retry policy (30 seconds, exponential, capped
1422          * at 5hrs). Unfortunately, due to the limitation of the job scheduler API, this retry
1423          * policy cannot be changed.
1424          *
1425          * Setting {@link JobInfo.Builder#setRequiresStorageNotLow(boolean)} is not allowed. Doing
1426          * so will result in {@link IllegalStateException} when {@link
1427          * #scheduleBackgroundDexoptJob()} is called. ART Service has its own storage check, which
1428          * skips package dexopt when the storage is low. The storage check is enabled by
1429          * default for background dexopt jobs. {@link
1430          * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} can be used to disable
1431          * the storage check by clearing the {@link ArtFlags#FLAG_SKIP_IF_STORAGE_LOW} flag.
1432          */
onOverrideJobInfo(@onNull JobInfo.Builder builder)1433         void onOverrideJobInfo(@NonNull JobInfo.Builder builder);
1434     }
1435 
1436     /** @hide */
1437     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
1438     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1439     public interface DexoptDoneCallback {
onDexoptDone(@onNull DexoptResult result)1440         void onDexoptDone(@NonNull DexoptResult result);
1441     }
1442 
1443     /** @hide */
1444     @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
1445     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
1446     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1447     public interface AdjustCompilerFilterCallback {
1448         /**
1449          * Returns the adjusted compiler filter for the given package. If a package doesn't need
1450          * adjustment, this callback must return {@code originalCompilerFilter}. The callback must
1451          * be able to handle unknown {@code originalCompilerFilter} and unknown {@code reason}
1452          * because more compiler filters and reasons may be added in the future.
1453          *
1454          * The returned compiler filter overrides any compiler filter set by {@link
1455          * DexoptParams.Builder#setCompilerFilter}, no matter the dexopt is initiated by a
1456          * {@link #dexoptPackage} API call or any automatic batch dexopt (e.g., dexopt on boot and
1457          * background dexopt).
1458          *
1459          * This callback is useful for:
1460          * - Consistently overriding the compiler filter regardless of the dexopt initiator, for
1461          *   some performance-sensitive packages.
1462          * - Providing a compiler filter for specific packages during batch dexopt.
1463          *
1464          * The actual compiler filter to be used for dexopt will be determined in the following
1465          * order:
1466          *
1467          * 1. The default compiler filter for the given reason.
1468          * 2. The compiler filter set explicitly by {@link DexoptParams.Builder#setCompilerFilter}.
1469          * 3. ART Service's internal adjustments to upgrade the compiler filter, based on whether
1470          *    the package is System UI, etc. (Not applicable if the dexopt is initiated by a shell
1471          *    command with an explicit "-m" flag.)
1472          * 4. The adjustments made by this callback. (Not applicable if the dexopt is initiated by a
1473          *    shell command with an explicit "-m" flag.)
1474          * 5. ART Service's internal adjustments to downgrade the compiler filter, based on whether
1475          *    the profile is available, etc.
1476          *
1477          * @param packageName the name of the package to be dexopted
1478          * @param originalCompilerFilter the compiler filter before adjustment. This is the result
1479          *         of step 3 described above. It would be the input to step 5 described above if
1480          *         it wasn't for this callback.
1481          * @param reason the compilation reason of this dexopt operation. It is a string defined in
1482          *         {@link ReasonMapping} or a custom string passed to {@link
1483          *         DexoptParams.Builder#Builder(String)}
1484          *
1485          * @return the compiler filter after adjustment. This will be the input to step 5 described
1486          *         above
1487          */
1488         @SuppressLint("UnflaggedApi") // Flag support for mainline is not available.
1489         @NonNull
onAdjustCompilerFilter(@onNull String packageName, @NonNull String originalCompilerFilter, @NonNull String reason)1490         String onAdjustCompilerFilter(@NonNull String packageName,
1491                 @NonNull String originalCompilerFilter, @NonNull String reason);
1492     }
1493 
1494     /**
1495      * Represents an error that happens when snapshotting profiles.
1496      *
1497      * @hide
1498      */
1499     @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
1500     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1501     public static class SnapshotProfileException extends Exception {
1502         /** @hide */
SnapshotProfileException(@onNull Throwable cause)1503         public SnapshotProfileException(@NonNull Throwable cause) {
1504             super(cause);
1505         }
1506 
1507         /** @hide */
SnapshotProfileException(@onNull String message)1508         public SnapshotProfileException(@NonNull String message) {
1509             super(message);
1510         }
1511     }
1512 
1513     /**
1514      * Injector pattern for testing purpose.
1515      *
1516      * @hide
1517      */
1518     @VisibleForTesting
1519     public static class Injector {
1520         @Nullable private final ArtManagerLocal mArtManagerLocal;
1521         @Nullable private final Context mContext;
1522         @Nullable private final PackageManagerLocal mPackageManagerLocal;
1523         @Nullable private final Config mConfig;
1524         @Nullable private BackgroundDexoptJob mBgDexoptJob = null;
1525         @Nullable private PreRebootDexoptJob mPrDexoptJob = null;
1526 
1527         /** For compatibility with S and T. New code should not use this. */
1528         @Deprecated
Injector()1529         Injector() {
1530             mArtManagerLocal = null;
1531             mContext = null;
1532             mPackageManagerLocal = null;
1533             mConfig = null;
1534         }
1535 
1536         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
Injector(@onNull ArtManagerLocal artManagerLocal, @Nullable Context context)1537         Injector(@NonNull ArtManagerLocal artManagerLocal, @Nullable Context context) {
1538             // We only need them on Android U and above, where a context is passed.
1539             mArtManagerLocal = artManagerLocal;
1540             mContext = context;
1541             mPackageManagerLocal = Objects.requireNonNull(
1542                     LocalManagerRegistry.getManager(PackageManagerLocal.class));
1543             mConfig = new Config();
1544 
1545             // Call the getters for the dependencies that aren't optional, to ensure correct
1546             // initialization order.
1547             getDexoptHelper();
1548             getUserManager();
1549             getDexUseManager();
1550             getStorageManager();
1551             GlobalInjector.getInstance().checkArtModuleServiceManager();
1552         }
1553 
1554         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1555         @NonNull
getContext()1556         public Context getContext() {
1557             return Objects.requireNonNull(mContext);
1558         }
1559 
1560         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1561         @NonNull
getPackageManagerLocal()1562         public PackageManagerLocal getPackageManagerLocal() {
1563             return Objects.requireNonNull(mPackageManagerLocal);
1564         }
1565 
1566         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1567         @NonNull
getArtd()1568         public IArtd getArtd() {
1569             return ArtdRefCache.getInstance().getArtd();
1570         }
1571 
1572         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1573         @NonNull
createArtdPin()1574         public ArtdRefCache.Pin createArtdPin() {
1575             return ArtdRefCache.getInstance().new Pin();
1576         }
1577 
1578         /** Returns a new {@link DexoptHelper} instance. */
1579         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1580         @NonNull
getDexoptHelper()1581         public DexoptHelper getDexoptHelper() {
1582             return new DexoptHelper(getContext(), getConfig());
1583         }
1584 
1585         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1586         @NonNull
getConfig()1587         public Config getConfig() {
1588             return mConfig;
1589         }
1590 
1591         /** Returns the registered {@link AppHibernationManager} instance. */
1592         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1593         @NonNull
getAppHibernationManager()1594         public AppHibernationManager getAppHibernationManager() {
1595             return Objects.requireNonNull(mContext.getSystemService(AppHibernationManager.class));
1596         }
1597 
1598         /**
1599          * Returns the {@link BackgroundDexoptJob} instance.
1600          *
1601          * @throws RuntimeException if called during boot before the job scheduler service has
1602          *         started.
1603          */
1604         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1605         @NonNull
getBackgroundDexoptJob()1606         public synchronized BackgroundDexoptJob getBackgroundDexoptJob() {
1607             if (mBgDexoptJob == null) {
1608                 mBgDexoptJob = new BackgroundDexoptJob(mContext, mArtManagerLocal, mConfig);
1609             }
1610             return mBgDexoptJob;
1611         }
1612 
1613         @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
1614         @NonNull
getPreRebootDexoptJob()1615         public synchronized PreRebootDexoptJob getPreRebootDexoptJob() {
1616             if (mPrDexoptJob == null) {
1617                 mPrDexoptJob = new PreRebootDexoptJob(mContext);
1618             }
1619             return mPrDexoptJob;
1620         }
1621 
1622         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1623         @NonNull
getUserManager()1624         public UserManager getUserManager() {
1625             return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
1626         }
1627 
1628         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1629         @NonNull
getDexUseManager()1630         public DexUseManagerLocal getDexUseManager() {
1631             return GlobalInjector.getInstance().getDexUseManager();
1632         }
1633 
1634         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
isSystemUiPackage(@onNull String packageName)1635         public boolean isSystemUiPackage(@NonNull String packageName) {
1636             return Utils.isSystemUiPackage(mContext, packageName);
1637         }
1638 
1639         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
isLauncherPackage(@onNull String packageName)1640         public boolean isLauncherPackage(@NonNull String packageName) {
1641             return Utils.isLauncherPackage(mContext, packageName);
1642         }
1643 
1644         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
getCurrentTimeMillis()1645         public long getCurrentTimeMillis() {
1646             return System.currentTimeMillis();
1647         }
1648 
1649         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1650         @NonNull
getStorageManager()1651         public StorageManager getStorageManager() {
1652             return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
1653         }
1654 
1655         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1656         @NonNull
getTempDir()1657         public String getTempDir() {
1658             // This is a path that system_server is known to have full access to.
1659             return "/data/system";
1660         }
1661 
1662         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1663         @NonNull
getArtFileManager()1664         public ArtFileManager getArtFileManager() {
1665             return new ArtFileManager(getContext());
1666         }
1667 
1668         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1669         @NonNull
getDexMetadataHelper()1670         public DexMetadataHelper getDexMetadataHelper() {
1671             return new DexMetadataHelper();
1672         }
1673 
1674         @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
1675         @NonNull
getPreRebootStatsReporter()1676         public PreRebootStatsReporter getPreRebootStatsReporter() {
1677             return new PreRebootStatsReporter();
1678         }
1679     }
1680 }
1681