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