• 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.pm;
18 
19 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
20 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
21 import static android.os.Trace.TRACE_TAG_DALVIK;
22 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
23 import static android.os.incremental.IncrementalManager.isIncrementalPath;
24 
25 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
26 import static com.android.server.pm.ApexManager.ActiveApexInfo;
27 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
28 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
29 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE;
30 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_OTA;
31 import static com.android.server.pm.PackageManagerService.REASON_CMDLINE;
32 import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT;
33 import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX;
34 import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
35 import static com.android.server.pm.PackageManagerService.TAG;
36 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_APEX_PKG;
37 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG;
38 import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
39 
40 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
41 
42 import android.annotation.NonNull;
43 import android.annotation.Nullable;
44 import android.app.AppGlobals;
45 import android.content.BroadcastReceiver;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.content.pm.ApexStagedEvent;
50 import android.content.pm.Flags;
51 import android.content.pm.IPackageManagerNative;
52 import android.content.pm.IStagedApexObserver;
53 import android.content.pm.PackageManager;
54 import android.content.pm.ResolveInfo;
55 import android.os.Binder;
56 import android.os.RemoteException;
57 import android.os.ServiceManager;
58 import android.os.SystemClock;
59 import android.os.SystemProperties;
60 import android.os.Trace;
61 import android.os.UserHandle;
62 import android.provider.Settings.Global;
63 import android.text.TextUtils;
64 import android.util.ArraySet;
65 import android.util.Log;
66 import android.util.Slog;
67 
68 import com.android.internal.logging.MetricsLogger;
69 import com.android.internal.util.FrameworkStatsLog;
70 import com.android.internal.util.IndentingPrintWriter;
71 import com.android.server.LocalManagerRegistry;
72 import com.android.server.LocalServices;
73 import com.android.server.art.ArtManagerLocal;
74 import com.android.server.art.DexUseManagerLocal;
75 import com.android.server.art.ReasonMapping;
76 import com.android.server.art.model.ArtFlags;
77 import com.android.server.art.model.DexoptParams;
78 import com.android.server.art.model.DexoptResult;
79 import com.android.server.pinner.PinnerService;
80 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
81 import com.android.server.pm.dex.DexManager;
82 import com.android.server.pm.dex.DexoptOptions;
83 import com.android.server.pm.local.PackageManagerLocalImpl;
84 import com.android.server.pm.pkg.AndroidPackage;
85 import com.android.server.pm.pkg.PackageState;
86 import com.android.server.pm.pkg.PackageStateInternal;
87 
88 import java.io.File;
89 import java.nio.file.Path;
90 import java.nio.file.Paths;
91 import java.util.ArrayList;
92 import java.util.Arrays;
93 import java.util.Collection;
94 import java.util.Collections;
95 import java.util.Comparator;
96 import java.util.HashSet;
97 import java.util.List;
98 import java.util.Set;
99 import java.util.concurrent.CompletableFuture;
100 import java.util.concurrent.LinkedBlockingQueue;
101 import java.util.concurrent.ThreadPoolExecutor;
102 import java.util.concurrent.TimeUnit;
103 import java.util.function.Predicate;
104 
105 /**
106  * Helper class for dex optimization operations in PackageManagerService.
107  */
108 public final class DexOptHelper {
109     private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
110 
111     @NonNull
112     private static final ThreadPoolExecutor sDexoptExecutor =
113             new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
114                     60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
115 
116     private static boolean sArtManagerLocalIsInitialized = false;
117 
118     private final PackageManagerService mPm;
119 
120     // Start time for the boot dexopt in performPackageDexOptUpgradeIfNeeded when ART Service is
121     // used, to make it available to the onDexoptDone callback.
122     private volatile long mBootDexoptStartTime;
123 
124     static {
125         // Recycle the thread if it's not used for `keepAliveTime`.
sDexoptExecutor.allowsCoreThreadTimeOut()126         sDexoptExecutor.allowsCoreThreadTimeOut();
127     }
128 
DexOptHelper(PackageManagerService pm)129     DexOptHelper(PackageManagerService pm) {
130         mPm = pm;
131     }
132 
133     /*
134      * Return the prebuilt profile path given a package base code path.
135      */
getPrebuildProfilePath(AndroidPackage pkg)136     private static String getPrebuildProfilePath(AndroidPackage pkg) {
137         return pkg.getBaseApkPath() + ".prof";
138     }
139 
140     /**
141      * Called during startup to do any boot time dexopting. This can occasionally be time consuming
142      * (30+ seconds) and the function will block until it is complete.
143      */
performPackageDexOptUpgradeIfNeeded()144     public void performPackageDexOptUpgradeIfNeeded() {
145         PackageManagerServiceUtils.enforceSystemOrRoot(
146                 "Only the system can request package update");
147 
148         int reason;
149         if (mPm.isFirstBoot()) {
150             reason = REASON_FIRST_BOOT; // First boot or factory reset.
151         } else if (mPm.isDeviceUpgrading()) {
152             reason = REASON_BOOT_AFTER_OTA;
153         } else if (hasBcpApexesChanged()) {
154             reason = REASON_BOOT_AFTER_MAINLINE_UPDATE;
155         } else {
156             return;
157         }
158 
159         Log.i(TAG,
160                 "Starting boot dexopt for reason "
161                         + DexoptOptions.convertToArtServiceDexoptReason(reason));
162 
163         final long startTime = System.nanoTime();
164 
165         mBootDexoptStartTime = startTime;
166         getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason),
167                 null /* progressCallbackExecutor */, null /* progressCallback */);
168     }
169 
reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed)170     private void reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed) {
171         final int elapsedTimeSeconds =
172                 (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
173 
174         final Computer newSnapshot = mPm.snapshotComputer();
175 
176         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", numDexopted);
177         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", numSkipped);
178         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", numFailed);
179         // TODO(b/251903639): getOptimizablePackages calls PackageDexOptimizer.canOptimizePackage
180         // which duplicates logic in ART Service (com.android.server.art.Utils.canDexoptPackage).
181         MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_total",
182                 getOptimizablePackages(newSnapshot).size());
183         MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
184     }
185 
getOptimizablePackages(@onNull Computer snapshot)186     public List<String> getOptimizablePackages(@NonNull Computer snapshot) {
187         ArrayList<String> pkgs = new ArrayList<>();
188         mPm.forEachPackageState(snapshot, packageState -> {
189             final AndroidPackage pkg = packageState.getPkg();
190             if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) {
191                 pkgs.add(packageState.getPackageName());
192             }
193         });
194         return pkgs;
195     }
196 
performDexOpt(DexoptOptions options)197     /*package*/ boolean performDexOpt(DexoptOptions options) {
198         final Computer snapshot = mPm.snapshotComputer();
199         if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
200             return false;
201         } else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
202             return false;
203         }
204         var pkg = snapshot.getPackage(options.getPackageName());
205         if (pkg != null && pkg.isApex()) {
206             // skip APEX
207             return true;
208         }
209 
210         @DexOptResult int dexoptStatus;
211         if (options.isDexoptOnlySecondaryDex()) {
212             dexoptStatus = performDexOptWithArtService(options, 0 /* extraFlags */);
213         } else {
214             dexoptStatus = performDexOptWithStatus(options);
215         }
216         return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
217     }
218 
219     /**
220      * Perform dexopt on the given package and return one of following result:
221      * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
222      * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
223      * {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
224      * {@link PackageDexOptimizer#DEX_OPT_FAILED}
225      */
226     @DexOptResult
performDexOptWithStatus(DexoptOptions options)227     /* package */ int performDexOptWithStatus(DexoptOptions options) {
228         return performDexOptTraced(options);
229     }
230 
231     @DexOptResult
performDexOptTraced(DexoptOptions options)232     private int performDexOptTraced(DexoptOptions options) {
233         Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt");
234         try {
235             return performDexOptInternal(options);
236         } finally {
237             Trace.traceEnd(TRACE_TAG_DALVIK);
238         }
239     }
240 
241     // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
242     // if the package can now be considered up to date for the given filter.
243     @DexOptResult
performDexOptInternal(DexoptOptions options)244     private int performDexOptInternal(DexoptOptions options) {
245         return performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);
246     }
247 
248     /**
249      * Performs dexopt on the given package using ART Service.
250      */
251     @DexOptResult
performDexOptWithArtService(DexoptOptions options, int extraFlags)252     private int performDexOptWithArtService(DexoptOptions options,
253             /*@DexoptFlags*/ int extraFlags) {
254         try (PackageManagerLocal.FilteredSnapshot snapshot =
255                         getPackageManagerLocal().withFilteredSnapshot()) {
256             PackageState ops = snapshot.getPackageState(options.getPackageName());
257             if (ops == null) {
258                 return PackageDexOptimizer.DEX_OPT_FAILED;
259             }
260             AndroidPackage oap = ops.getAndroidPackage();
261             if (oap == null) {
262                 return PackageDexOptimizer.DEX_OPT_FAILED;
263             }
264             DexoptParams params = options.convertToDexoptParams(extraFlags);
265             DexoptResult result =
266                     getArtManagerLocal().dexoptPackage(snapshot, options.getPackageName(), params);
267             return convertToDexOptResult(result);
268         }
269     }
270 
performDexOptMode(@onNull Computer snapshot, String packageName, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)271     public boolean performDexOptMode(@NonNull Computer snapshot, String packageName,
272             String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) {
273         if (!PackageManagerServiceUtils.isSystemOrRootOrShell()
274                 && !isCallerInstallerForPackage(snapshot, packageName)) {
275             throw new SecurityException("performDexOptMode");
276         }
277 
278         int flags = (force ? DexoptOptions.DEXOPT_FORCE : 0)
279                 | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
280 
281         if (isProfileGuidedCompilerFilter(targetCompilerFilter)) {
282             // Set this flag whenever the filter is profile guided, to align with ART Service
283             // behavior.
284             flags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES;
285         }
286 
287         return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
288                 targetCompilerFilter, splitName, flags));
289     }
290 
isCallerInstallerForPackage(@onNull Computer snapshot, String packageName)291     private boolean isCallerInstallerForPackage(@NonNull Computer snapshot, String packageName) {
292         final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
293         if (packageState == null) {
294             return false;
295         }
296         final InstallSource installSource = packageState.getInstallSource();
297 
298         final PackageStateInternal installerPackageState =
299                 snapshot.getPackageStateInternal(installSource.mInstallerPackageName);
300         if (installerPackageState == null) {
301             return false;
302         }
303         final AndroidPackage installerPkg = installerPackageState.getPkg();
304         return installerPkg.getUid() == Binder.getCallingUid();
305     }
306 
performDexOptSecondary( String packageName, String compilerFilter, boolean force)307     public boolean performDexOptSecondary(
308             String packageName, String compilerFilter, boolean force) {
309         int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
310                 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
311                 | DexoptOptions.DEXOPT_BOOT_COMPLETE
312                 | (force ? DexoptOptions.DEXOPT_FORCE : 0);
313         return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
314                 compilerFilter, null /* splitName */, flags));
315     }
316 
317     // Sort apps by importance for dexopt ordering. Important apps are given
318     // more priority in case the device runs out of space.
getPackagesForDexopt( Collection<? extends PackageStateInternal> packages, PackageManagerService packageManagerService)319     public static List<PackageStateInternal> getPackagesForDexopt(
320             Collection<? extends PackageStateInternal> packages,
321             PackageManagerService packageManagerService) {
322         return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
323     }
324 
getPackagesForDexopt( Collection<? extends PackageStateInternal> pkgSettings, PackageManagerService packageManagerService, boolean debug)325     public static List<PackageStateInternal> getPackagesForDexopt(
326             Collection<? extends PackageStateInternal> pkgSettings,
327             PackageManagerService packageManagerService,
328             boolean debug) {
329         List<PackageStateInternal> result = new ArrayList<>();
330         ArrayList<PackageStateInternal> remainingPkgSettings = new ArrayList<>(pkgSettings);
331 
332         // First, remove all settings without available packages
333         remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
334         remainingPkgSettings.removeIf(REMOVE_IF_APEX_PKG);
335 
336         ArrayList<PackageStateInternal> sortTemp = new ArrayList<>(remainingPkgSettings.size());
337 
338         final Computer snapshot = packageManagerService.snapshotComputer();
339 
340         // Give priority to core apps.
341         applyPackageFilter(snapshot, pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
342                 remainingPkgSettings, sortTemp, packageManagerService);
343 
344         // Give priority to system apps that listen for pre boot complete.
345         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
346         final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
347         applyPackageFilter(snapshot, pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
348                 remainingPkgSettings, sortTemp, packageManagerService);
349 
350         // Give priority to apps used by other apps.
351         DexManager dexManager = packageManagerService.getDexManager();
352         applyPackageFilter(snapshot, pkgSetting ->
353                         dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
354                                 .isAnyCodePathUsedByOtherApps(),
355                 result, remainingPkgSettings, sortTemp, packageManagerService);
356 
357         // Filter out packages that aren't recently used, add all remaining apps.
358         // TODO: add a property to control this?
359         Predicate<PackageStateInternal> remainingPredicate;
360         if (!remainingPkgSettings.isEmpty()
361                 && packageManagerService.isHistoricalPackageUsageAvailable()) {
362             if (debug) {
363                 Log.i(TAG, "Looking at historical package use");
364             }
365             // Get the package that was used last.
366             PackageStateInternal lastUsed = Collections.max(remainingPkgSettings,
367                     Comparator.comparingLong(
368                             pkgSetting -> pkgSetting.getTransientState()
369                                     .getLatestForegroundPackageUseTimeInMills()));
370             if (debug) {
371                 Log.i(TAG, "Taking package " + lastUsed.getPackageName()
372                         + " as reference in time use");
373             }
374             long estimatedPreviousSystemUseTime = lastUsed.getTransientState()
375                     .getLatestForegroundPackageUseTimeInMills();
376             // Be defensive if for some reason package usage has bogus data.
377             if (estimatedPreviousSystemUseTime != 0) {
378                 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
379                 remainingPredicate = pkgSetting -> pkgSetting.getTransientState()
380                         .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
381             } else {
382                 // No meaningful historical info. Take all.
383                 remainingPredicate = pkgSetting -> true;
384             }
385             sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
386         } else {
387             // No historical info. Take all.
388             remainingPredicate = pkgSetting -> true;
389         }
390         applyPackageFilter(snapshot, remainingPredicate, result, remainingPkgSettings, sortTemp,
391                 packageManagerService);
392 
393         // Make sure the system server isn't in the result, because it can never be dexopted here.
394         result.removeIf(pkgSetting -> PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPackageName()));
395 
396         if (debug) {
397             Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
398             Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
399         }
400 
401         return result;
402     }
403 
404     // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
405     // package will be removed from {@code packages} and added to {@code result} with its
406     // dependencies. If usage data is available, the positive packages will be sorted by usage
407     // 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)408     private static void applyPackageFilter(@NonNull Computer snapshot,
409             Predicate<PackageStateInternal> filter,
410             Collection<PackageStateInternal> result,
411             Collection<PackageStateInternal> packages,
412             @NonNull List<PackageStateInternal> sortTemp,
413             PackageManagerService packageManagerService) {
414         for (PackageStateInternal pkgSetting : packages) {
415             if (filter.test(pkgSetting)) {
416                 sortTemp.add(pkgSetting);
417             }
418         }
419 
420         sortPackagesByUsageDate(sortTemp, packageManagerService);
421         packages.removeAll(sortTemp);
422 
423         for (PackageStateInternal pkgSetting : sortTemp) {
424             result.add(pkgSetting);
425 
426             List<PackageStateInternal> deps = snapshot.findSharedNonSystemLibraries(pkgSetting);
427             if (!deps.isEmpty()) {
428                 deps.removeAll(result);
429                 result.addAll(deps);
430                 packages.removeAll(deps);
431             }
432         }
433 
434         sortTemp.clear();
435     }
436 
437     // Sort a list of apps by their last usage, most recently used apps first. The order of
438     // packages without usage data is undefined (but they will be sorted after the packages
439     // that do have usage data).
sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings, PackageManagerService packageManagerService)440     private static void sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings,
441             PackageManagerService packageManagerService) {
442         if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
443             return;
444         }
445 
446         Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
447                 Long.compare(
448                         pkgSetting2.getTransientState().getLatestForegroundPackageUseTimeInMills(),
449                         pkgSetting1.getTransientState().getLatestForegroundPackageUseTimeInMills())
450         );
451     }
452 
getPackageNamesForIntent(Intent intent, int userId)453     private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
454         List<ResolveInfo> ris = null;
455         try {
456             ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
457                     .getList();
458         } catch (RemoteException e) {
459         }
460         ArraySet<String> pkgNames = new ArraySet<String>();
461         if (ris != null) {
462             for (ResolveInfo ri : ris) {
463                 pkgNames.add(ri.activityInfo.packageName);
464             }
465         }
466         return pkgNames;
467     }
468 
packagesToString(List<PackageStateInternal> pkgSettings)469     public static String packagesToString(List<PackageStateInternal> pkgSettings) {
470         StringBuilder sb = new StringBuilder();
471         for (int index = 0; index < pkgSettings.size(); index++) {
472             if (sb.length() > 0) {
473                 sb.append(", ");
474             }
475             sb.append(pkgSettings.get(index).getPackageName());
476         }
477         return sb.toString();
478     }
479 
480      /**
481      * Requests that files preopted on a secondary system partition be copied to the data partition
482      * if possible.  Note that the actual copying of the files is accomplished by init for security
483      * reasons. This simply requests that the copy takes place and awaits confirmation of its
484      * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
485      */
requestCopyPreoptedFiles()486     public static void requestCopyPreoptedFiles() {
487         final int WAIT_TIME_MS = 100;
488         final String CP_PREOPT_PROPERTY = "sys.cppreopt";
489         if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
490             SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
491             // We will wait for up to 100 seconds.
492             final long timeStart = SystemClock.uptimeMillis();
493             final long timeEnd = timeStart + 100 * 1000;
494             long timeNow = timeStart;
495             while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
496                 try {
497                     Thread.sleep(WAIT_TIME_MS);
498                 } catch (InterruptedException e) {
499                     // Do nothing
500                 }
501                 timeNow = SystemClock.uptimeMillis();
502                 if (timeNow > timeEnd) {
503                     SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
504                     Slog.wtf(TAG, "cppreopt did not finish!");
505                     break;
506                 }
507             }
508 
509             Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
510         }
511     }
512 
513     /**
514      * Dumps the dexopt state for the given package, or all packages if it is null.
515      */
dumpDexoptState( @onNull IndentingPrintWriter ipw, @Nullable String packageName)516     public static void dumpDexoptState(
517             @NonNull IndentingPrintWriter ipw, @Nullable String packageName) {
518         try (PackageManagerLocal.FilteredSnapshot snapshot =
519                         getPackageManagerLocal().withFilteredSnapshot()) {
520             if (packageName != null) {
521                 try {
522                     DexOptHelper.getArtManagerLocal().dumpPackage(ipw, snapshot, packageName);
523                 } catch (IllegalArgumentException e) {
524                     // Package isn't found, but that should only happen due to race.
525                     ipw.println(e);
526                 }
527             } else {
528                 DexOptHelper.getArtManagerLocal().dump(ipw, snapshot);
529             }
530         }
531     }
532 
533     /**
534      * Returns the module names of the APEXes that contribute to bootclasspath.
535      */
getBcpApexes()536     private static List<String> getBcpApexes() {
537         String bcp = System.getenv("BOOTCLASSPATH");
538         if (TextUtils.isEmpty(bcp)) {
539             Log.e(TAG, "Unable to get BOOTCLASSPATH");
540             return List.of();
541         }
542 
543         ArrayList<String> bcpApexes = new ArrayList<>();
544         for (String pathStr : bcp.split(":")) {
545             Path path = Paths.get(pathStr);
546             // Check if the path is in the format of `/apex/<apex-module-name>/...` and extract the
547             // apex module name from the path.
548             if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) {
549                 bcpApexes.add(path.getName(1).toString());
550             }
551         }
552 
553         return bcpApexes;
554     }
555 
556     /**
557      * Returns true of any of the APEXes that contribute to bootclasspath has changed during this
558      * boot.
559      */
hasBcpApexesChanged()560     private static boolean hasBcpApexesChanged() {
561         Set<String> bcpApexes = new HashSet<>(getBcpApexes());
562         ApexManager apexManager = ApexManager.getInstance();
563         for (ActiveApexInfo apexInfo : apexManager.getActiveApexInfos()) {
564             if (bcpApexes.contains(apexInfo.apexModuleName) && apexInfo.activeApexChanged) {
565                 return true;
566             }
567         }
568         return false;
569     }
570 
571     /**
572      * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization.
573      */
getDexUseManagerLocal()574     public static @Nullable DexUseManagerLocal getDexUseManagerLocal() {
575         try {
576             return LocalManagerRegistry.getManagerOrThrow(DexUseManagerLocal.class);
577         } catch (ManagerNotFoundException e) {
578             throw new RuntimeException(e);
579         }
580     }
581 
582     private class DexoptDoneHandler implements ArtManagerLocal.DexoptDoneCallback {
583         /**
584          * Called after every package dexopt operation done by {@link ArtManagerLocal} (when ART
585          * Service is in use).
586          */
587         @Override
onDexoptDone(@onNull DexoptResult result)588         public void onDexoptDone(@NonNull DexoptResult result) {
589             switch (result.getReason()) {
590                 case ReasonMapping.REASON_FIRST_BOOT:
591                 case ReasonMapping.REASON_BOOT_AFTER_OTA:
592                 case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
593                     int numDexopted = 0;
594                     int numSkipped = 0;
595                     int numFailed = 0;
596                     for (DexoptResult.PackageDexoptResult pkgRes :
597                             result.getPackageDexoptResults()) {
598                         switch (pkgRes.getStatus()) {
599                             case DexoptResult.DEXOPT_PERFORMED:
600                                 numDexopted += 1;
601                                 break;
602                             case DexoptResult.DEXOPT_SKIPPED:
603                                 numSkipped += 1;
604                                 break;
605                             case DexoptResult.DEXOPT_FAILED:
606                                 numFailed += 1;
607                                 break;
608                         }
609                     }
610 
611                     reportBootDexopt(mBootDexoptStartTime, numDexopted, numSkipped, numFailed);
612                     break;
613             }
614 
615             for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
616                 CompilerStats.PackageStats stats =
617                         mPm.getOrCreateCompilerPackageStats(pkgRes.getPackageName());
618                 for (DexoptResult.DexContainerFileDexoptResult dexRes :
619                         pkgRes.getDexContainerFileDexoptResults()) {
620                     stats.setCompileTime(
621                             dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis());
622                 }
623             }
624 
625             synchronized (mPm.mLock) {
626                 mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
627                 mPm.mCompilerStats.maybeWriteAsync();
628             }
629 
630             if (result.getReason().equals(ReasonMapping.REASON_INACTIVE)) {
631                 for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
632                     if (pkgRes.getStatus() == DexoptResult.DEXOPT_PERFORMED) {
633                         long pkgSizeBytes = 0;
634                         long pkgSizeBeforeBytes = 0;
635                         for (DexoptResult.DexContainerFileDexoptResult dexRes :
636                                 pkgRes.getDexContainerFileDexoptResults()) {
637                             long dexContainerSize = new File(dexRes.getDexContainerFile()).length();
638                             pkgSizeBytes += dexRes.getSizeBytes() + dexContainerSize;
639                             pkgSizeBeforeBytes += dexRes.getSizeBeforeBytes() + dexContainerSize;
640                         }
641                         FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED,
642                                 pkgRes.getPackageName(), pkgSizeBeforeBytes, pkgSizeBytes,
643                                 false /* aggressive */);
644                     }
645                 }
646             }
647 
648             var updatedPackages = new ArraySet<String>();
649             for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
650                 if (pkgRes.hasUpdatedArtifacts()) {
651                     updatedPackages.add(pkgRes.getPackageName());
652                 }
653             }
654             if (!updatedPackages.isEmpty()) {
655                 LocalServices.getService(PinnerService.class)
656                         .update(updatedPackages, false /* force */);
657             }
658         }
659     }
660 
661     /**
662      * Initializes {@link ArtManagerLocal} before {@link getArtManagerLocal} is called.
663      */
initializeArtManagerLocal( @onNull Context systemContext, @NonNull PackageManagerService pm)664     public static void initializeArtManagerLocal(
665             @NonNull Context systemContext, @NonNull PackageManagerService pm) {
666         ArtManagerLocal artManager = new ArtManagerLocal(systemContext);
667         artManager.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run,
668                 pm.getDexOptHelper().new DexoptDoneHandler());
669         LocalManagerRegistry.addManager(ArtManagerLocal.class, artManager);
670         sArtManagerLocalIsInitialized = true;
671 
672         // Schedule the background job when boot is complete. This decouples us from when
673         // JobSchedulerService is initialized.
674         systemContext.registerReceiver(new BroadcastReceiver() {
675             @Override
676             public void onReceive(Context context, Intent intent) {
677                 context.unregisterReceiver(this);
678                 artManager.scheduleBackgroundDexoptJob();
679             }
680         }, new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED));
681 
682         StagedApexObserver.registerForStagedApexUpdates(artManager);
683     }
684 
685     /**
686      * Returns true if an {@link ArtManagerLocal} instance has been created.
687      *
688      * Avoid this function if at all possible, because it may hide initialization order problems.
689      */
artManagerLocalIsInitialized()690     public static boolean artManagerLocalIsInitialized() {
691         return sArtManagerLocalIsInitialized;
692     }
693 
694     /**
695      * Returns the registered {@link ArtManagerLocal} instance, or else throws an unchecked error.
696      */
getArtManagerLocal()697     public static @NonNull ArtManagerLocal getArtManagerLocal() {
698         try {
699             return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class);
700         } catch (ManagerNotFoundException e) {
701             throw new RuntimeException(e);
702         }
703     }
704 
705     /**
706      * Converts an ART Service {@link DexoptResult} to {@link DexOptResult}.
707      *
708      * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager.
709      */
710     @DexOptResult
convertToDexOptResult(DexoptResult result)711     private static int convertToDexOptResult(DexoptResult result) {
712         /*@DexoptResultStatus*/ int status = result.getFinalStatus();
713         switch (status) {
714             case DexoptResult.DEXOPT_SKIPPED:
715                 return PackageDexOptimizer.DEX_OPT_SKIPPED;
716             case DexoptResult.DEXOPT_FAILED:
717                 return PackageDexOptimizer.DEX_OPT_FAILED;
718             case DexoptResult.DEXOPT_PERFORMED:
719                 return PackageDexOptimizer.DEX_OPT_PERFORMED;
720             case DexoptResult.DEXOPT_CANCELLED:
721                 return PackageDexOptimizer.DEX_OPT_CANCELLED;
722             default:
723                 throw new IllegalArgumentException("DexoptResult for "
724                         + result.getPackageDexoptResults().get(0).getPackageName()
725                         + " has unsupported status " + status);
726         }
727     }
728 
729     /** Returns DexoptOptions by the given InstallRequest. */
getDexoptOptionsByInstallRequest( InstallRequest installRequest, DexManager dexManager)730     private static DexoptOptions getDexoptOptionsByInstallRequest(
731             InstallRequest installRequest, DexManager dexManager) {
732         final PackageSetting ps = installRequest.getScannedPackageSetting();
733         final String packageName = ps.getPackageName();
734         final boolean isBackupOrRestore =
735                 installRequest.getInstallReason() == INSTALL_REASON_DEVICE_RESTORE
736                         || installRequest.getInstallReason() == INSTALL_REASON_DEVICE_SETUP;
737         final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
738                 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
739                 | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE
740                 | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
741         // Compute the compilation reason from the installation scenario.
742         final int compilationReason =
743                 dexManager.getCompilationReasonForInstallScenario(
744                         installRequest.getInstallScenario());
745         final AndroidPackage pkg = ps.getPkg();
746         var options = new DexoptOptions(packageName, compilationReason, dexoptFlags);
747         if (installRequest.getDexoptCompilerFilter() != null) {
748             options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter());
749         } else if (shouldSkipDexopt(installRequest)) {
750             options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP);
751         }
752         return options;
753     }
754 
755     /** Perform dexopt if needed for the installation */
performDexoptIfNeeded( InstallRequest installRequest, DexManager dexManager, PackageManagerTracedLock.RawLock installLock)756     static void performDexoptIfNeeded(
757             InstallRequest installRequest,
758             DexManager dexManager,
759             PackageManagerTracedLock.RawLock installLock) {
760         if (!shouldCallArtService(installRequest)) {
761             return;
762         }
763 
764         // dexopt can take long, and ArtService doesn't require installd, so we release the lock
765         // here and re-acquire the lock after dexopt is finished.
766         if (installLock != null) {
767             installLock.unlock();
768         }
769         try {
770             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
771             DexoptOptions dexoptOptions =
772                     getDexoptOptionsByInstallRequest(installRequest, dexManager);
773             // Don't fail application installs if the dexopt step fails.
774             DexoptResult dexOptResult =
775                     DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions);
776             installRequest.onDexoptFinished(dexOptResult);
777         } finally {
778             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
779             if (installLock != null) {
780                 installLock.lock();
781             }
782         }
783     }
784 
785     /** Same as above, but runs asynchronously. */
performDexoptIfNeededAsync( InstallRequest installRequest, DexManager dexManager)786     static CompletableFuture<Void> performDexoptIfNeededAsync(
787             InstallRequest installRequest, DexManager dexManager) {
788         if (!shouldCallArtService(installRequest)) {
789             return CompletableFuture.completedFuture(null);
790         }
791 
792         return CompletableFuture.runAsync(
793                         () -> {
794                             try {
795                                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
796                                 DexoptOptions dexoptOptions =
797                                         getDexoptOptionsByInstallRequest(
798                                                 installRequest, dexManager);
799                                 // Don't fail application installs if the dexopt step fails.
800                                 // TODO(b/393076925): Make this async in ART Service.
801                                 DexoptResult dexOptResult =
802                                         DexOptHelper.dexoptPackageUsingArtService(
803                                                 installRequest, dexoptOptions);
804                                 installRequest.onDexoptFinished(dexOptResult);
805                             } finally {
806                                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
807                             }
808                         },
809                         sDexoptExecutor)
810                 .exceptionally(
811                         (t) -> {
812                             // This should never happen. A normal dexopt failure should result
813                             // in a DexoptResult.DEXOPT_FAILED, not an exception.
814                             Slog.wtf(TAG, "Dexopt encountered a fatal error", t);
815                             return null;
816                         });
817     }
818 
819     /**
820      * Use ArtService to perform dexopt by the given InstallRequest.
821      */
822     static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest,
823             DexoptOptions dexoptOptions) {
824         final PackageSetting ps = installRequest.getScannedPackageSetting();
825         final String packageName = ps.getPackageName();
826 
827         PackageSetting uncommittedPs = null;
828         if (Flags.improveInstallFreeze()) {
829             uncommittedPs = ps;
830         }
831 
832         PackageManagerLocal packageManagerLocal =
833                 LocalManagerRegistry.getManager(PackageManagerLocal.class);
834         try (PackageManagerLocal.FilteredSnapshot snapshot =
835                      PackageManagerLocalImpl.withFilteredSnapshot(packageManagerLocal,
836                 uncommittedPs)) {
837             boolean ignoreDexoptProfile =
838                     (installRequest.getInstallFlags()
839                             & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
840                             != 0;
841             /*@DexoptFlags*/ int extraFlags =
842                     ignoreDexoptProfile ? ArtFlags.FLAG_IGNORE_PROFILE : 0;
843             DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags);
844             DexoptResult dexOptResult = getArtManagerLocal().dexoptPackage(
845                     snapshot, packageName, params);
846 
847             return dexOptResult;
848         }
849     }
850 
851     private static boolean shouldSkipDexopt(InstallRequest installRequest) {
852         PackageSetting ps = installRequest.getScannedPackageSetting();
853         AndroidPackage pkg = ps.getPkg();
854         boolean onIncremental = isIncrementalPath(ps.getPathString());
855         return pkg == null || pkg.isDebuggable() || onIncremental;
856     }
857 
858     /**
859      * Returns whether to call ART Service to perform dexopt for the given InstallRequest. Note that
860      * ART Service may still skip dexopt, depending on the specified compiler filter, compilation
861      * reason, and other conditions.
862      */
863     private static boolean shouldCallArtService(InstallRequest installRequest) {
864         final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0);
865         // Historically, we did not dexopt instant apps,  and we have no plan to do so in the
866         // future, so there is no need to call into ART Service.
867         final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0);
868         final PackageSetting ps = installRequest.getScannedPackageSetting();
869         final AndroidPackage pkg = ps.getPkg();
870         final boolean performDexOptForRollback =
871                 !(installRequest.isRollback()
872                         && installRequest
873                                 .getInstallSource()
874                                 .mInitiatingPackageName
875                                 .equals("android"));
876 
877         // THINK TWICE when you add a new condition here. You probably want to add a condition to
878         // `shouldSkipDexopt` instead. In that way, ART Service will be called with the "skip"
879         // compiler filter and it will have the chance to decide whether to skip dexopt.
880         return !instantApp && pkg != null && !isApex && performDexOptForRollback;
881     }
882 
883     private static class StagedApexObserver extends IStagedApexObserver.Stub {
884         private final @NonNull ArtManagerLocal mArtManager;
885 
886         static void registerForStagedApexUpdates(@NonNull ArtManagerLocal artManager) {
887             IPackageManagerNative packageNative = IPackageManagerNative.Stub.asInterface(
888                     ServiceManager.getService("package_native"));
889             if (packageNative == null) {
890                 Log.e(TAG, "No IPackageManagerNative");
891                 return;
892             }
893 
894             try {
895                 packageNative.registerStagedApexObserver(new StagedApexObserver(artManager));
896             } catch (RemoteException e) {
897                 Log.e(TAG, "Failed to register staged apex observer", e);
898             }
899         }
900 
901         private StagedApexObserver(@NonNull ArtManagerLocal artManager) {
902             mArtManager = artManager;
903         }
904 
905         @Override
906         public void onApexStaged(@NonNull ApexStagedEvent event) {
907             mArtManager.onApexStaged(Arrays.stream(event.stagedApexInfos)
908                     .map(info -> info.moduleName).toArray(String[]::new));
909         }
910     }
911 }
912