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.pm;
18 
19 import static android.os.Trace.TRACE_TAG_DALVIK;
20 
21 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
22 import static com.android.server.pm.ApexManager.ActiveApexInfo;
23 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
24 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
25 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
26 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE;
27 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_OTA;
28 import static com.android.server.pm.PackageManagerService.REASON_CMDLINE;
29 import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT;
30 import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
31 import static com.android.server.pm.PackageManagerService.TAG;
32 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
33 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
34 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_APEX_PKG;
35 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG;
36 import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
37 
38 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
39 
40 import android.annotation.NonNull;
41 import android.annotation.Nullable;
42 import android.app.AppGlobals;
43 import android.app.role.RoleManager;
44 import android.content.BroadcastReceiver;
45 import android.content.Context;
46 import android.content.Intent;
47 import android.content.IntentFilter;
48 import android.content.pm.ResolveInfo;
49 import android.content.pm.SharedLibraryInfo;
50 import android.content.pm.dex.ArtManager;
51 import android.os.Binder;
52 import android.os.RemoteException;
53 import android.os.SystemClock;
54 import android.os.SystemProperties;
55 import android.os.Trace;
56 import android.os.UserHandle;
57 import android.text.TextUtils;
58 import android.util.ArraySet;
59 import android.util.Log;
60 import android.util.Slog;
61 
62 import com.android.internal.logging.MetricsLogger;
63 import com.android.internal.util.FrameworkStatsLog;
64 import com.android.internal.util.IndentingPrintWriter;
65 import com.android.server.LocalManagerRegistry;
66 import com.android.server.LocalServices;
67 import com.android.server.PinnerService;
68 import com.android.server.art.ArtManagerLocal;
69 import com.android.server.art.DexUseManagerLocal;
70 import com.android.server.art.ReasonMapping;
71 import com.android.server.art.model.ArtFlags;
72 import com.android.server.art.model.DexoptParams;
73 import com.android.server.art.model.DexoptResult;
74 import com.android.server.pm.Installer.InstallerException;
75 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
76 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
77 import com.android.server.pm.dex.DexManager;
78 import com.android.server.pm.dex.DexoptOptions;
79 import com.android.server.pm.pkg.AndroidPackage;
80 import com.android.server.pm.pkg.PackageState;
81 import com.android.server.pm.pkg.PackageStateInternal;
82 
83 import java.io.File;
84 import java.nio.file.Path;
85 import java.nio.file.Paths;
86 import java.util.ArrayList;
87 import java.util.Collection;
88 import java.util.Collections;
89 import java.util.Comparator;
90 import java.util.HashSet;
91 import java.util.List;
92 import java.util.Set;
93 import java.util.concurrent.TimeUnit;
94 import java.util.function.Predicate;
95 
96 /**
97  * Helper class for dex optimization operations in PackageManagerService.
98  */
99 public final class DexOptHelper {
100     private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
101 
102     private static boolean sArtManagerLocalIsInitialized = false;
103 
104     private final PackageManagerService mPm;
105 
106     // Start time for the boot dexopt in performPackageDexOptUpgradeIfNeeded when ART Service is
107     // used, to make it available to the onDexoptDone callback.
108     private volatile long mBootDexoptStartTime;
109 
DexOptHelper(PackageManagerService pm)110     DexOptHelper(PackageManagerService pm) {
111         mPm = pm;
112     }
113 
114     /*
115      * Return the prebuilt profile path given a package base code path.
116      */
getPrebuildProfilePath(AndroidPackage pkg)117     private static String getPrebuildProfilePath(AndroidPackage pkg) {
118         return pkg.getBaseApkPath() + ".prof";
119     }
120 
121     /**
122      * Performs dexopt on the set of packages in {@code packages} and returns an int array
123      * containing statistics about the invocation. The array consists of three elements,
124      * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
125      * and {@code numberOfPackagesFailed}.
126      */
performDexOptUpgrade(List<PackageStateInternal> packageStates, final int compilationReason, boolean bootComplete)127     public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates,
128             final int compilationReason, boolean bootComplete)
129             throws LegacyDexoptDisabledException {
130         Installer.checkLegacyDexoptDisabled();
131         int numberOfPackagesVisited = 0;
132         int numberOfPackagesOptimized = 0;
133         int numberOfPackagesSkipped = 0;
134         int numberOfPackagesFailed = 0;
135         final int numberOfPackagesToDexopt = packageStates.size();
136 
137         for (var packageState : packageStates) {
138             var pkg = packageState.getAndroidPackage();
139             numberOfPackagesVisited++;
140 
141             boolean useProfileForDexopt = false;
142 
143             if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && packageState.isSystem()) {
144                 // Copy over initial preopt profiles since we won't get any JIT samples for methods
145                 // that are already compiled.
146                 File profileFile = new File(getPrebuildProfilePath(pkg));
147                 // Copy profile if it exists.
148                 if (profileFile.exists()) {
149                     try {
150                         // We could also do this lazily before calling dexopt in
151                         // PackageDexOptimizer to prevent this happening on first boot. The issue
152                         // is that we don't have a good way to say "do this only once".
153                         if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
154                                 pkg.getUid(), pkg.getPackageName(),
155                                 ArtManager.getProfileName(null))) {
156                             Log.e(TAG, "Installer failed to copy system profile!");
157                         } else {
158                             // Disabled as this causes speed-profile compilation during first boot
159                             // even if things are already compiled.
160                             // useProfileForDexopt = true;
161                         }
162                     } catch (InstallerException | RuntimeException e) {
163                         Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
164                                 e);
165                     }
166                 } else {
167                     PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(
168                             pkg.getPackageName());
169                     // Handle compressed APKs in this path. Only do this for stubs with profiles to
170                     // minimize the number off apps being speed-profile compiled during first boot.
171                     // The other paths will not change the filter.
172                     if (disabledPs != null && disabledPs.getPkg().isStub()) {
173                         // The package is the stub one, remove the stub suffix to get the normal
174                         // package and APK names.
175                         String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
176                                 .replace(STUB_SUFFIX, "");
177                         profileFile = new File(systemProfilePath);
178                         // If we have a profile for a compressed APK, copy it to the reference
179                         // location.
180                         // Note that copying the profile here will cause it to override the
181                         // reference profile every OTA even though the existing reference profile
182                         // may have more data. We can't copy during decompression since the
183                         // directories are not set up at that point.
184                         if (profileFile.exists()) {
185                             try {
186                                 // We could also do this lazily before calling dexopt in
187                                 // PackageDexOptimizer to prevent this happening on first boot. The
188                                 // issue is that we don't have a good way to say "do this only
189                                 // once".
190                                 if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
191                                         pkg.getUid(), pkg.getPackageName(),
192                                         ArtManager.getProfileName(null))) {
193                                     Log.e(TAG, "Failed to copy system profile for stub package!");
194                                 } else {
195                                     useProfileForDexopt = true;
196                                 }
197                             } catch (InstallerException | RuntimeException e) {
198                                 Log.e(TAG, "Failed to copy profile "
199                                         + profileFile.getAbsolutePath() + " ", e);
200                             }
201                         }
202                     }
203                 }
204             }
205 
206             if (!mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) {
207                 if (DEBUG_DEXOPT) {
208                     Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
209                 }
210                 numberOfPackagesSkipped++;
211                 continue;
212             }
213 
214             if (DEBUG_DEXOPT) {
215                 Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of "
216                         + numberOfPackagesToDexopt + ": " + pkg.getPackageName());
217             }
218 
219             int pkgCompilationReason = compilationReason;
220             if (useProfileForDexopt) {
221                 // Use background dexopt mode to try and use the profile. Note that this does not
222                 // guarantee usage of the profile.
223                 pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
224             }
225 
226             // TODO(b/251903639): Do this when ART Service is used, or remove it from here.
227             if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
228                 mPm.mArtManagerService.compileLayouts(packageState, pkg);
229             }
230 
231             int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
232 
233             String filter = getCompilerFilterForReason(pkgCompilationReason);
234             if (isProfileGuidedCompilerFilter(filter)) {
235                 // DEXOPT_CHECK_FOR_PROFILES_UPDATES used to be false to avoid merging profiles
236                 // during boot which might interfere with background compilation (b/28612421).
237                 // However those problems were related to the verify-profile compiler filter which
238                 // doesn't exist any more, so enable it again.
239                 dexoptFlags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES;
240             }
241 
242             if (compilationReason == REASON_FIRST_BOOT) {
243                 // TODO: This doesn't cover the upgrade case, we should check for this too.
244                 dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
245             }
246             int primaryDexOptStatus = performDexOptTraced(
247                     new DexoptOptions(pkg.getPackageName(), pkgCompilationReason, filter,
248                             /*splitName*/ null, dexoptFlags));
249 
250             switch (primaryDexOptStatus) {
251                 case PackageDexOptimizer.DEX_OPT_PERFORMED:
252                     numberOfPackagesOptimized++;
253                     break;
254                 case PackageDexOptimizer.DEX_OPT_SKIPPED:
255                     numberOfPackagesSkipped++;
256                     break;
257                 case PackageDexOptimizer.DEX_OPT_CANCELLED:
258                     // ignore this case
259                     break;
260                 case PackageDexOptimizer.DEX_OPT_FAILED:
261                     numberOfPackagesFailed++;
262                     break;
263                 default:
264                     Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStatus);
265                     break;
266             }
267         }
268 
269         return new int[]{numberOfPackagesOptimized, numberOfPackagesSkipped,
270                 numberOfPackagesFailed};
271     }
272 
273     /**
274      * Checks if system UI package (typically "com.android.systemui") needs to be re-compiled, and
275      * compiles it if needed.
276      */
checkAndDexOptSystemUi(int reason)277     private void checkAndDexOptSystemUi(int reason) throws LegacyDexoptDisabledException {
278         Computer snapshot = mPm.snapshotComputer();
279         String sysUiPackageName =
280                 mPm.mContext.getString(com.android.internal.R.string.config_systemUi);
281         AndroidPackage pkg = snapshot.getPackage(sysUiPackageName);
282         if (pkg == null) {
283             Log.w(TAG, "System UI package " + sysUiPackageName + " is not found for dexopting");
284             return;
285         }
286 
287         String defaultCompilerFilter = getCompilerFilterForReason(reason);
288         String targetCompilerFilter =
289                 SystemProperties.get("dalvik.vm.systemuicompilerfilter", defaultCompilerFilter);
290         String compilerFilter;
291 
292         if (isProfileGuidedCompilerFilter(targetCompilerFilter)) {
293             compilerFilter = "verify";
294             File profileFile = new File(getPrebuildProfilePath(pkg));
295 
296             // Copy the profile to the reference profile path if it exists. Installd can only use a
297             // profile at the reference profile path for dexopt.
298             if (profileFile.exists()) {
299                 try {
300                     synchronized (mPm.mInstallLock) {
301                         if (mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
302                                     pkg.getUid(), pkg.getPackageName(),
303                                     ArtManager.getProfileName(null))) {
304                             compilerFilter = targetCompilerFilter;
305                         } else {
306                             Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath());
307                         }
308                     }
309                 } catch (InstallerException | RuntimeException e) {
310                     Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath(), e);
311                 }
312             }
313         } else {
314             compilerFilter = targetCompilerFilter;
315         }
316 
317         performDexoptPackage(sysUiPackageName, reason, compilerFilter);
318     }
319 
dexoptLauncher(int reason)320     private void dexoptLauncher(int reason) throws LegacyDexoptDisabledException {
321         Computer snapshot = mPm.snapshotComputer();
322         RoleManager roleManager = mPm.mContext.getSystemService(RoleManager.class);
323         for (var packageName : roleManager.getRoleHolders(RoleManager.ROLE_HOME)) {
324             AndroidPackage pkg = snapshot.getPackage(packageName);
325             if (pkg == null) {
326                 Log.w(TAG, "Launcher package " + packageName + " is not found for dexopting");
327             } else {
328                 performDexoptPackage(packageName, reason, "speed-profile");
329             }
330         }
331     }
332 
performDexoptPackage(@onNull String packageName, int reason, @NonNull String compilerFilter)333     private void performDexoptPackage(@NonNull String packageName, int reason,
334             @NonNull String compilerFilter) throws LegacyDexoptDisabledException {
335         Installer.checkLegacyDexoptDisabled();
336 
337         // DEXOPT_CHECK_FOR_PROFILES_UPDATES is set to replicate behaviour that will be
338         // unconditionally enabled for profile guided filters when ART Service is called instead of
339         // the legacy PackageDexOptimizer implementation.
340         int dexoptFlags = isProfileGuidedCompilerFilter(compilerFilter)
341                 ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
342                 : 0;
343 
344         performDexOptTraced(new DexoptOptions(
345                 packageName, reason, compilerFilter, null /* splitName */, dexoptFlags));
346     }
347 
348     /**
349      * Called during startup to do any boot time dexopting. This can occasionally be time consuming
350      * (30+ seconds) and the function will block until it is complete.
351      */
performPackageDexOptUpgradeIfNeeded()352     public void performPackageDexOptUpgradeIfNeeded() {
353         PackageManagerServiceUtils.enforceSystemOrRoot(
354                 "Only the system can request package update");
355 
356         int reason;
357         if (mPm.isFirstBoot()) {
358             reason = REASON_FIRST_BOOT; // First boot or factory reset.
359         } else if (mPm.isDeviceUpgrading()) {
360             reason = REASON_BOOT_AFTER_OTA;
361         } else if (hasBcpApexesChanged()) {
362             reason = REASON_BOOT_AFTER_MAINLINE_UPDATE;
363         } else {
364             return;
365         }
366 
367         Log.i(TAG,
368                 "Starting boot dexopt for reason "
369                         + DexoptOptions.convertToArtServiceDexoptReason(reason));
370 
371         final long startTime = System.nanoTime();
372 
373         if (useArtService()) {
374             mBootDexoptStartTime = startTime;
375             getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason),
376                     null /* progressCallbackExecutor */, null /* progressCallback */);
377         } else {
378             try {
379                 // System UI and the launcher are important to user experience, so we check them
380                 // after a mainline update or OTA. They may need to be re-compiled in these cases.
381                 checkAndDexOptSystemUi(reason);
382                 dexoptLauncher(reason);
383 
384                 if (reason != REASON_BOOT_AFTER_OTA && reason != REASON_FIRST_BOOT) {
385                     return;
386                 }
387 
388                 final Computer snapshot = mPm.snapshotComputer();
389 
390                 // TODO(b/251903639): Align this with how ART Service selects packages for boot
391                 // compilation.
392                 List<PackageStateInternal> pkgSettings =
393                         getPackagesForDexopt(snapshot.getPackageStates().values(), mPm);
394 
395                 final int[] stats =
396                         performDexOptUpgrade(pkgSettings, reason, false /* bootComplete */);
397                 reportBootDexopt(startTime, stats[0], stats[1], stats[2]);
398             } catch (LegacyDexoptDisabledException e) {
399                 throw new RuntimeException(e);
400             }
401         }
402     }
403 
reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed)404     private void reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed) {
405         final int elapsedTimeSeconds =
406                 (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
407 
408         final Computer newSnapshot = mPm.snapshotComputer();
409 
410         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", numDexopted);
411         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", numSkipped);
412         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", numFailed);
413         // TODO(b/251903639): getOptimizablePackages calls PackageDexOptimizer.canOptimizePackage
414         // which duplicates logic in ART Service (com.android.server.art.Utils.canDexoptPackage).
415         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_total",
416                 getOptimizablePackages(newSnapshot).size());
417         MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
418     }
419 
getOptimizablePackages(@onNull Computer snapshot)420     public List<String> getOptimizablePackages(@NonNull Computer snapshot) {
421         ArrayList<String> pkgs = new ArrayList<>();
422         mPm.forEachPackageState(snapshot, packageState -> {
423             final AndroidPackage pkg = packageState.getPkg();
424             if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) {
425                 pkgs.add(packageState.getPackageName());
426             }
427         });
428         return pkgs;
429     }
430 
performDexOpt(DexoptOptions options)431     /*package*/ boolean performDexOpt(DexoptOptions options) {
432         final Computer snapshot = mPm.snapshotComputer();
433         if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
434             return false;
435         } else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
436             return false;
437         }
438         var pkg = snapshot.getPackage(options.getPackageName());
439         if (pkg != null && pkg.isApex()) {
440             // skip APEX
441             return true;
442         }
443 
444         @DexOptResult int dexoptStatus;
445         if (options.isDexoptOnlySecondaryDex()) {
446             if (useArtService()) {
447                 dexoptStatus = performDexOptWithArtService(options, 0 /* extraFlags */);
448             } else {
449                 try {
450                     return mPm.getDexManager().dexoptSecondaryDex(options);
451                 } catch (LegacyDexoptDisabledException e) {
452                     throw new RuntimeException(e);
453                 }
454             }
455         } else {
456             dexoptStatus = performDexOptWithStatus(options);
457         }
458         return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
459     }
460 
461     /**
462      * Perform dexopt on the given package and return one of following result:
463      * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
464      * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
465      * {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
466      * {@link PackageDexOptimizer#DEX_OPT_FAILED}
467      */
468     @DexOptResult
performDexOptWithStatus(DexoptOptions options)469     /* package */ int performDexOptWithStatus(DexoptOptions options) {
470         return performDexOptTraced(options);
471     }
472 
473     @DexOptResult
performDexOptTraced(DexoptOptions options)474     private int performDexOptTraced(DexoptOptions options) {
475         Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt");
476         try {
477             return performDexOptInternal(options);
478         } finally {
479             Trace.traceEnd(TRACE_TAG_DALVIK);
480         }
481     }
482 
483     // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
484     // if the package can now be considered up to date for the given filter.
485     @DexOptResult
performDexOptInternal(DexoptOptions options)486     private int performDexOptInternal(DexoptOptions options) {
487         if (useArtService()) {
488             return performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);
489         }
490 
491         AndroidPackage p;
492         PackageSetting pkgSetting;
493         synchronized (mPm.mLock) {
494             p = mPm.mPackages.get(options.getPackageName());
495             pkgSetting = mPm.mSettings.getPackageLPr(options.getPackageName());
496             if (p == null || pkgSetting == null) {
497                 // Package could not be found. Report failure.
498                 return PackageDexOptimizer.DEX_OPT_FAILED;
499             }
500             if (p.isApex()) {
501                 // APEX needs no dexopt
502                 return PackageDexOptimizer.DEX_OPT_SKIPPED;
503             }
504             mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
505             mPm.mCompilerStats.maybeWriteAsync();
506         }
507         final long callingId = Binder.clearCallingIdentity();
508         try {
509             return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
510         } catch (LegacyDexoptDisabledException e) {
511             throw new RuntimeException(e);
512         } finally {
513             Binder.restoreCallingIdentity(callingId);
514         }
515     }
516 
517     /**
518      * Performs dexopt on the given package using ART Service. May only be called when ART Service
519      * is enabled, i.e. when {@link useArtService} returns true.
520      */
521     @DexOptResult
performDexOptWithArtService(DexoptOptions options, int extraFlags)522     private int performDexOptWithArtService(DexoptOptions options,
523             /*@DexoptFlags*/ int extraFlags) {
524         try (PackageManagerLocal.FilteredSnapshot snapshot =
525                         getPackageManagerLocal().withFilteredSnapshot()) {
526             PackageState ops = snapshot.getPackageState(options.getPackageName());
527             if (ops == null) {
528                 return PackageDexOptimizer.DEX_OPT_FAILED;
529             }
530             AndroidPackage oap = ops.getAndroidPackage();
531             if (oap == null) {
532                 return PackageDexOptimizer.DEX_OPT_FAILED;
533             }
534             DexoptParams params = options.convertToDexoptParams(extraFlags);
535             DexoptResult result =
536                     getArtManagerLocal().dexoptPackage(snapshot, options.getPackageName(), params);
537             return convertToDexOptResult(result);
538         }
539     }
540 
541     @DexOptResult
performDexOptInternalWithDependenciesLI( AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options)542     private int performDexOptInternalWithDependenciesLI(
543             AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options)
544             throws LegacyDexoptDisabledException {
545         if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
546             // This needs to be done in odrefresh in early boot, for security reasons.
547             throw new IllegalArgumentException("Cannot dexopt the system server");
548         }
549 
550         // Select the dex optimizer based on the force parameter.
551         // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
552         //       allocate an object here.
553         PackageDexOptimizer pdo = options.isForce()
554                 ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPm.mPackageDexOptimizer)
555                 : mPm.mPackageDexOptimizer;
556 
557         // Dexopt all dependencies first. Note: we ignore the return value and march on
558         // on errors.
559         // Note that we are going to call performDexOpt on those libraries as many times as
560         // they are referenced in packages. When we do a batch of performDexOpt (for example
561         // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
562         // and the first package that uses the library will dexopt it. The
563         // others will see that the compiled code for the library is up to date.
564         Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
565         final String[] instructionSets = getAppDexInstructionSets(
566                 pkgSetting.getPrimaryCpuAbi(),
567                 pkgSetting.getSecondaryCpuAbi());
568         if (!deps.isEmpty()) {
569             DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
570                     options.getCompilationReason(), options.getCompilerFilter(),
571                     options.getSplitName(),
572                     options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
573             for (SharedLibraryInfo info : deps) {
574                 Computer snapshot = mPm.snapshotComputer();
575                 AndroidPackage depPackage = snapshot.getPackage(info.getPackageName());
576                 PackageStateInternal depPackageStateInternal =
577                         snapshot.getPackageStateInternal(info.getPackageName());
578                 if (depPackage != null && depPackageStateInternal != null) {
579                     // TODO: Analyze and investigate if we (should) profile libraries.
580                     pdo.performDexOpt(depPackage, depPackageStateInternal, instructionSets,
581                             mPm.getOrCreateCompilerPackageStats(depPackage),
582                             mPm.getDexManager().getPackageUseInfoOrDefault(
583                                     depPackage.getPackageName()), libraryOptions);
584                 } else {
585                     // TODO(ngeoffray): Support dexopting system shared libraries.
586                 }
587             }
588         }
589 
590         return pdo.performDexOpt(p, pkgSetting, instructionSets,
591                 mPm.getOrCreateCompilerPackageStats(p),
592                 mPm.getDexManager().getPackageUseInfoOrDefault(p.getPackageName()), options);
593     }
594 
595     /** @deprecated For legacy shell command only. */
596     @Deprecated
forceDexOpt(@onNull Computer snapshot, String packageName)597     public void forceDexOpt(@NonNull Computer snapshot, String packageName)
598             throws LegacyDexoptDisabledException {
599         PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
600 
601         final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
602         final AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
603         if (packageState == null || pkg == null) {
604             throw new IllegalArgumentException("Unknown package: " + packageName);
605         }
606         if (pkg.isApex()) {
607             throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName);
608         }
609 
610         Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt");
611 
612         // Whoever is calling forceDexOpt wants a compiled package.
613         // Don't use profiles since that may cause compilation to be skipped.
614         DexoptOptions options = new DexoptOptions(packageName, REASON_CMDLINE,
615                 getDefaultCompilerFilter(), null /* splitName */,
616                 DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE);
617 
618         @DexOptResult int res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
619 
620         Trace.traceEnd(TRACE_TAG_DALVIK);
621         if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
622             throw new IllegalStateException("Failed to dexopt: " + res);
623         }
624     }
625 
performDexOptMode(@onNull Computer snapshot, String packageName, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)626     public boolean performDexOptMode(@NonNull Computer snapshot, String packageName,
627             String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) {
628         if (!PackageManagerServiceUtils.isSystemOrRootOrShell()
629                 && !isCallerInstallerForPackage(snapshot, packageName)) {
630             throw new SecurityException("performDexOptMode");
631         }
632 
633         int flags = (force ? DexoptOptions.DEXOPT_FORCE : 0)
634                 | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
635 
636         if (isProfileGuidedCompilerFilter(targetCompilerFilter)) {
637             // Set this flag whenever the filter is profile guided, to align with ART Service
638             // behavior.
639             flags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES;
640         }
641 
642         return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
643                 targetCompilerFilter, splitName, flags));
644     }
645 
isCallerInstallerForPackage(@onNull Computer snapshot, String packageName)646     private boolean isCallerInstallerForPackage(@NonNull Computer snapshot, String packageName) {
647         final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
648         if (packageState == null) {
649             return false;
650         }
651         final InstallSource installSource = packageState.getInstallSource();
652 
653         final PackageStateInternal installerPackageState =
654                 snapshot.getPackageStateInternal(installSource.mInstallerPackageName);
655         if (installerPackageState == null) {
656             return false;
657         }
658         final AndroidPackage installerPkg = installerPackageState.getPkg();
659         return installerPkg.getUid() == Binder.getCallingUid();
660     }
661 
performDexOptSecondary( String packageName, String compilerFilter, boolean force)662     public boolean performDexOptSecondary(
663             String packageName, String compilerFilter, boolean force) {
664         int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
665                 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
666                 | DexoptOptions.DEXOPT_BOOT_COMPLETE
667                 | (force ? DexoptOptions.DEXOPT_FORCE : 0);
668         return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
669                 compilerFilter, null /* splitName */, flags));
670     }
671 
672     // Sort apps by importance for dexopt ordering. Important apps are given
673     // more priority in case the device runs out of space.
getPackagesForDexopt( Collection<? extends PackageStateInternal> packages, PackageManagerService packageManagerService)674     public static List<PackageStateInternal> getPackagesForDexopt(
675             Collection<? extends PackageStateInternal> packages,
676             PackageManagerService packageManagerService) {
677         return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
678     }
679 
getPackagesForDexopt( Collection<? extends PackageStateInternal> pkgSettings, PackageManagerService packageManagerService, boolean debug)680     public static List<PackageStateInternal> getPackagesForDexopt(
681             Collection<? extends PackageStateInternal> pkgSettings,
682             PackageManagerService packageManagerService,
683             boolean debug) {
684         List<PackageStateInternal> result = new ArrayList<>();
685         ArrayList<PackageStateInternal> remainingPkgSettings = new ArrayList<>(pkgSettings);
686 
687         // First, remove all settings without available packages
688         remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
689         remainingPkgSettings.removeIf(REMOVE_IF_APEX_PKG);
690 
691         ArrayList<PackageStateInternal> sortTemp = new ArrayList<>(remainingPkgSettings.size());
692 
693         final Computer snapshot = packageManagerService.snapshotComputer();
694 
695         // Give priority to core apps.
696         applyPackageFilter(snapshot, pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
697                 remainingPkgSettings, sortTemp, packageManagerService);
698 
699         // Give priority to system apps that listen for pre boot complete.
700         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
701         final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
702         applyPackageFilter(snapshot, pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
703                 remainingPkgSettings, sortTemp, packageManagerService);
704 
705         // Give priority to apps used by other apps.
706         DexManager dexManager = packageManagerService.getDexManager();
707         applyPackageFilter(snapshot, pkgSetting ->
708                         dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
709                                 .isAnyCodePathUsedByOtherApps(),
710                 result, remainingPkgSettings, sortTemp, packageManagerService);
711 
712         // Filter out packages that aren't recently used, add all remaining apps.
713         // TODO: add a property to control this?
714         Predicate<PackageStateInternal> remainingPredicate;
715         if (!remainingPkgSettings.isEmpty()
716                 && packageManagerService.isHistoricalPackageUsageAvailable()) {
717             if (debug) {
718                 Log.i(TAG, "Looking at historical package use");
719             }
720             // Get the package that was used last.
721             PackageStateInternal lastUsed = Collections.max(remainingPkgSettings,
722                     Comparator.comparingLong(
723                             pkgSetting -> pkgSetting.getTransientState()
724                                     .getLatestForegroundPackageUseTimeInMills()));
725             if (debug) {
726                 Log.i(TAG, "Taking package " + lastUsed.getPackageName()
727                         + " as reference in time use");
728             }
729             long estimatedPreviousSystemUseTime = lastUsed.getTransientState()
730                     .getLatestForegroundPackageUseTimeInMills();
731             // Be defensive if for some reason package usage has bogus data.
732             if (estimatedPreviousSystemUseTime != 0) {
733                 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
734                 remainingPredicate = pkgSetting -> pkgSetting.getTransientState()
735                         .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
736             } else {
737                 // No meaningful historical info. Take all.
738                 remainingPredicate = pkgSetting -> true;
739             }
740             sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
741         } else {
742             // No historical info. Take all.
743             remainingPredicate = pkgSetting -> true;
744         }
745         applyPackageFilter(snapshot, remainingPredicate, result, remainingPkgSettings, sortTemp,
746                 packageManagerService);
747 
748         // Make sure the system server isn't in the result, because it can never be dexopted here.
749         result.removeIf(pkgSetting -> PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPackageName()));
750 
751         if (debug) {
752             Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
753             Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
754         }
755 
756         return result;
757     }
758 
759     // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
760     // package will be removed from {@code packages} and added to {@code result} with its
761     // dependencies. If usage data is available, the positive packages will be sorted by usage
762     // data (with {@code sortTemp} as temporary storage).
applyPackageFilter(@onNull Computer snapshot, Predicate<PackageStateInternal> filter, Collection<PackageStateInternal> result, Collection<PackageStateInternal> packages, @NonNull List<PackageStateInternal> sortTemp, PackageManagerService packageManagerService)763     private static void applyPackageFilter(@NonNull Computer snapshot,
764             Predicate<PackageStateInternal> filter,
765             Collection<PackageStateInternal> result,
766             Collection<PackageStateInternal> packages,
767             @NonNull List<PackageStateInternal> sortTemp,
768             PackageManagerService packageManagerService) {
769         for (PackageStateInternal pkgSetting : packages) {
770             if (filter.test(pkgSetting)) {
771                 sortTemp.add(pkgSetting);
772             }
773         }
774 
775         sortPackagesByUsageDate(sortTemp, packageManagerService);
776         packages.removeAll(sortTemp);
777 
778         for (PackageStateInternal pkgSetting : sortTemp) {
779             result.add(pkgSetting);
780 
781             List<PackageStateInternal> deps = snapshot.findSharedNonSystemLibraries(pkgSetting);
782             if (!deps.isEmpty()) {
783                 deps.removeAll(result);
784                 result.addAll(deps);
785                 packages.removeAll(deps);
786             }
787         }
788 
789         sortTemp.clear();
790     }
791 
792     // Sort a list of apps by their last usage, most recently used apps first. The order of
793     // packages without usage data is undefined (but they will be sorted after the packages
794     // that do have usage data).
sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings, PackageManagerService packageManagerService)795     private static void sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings,
796             PackageManagerService packageManagerService) {
797         if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
798             return;
799         }
800 
801         Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
802                 Long.compare(
803                         pkgSetting2.getTransientState().getLatestForegroundPackageUseTimeInMills(),
804                         pkgSetting1.getTransientState().getLatestForegroundPackageUseTimeInMills())
805         );
806     }
807 
getPackageNamesForIntent(Intent intent, int userId)808     private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
809         List<ResolveInfo> ris = null;
810         try {
811             ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
812                     .getList();
813         } catch (RemoteException e) {
814         }
815         ArraySet<String> pkgNames = new ArraySet<String>();
816         if (ris != null) {
817             for (ResolveInfo ri : ris) {
818                 pkgNames.add(ri.activityInfo.packageName);
819             }
820         }
821         return pkgNames;
822     }
823 
packagesToString(List<PackageStateInternal> pkgSettings)824     public static String packagesToString(List<PackageStateInternal> pkgSettings) {
825         StringBuilder sb = new StringBuilder();
826         for (int index = 0; index < pkgSettings.size(); index++) {
827             if (sb.length() > 0) {
828                 sb.append(", ");
829             }
830             sb.append(pkgSettings.get(index).getPackageName());
831         }
832         return sb.toString();
833     }
834 
835      /**
836      * Requests that files preopted on a secondary system partition be copied to the data partition
837      * if possible.  Note that the actual copying of the files is accomplished by init for security
838      * reasons. This simply requests that the copy takes place and awaits confirmation of its
839      * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
840      */
requestCopyPreoptedFiles()841     public static void requestCopyPreoptedFiles() {
842         final int WAIT_TIME_MS = 100;
843         final String CP_PREOPT_PROPERTY = "sys.cppreopt";
844         if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
845             SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
846             // We will wait for up to 100 seconds.
847             final long timeStart = SystemClock.uptimeMillis();
848             final long timeEnd = timeStart + 100 * 1000;
849             long timeNow = timeStart;
850             while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
851                 try {
852                     Thread.sleep(WAIT_TIME_MS);
853                 } catch (InterruptedException e) {
854                     // Do nothing
855                 }
856                 timeNow = SystemClock.uptimeMillis();
857                 if (timeNow > timeEnd) {
858                     SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
859                     Slog.wtf(TAG, "cppreopt did not finish!");
860                     break;
861                 }
862             }
863 
864             Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
865         }
866     }
867 
controlDexOptBlocking(boolean block)868     /*package*/ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
869         mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
870     }
871 
872     /**
873      * Dumps the dexopt state for the given package, or all packages if it is null.
874      */
dumpDexoptState( @onNull IndentingPrintWriter ipw, @Nullable String packageName)875     public static void dumpDexoptState(
876             @NonNull IndentingPrintWriter ipw, @Nullable String packageName) {
877         try (PackageManagerLocal.FilteredSnapshot snapshot =
878                         getPackageManagerLocal().withFilteredSnapshot()) {
879             if (packageName != null) {
880                 try {
881                     DexOptHelper.getArtManagerLocal().dumpPackage(ipw, snapshot, packageName);
882                 } catch (IllegalArgumentException e) {
883                     // Package isn't found, but that should only happen due to race.
884                     ipw.println(e);
885                 }
886             } else {
887                 DexOptHelper.getArtManagerLocal().dump(ipw, snapshot);
888             }
889         }
890     }
891 
892     /**
893      * Returns the module names of the APEXes that contribute to bootclasspath.
894      */
getBcpApexes()895     private static List<String> getBcpApexes() {
896         String bcp = System.getenv("BOOTCLASSPATH");
897         if (TextUtils.isEmpty(bcp)) {
898             Log.e(TAG, "Unable to get BOOTCLASSPATH");
899             return List.of();
900         }
901 
902         ArrayList<String> bcpApexes = new ArrayList<>();
903         for (String pathStr : bcp.split(":")) {
904             Path path = Paths.get(pathStr);
905             // Check if the path is in the format of `/apex/<apex-module-name>/...` and extract the
906             // apex module name from the path.
907             if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) {
908                 bcpApexes.add(path.getName(1).toString());
909             }
910         }
911 
912         return bcpApexes;
913     }
914 
915     /**
916      * Returns true of any of the APEXes that contribute to bootclasspath has changed during this
917      * boot.
918      */
hasBcpApexesChanged()919     private static boolean hasBcpApexesChanged() {
920         Set<String> bcpApexes = new HashSet<>(getBcpApexes());
921         ApexManager apexManager = ApexManager.getInstance();
922         for (ActiveApexInfo apexInfo : apexManager.getActiveApexInfos()) {
923             if (bcpApexes.contains(apexInfo.apexModuleName) && apexInfo.activeApexChanged) {
924                 return true;
925             }
926         }
927         return false;
928     }
929 
930     /**
931      * Returns true if ART Service should be used for package optimization.
932      */
useArtService()933     public static boolean useArtService() {
934         return SystemProperties.getBoolean("dalvik.vm.useartservice", false);
935     }
936 
937     /**
938      * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization.
939      */
getDexUseManagerLocal()940     public static @Nullable DexUseManagerLocal getDexUseManagerLocal() {
941         if (!useArtService()) {
942             return null;
943         }
944         try {
945             return LocalManagerRegistry.getManagerOrThrow(DexUseManagerLocal.class);
946         } catch (ManagerNotFoundException e) {
947             throw new RuntimeException(e);
948         }
949     }
950 
951     private class DexoptDoneHandler implements ArtManagerLocal.DexoptDoneCallback {
952         /**
953          * Called after every package dexopt operation done by {@link ArtManagerLocal} (when ART
954          * Service is in use).
955          */
956         @Override
onDexoptDone(@onNull DexoptResult result)957         public void onDexoptDone(@NonNull DexoptResult result) {
958             switch (result.getReason()) {
959                 case ReasonMapping.REASON_FIRST_BOOT:
960                 case ReasonMapping.REASON_BOOT_AFTER_OTA:
961                 case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
962                     int numDexopted = 0;
963                     int numSkipped = 0;
964                     int numFailed = 0;
965                     for (DexoptResult.PackageDexoptResult pkgRes :
966                             result.getPackageDexoptResults()) {
967                         switch (pkgRes.getStatus()) {
968                             case DexoptResult.DEXOPT_PERFORMED:
969                                 numDexopted += 1;
970                                 break;
971                             case DexoptResult.DEXOPT_SKIPPED:
972                                 numSkipped += 1;
973                                 break;
974                             case DexoptResult.DEXOPT_FAILED:
975                                 numFailed += 1;
976                                 break;
977                         }
978                     }
979 
980                     reportBootDexopt(mBootDexoptStartTime, numDexopted, numSkipped, numFailed);
981                     break;
982             }
983 
984             for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
985                 CompilerStats.PackageStats stats =
986                         mPm.getOrCreateCompilerPackageStats(pkgRes.getPackageName());
987                 for (DexoptResult.DexContainerFileDexoptResult dexRes :
988                         pkgRes.getDexContainerFileDexoptResults()) {
989                     stats.setCompileTime(
990                             dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis());
991                 }
992             }
993 
994             synchronized (mPm.mLock) {
995                 mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
996                 mPm.mCompilerStats.maybeWriteAsync();
997             }
998 
999             if (result.getReason().equals(ReasonMapping.REASON_INACTIVE)) {
1000                 for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
1001                     if (pkgRes.getStatus() == DexoptResult.DEXOPT_PERFORMED) {
1002                         long pkgSizeBytes = 0;
1003                         long pkgSizeBeforeBytes = 0;
1004                         for (DexoptResult.DexContainerFileDexoptResult dexRes :
1005                                 pkgRes.getDexContainerFileDexoptResults()) {
1006                             long dexContainerSize = new File(dexRes.getDexContainerFile()).length();
1007                             pkgSizeBytes += dexRes.getSizeBytes() + dexContainerSize;
1008                             pkgSizeBeforeBytes += dexRes.getSizeBeforeBytes() + dexContainerSize;
1009                         }
1010                         FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED,
1011                                 pkgRes.getPackageName(), pkgSizeBeforeBytes, pkgSizeBytes,
1012                                 false /* aggressive */);
1013                     }
1014                 }
1015             }
1016 
1017             var updatedPackages = new ArraySet<String>();
1018             for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
1019                 if (pkgRes.hasUpdatedArtifacts()) {
1020                     updatedPackages.add(pkgRes.getPackageName());
1021                 }
1022             }
1023             if (!updatedPackages.isEmpty()) {
1024                 LocalServices.getService(PinnerService.class)
1025                         .update(updatedPackages, false /* force */);
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Initializes {@link ArtManagerLocal} before {@link getArtManagerLocal} is called.
1032      */
initializeArtManagerLocal( @onNull Context systemContext, @NonNull PackageManagerService pm)1033     public static void initializeArtManagerLocal(
1034             @NonNull Context systemContext, @NonNull PackageManagerService pm) {
1035         if (!useArtService()) {
1036             return;
1037         }
1038 
1039         ArtManagerLocal artManager = new ArtManagerLocal(systemContext);
1040         artManager.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run,
1041                 pm.getDexOptHelper().new DexoptDoneHandler());
1042         LocalManagerRegistry.addManager(ArtManagerLocal.class, artManager);
1043         sArtManagerLocalIsInitialized = true;
1044 
1045         // Schedule the background job when boot is complete. This decouples us from when
1046         // JobSchedulerService is initialized.
1047         systemContext.registerReceiver(new BroadcastReceiver() {
1048             @Override
1049             public void onReceive(Context context, Intent intent) {
1050                 context.unregisterReceiver(this);
1051                 artManager.scheduleBackgroundDexoptJob();
1052             }
1053         }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
1054     }
1055 
1056     /**
1057      * Returns true if an {@link ArtManagerLocal} instance has been created.
1058      *
1059      * Avoid this function if at all possible, because it may hide initialization order problems.
1060      */
artManagerLocalIsInitialized()1061     public static boolean artManagerLocalIsInitialized() {
1062         return sArtManagerLocalIsInitialized;
1063     }
1064 
1065     /**
1066      * Returns the registered {@link ArtManagerLocal} instance, or else throws an unchecked error.
1067      */
getArtManagerLocal()1068     public static @NonNull ArtManagerLocal getArtManagerLocal() {
1069         try {
1070             return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class);
1071         } catch (ManagerNotFoundException e) {
1072             throw new RuntimeException(e);
1073         }
1074     }
1075 
1076     /**
1077      * Converts an ART Service {@link DexoptResult} to {@link DexOptResult}.
1078      *
1079      * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager.
1080      */
1081     @DexOptResult
convertToDexOptResult(DexoptResult result)1082     private static int convertToDexOptResult(DexoptResult result) {
1083         /*@DexoptResultStatus*/ int status = result.getFinalStatus();
1084         switch (status) {
1085             case DexoptResult.DEXOPT_SKIPPED:
1086                 return PackageDexOptimizer.DEX_OPT_SKIPPED;
1087             case DexoptResult.DEXOPT_FAILED:
1088                 return PackageDexOptimizer.DEX_OPT_FAILED;
1089             case DexoptResult.DEXOPT_PERFORMED:
1090                 return PackageDexOptimizer.DEX_OPT_PERFORMED;
1091             case DexoptResult.DEXOPT_CANCELLED:
1092                 return PackageDexOptimizer.DEX_OPT_CANCELLED;
1093             default:
1094                 throw new IllegalArgumentException("DexoptResult for "
1095                         + result.getPackageDexoptResults().get(0).getPackageName()
1096                         + " has unsupported status " + status);
1097         }
1098     }
1099 }
1100