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