1 /*
2  * Copyright (C) 2015 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.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
20 
21 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
22 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
23 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
24 import static com.android.server.pm.Installer.DEXOPT_FORCE;
25 import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE;
26 import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
27 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
28 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
29 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
30 import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
31 import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
32 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
33 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
34 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES;
35 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
36 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE;
37 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
38 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
39 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
40 import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
41 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
42 
43 import static dalvik.system.DexFile.getSafeModeCompilerFilter;
44 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
45 
46 import android.annotation.IntDef;
47 import android.annotation.NonNull;
48 import android.annotation.Nullable;
49 import android.content.ContentResolver;
50 import android.content.Context;
51 import android.content.pm.ApplicationInfo;
52 import android.content.pm.SharedLibraryInfo;
53 import android.content.pm.dex.ArtManager;
54 import android.content.pm.dex.DexMetadataHelper;
55 import android.os.PowerManager;
56 import android.os.SystemClock;
57 import android.os.SystemProperties;
58 import android.os.Trace;
59 import android.os.UserHandle;
60 import android.os.WorkSource;
61 import android.util.Log;
62 import android.util.Slog;
63 import android.util.SparseArray;
64 
65 import com.android.internal.annotations.GuardedBy;
66 import com.android.internal.annotations.VisibleForTesting;
67 import com.android.internal.content.F2fsUtils;
68 import com.android.server.LocalServices;
69 import com.android.server.apphibernation.AppHibernationManagerInternal;
70 import com.android.server.pm.Installer.InstallerException;
71 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
72 import com.android.server.pm.dex.ArtManagerService;
73 import com.android.server.pm.dex.ArtStatsLogUtils;
74 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
75 import com.android.server.pm.dex.DexoptOptions;
76 import com.android.server.pm.dex.DexoptUtils;
77 import com.android.server.pm.dex.PackageDexUsage;
78 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
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 dalvik.system.DexFile;
84 
85 import java.io.File;
86 import java.io.IOException;
87 import java.lang.annotation.Retention;
88 import java.lang.annotation.RetentionPolicy;
89 import java.util.ArrayList;
90 import java.util.Arrays;
91 import java.util.List;
92 import java.util.Random;
93 
94 /**
95  * Helper class for running dexopt command on packages.
96  */
97 public class PackageDexOptimizer {
98     private static final String TAG = "PackageDexOptimizer";
99     static final String OAT_DIR_NAME = "oat";
100     // TODO b/19550105 Remove error codes and use exceptions
101     /** No need to run dexopt and it was skipped */
102     public static final int DEX_OPT_SKIPPED = 0;
103     /** Dexopt was completed */
104     public static final int DEX_OPT_PERFORMED = 1;
105     /**
106      * Cancelled while running it. This is not an error case as cancel was requested
107      * from the client.
108      */
109     public static final int DEX_OPT_CANCELLED = 2;
110     /** Failed to run dexopt */
111     public static final int DEX_OPT_FAILED = -1;
112 
113     @IntDef(prefix = {"DEX_OPT_"}, value = {
114             DEX_OPT_SKIPPED,
115             DEX_OPT_PERFORMED,
116             DEX_OPT_CANCELLED,
117             DEX_OPT_FAILED,
118     })
119     @Retention(RetentionPolicy.SOURCE)
120     public @interface DexOptResult {
121     }
122 
123     // One minute over PM WATCHDOG_TIMEOUT
124     private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
125 
126     private final PackageManagerTracedLock mInstallLock;
127 
128     /**
129      * This should be accessed only through {@link #getInstallerLI()} with
130      * {@link #mInstallLock}.
131      */
132     private final Installer mInstaller;
133 
134     @GuardedBy("mInstallLock")
135     private final PowerManager.WakeLock mDexoptWakeLock;
136     private volatile boolean mSystemReady;
137 
138     private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
139     private final Injector mInjector;
140 
141 
142     private final Context mContext;
143     private static final Random sRandom = new Random();
144 
PackageDexOptimizer(Installer installer, PackageManagerTracedLock installLock, Context context, String wakeLockTag)145     PackageDexOptimizer(Installer installer, PackageManagerTracedLock installLock, Context context,
146             String wakeLockTag) {
147         this(new Injector() {
148             @Override
149             public AppHibernationManagerInternal getAppHibernationManagerInternal() {
150                 return LocalServices.getService(AppHibernationManagerInternal.class);
151             }
152 
153             @Override
154             public PowerManager getPowerManager(Context context) {
155                 return context.getSystemService(PowerManager.class);
156             }
157         }, installer, installLock, context, wakeLockTag);
158     }
159 
PackageDexOptimizer(PackageDexOptimizer from)160     protected PackageDexOptimizer(PackageDexOptimizer from) {
161         this.mContext = from.mContext;
162         this.mInstaller = from.mInstaller;
163         this.mInstallLock = from.mInstallLock;
164         this.mDexoptWakeLock = from.mDexoptWakeLock;
165         this.mSystemReady = from.mSystemReady;
166         this.mInjector = from.mInjector;
167     }
168 
169     @VisibleForTesting
PackageDexOptimizer(@onNull Injector injector, Installer installer, PackageManagerTracedLock installLock, Context context, String wakeLockTag)170     PackageDexOptimizer(@NonNull Injector injector, Installer installer,
171             PackageManagerTracedLock installLock, Context context, String wakeLockTag) {
172         this.mContext = context;
173         this.mInstaller = installer;
174         this.mInstallLock = installLock;
175 
176         PowerManager powerManager = injector.getPowerManager(context);
177         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
178         mInjector = injector;
179     }
180 
canOptimizePackage(@onNull AndroidPackage pkg)181     boolean canOptimizePackage(@NonNull AndroidPackage pkg) {
182         // The system package has to be optimized during early boot by odrefresh instead.
183         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
184             return false;
185         }
186 
187         // We do not dexopt a package with no code.
188         if (!pkg.isDeclaredHavingCode()) {
189             return false;
190         }
191 
192         // We do not dexopt APEX packages.
193         if (pkg.isApex()) {
194             return false;
195         }
196 
197         // We do not dexopt unused packages.
198         // It's possible for this to be called before app hibernation service is ready due to
199         // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
200         // a hibernating app should have no artifacts to copy in the first place.
201         AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal();
202         if (ahm != null
203                 && ahm.isHibernatingGlobally(pkg.getPackageName())
204                 && ahm.isOatArtifactDeletionEnabled()) {
205             return false;
206         }
207 
208         return true;
209     }
210 
211     /**
212      * Performs dexopt on all code paths and libraries of the specified package for specified
213      * instruction sets.
214      *
215      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
216      * synchronized on {@link #mInstallLock}.
217      */
218     @DexOptResult
performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)219     int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
220             String[] instructionSets, CompilerStats.PackageStats packageStats,
221             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
222             throws LegacyDexoptDisabledException {
223         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
224             throw new IllegalArgumentException(
225                     "System server dexopting should be done via odrefresh");
226         }
227         if (pkg.getUid() == -1) {
228             throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName()
229                     + " has invalid uid.");
230         }
231         if (!canOptimizePackage(pkg)) {
232             return DEX_OPT_SKIPPED;
233         }
234         try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
235             final long acquireTime = acquireWakeLockLI(pkg.getUid());
236             try {
237                 return performDexOptLI(pkg, pkgSetting, instructionSets,
238                         packageStats, packageUseInfo, options);
239             } finally {
240                 releaseWakeLockLI(acquireTime);
241             }
242         }
243     }
244 
245     /**
246      * Performs dexopt on all code paths of the given package.
247      * It assumes the install lock is held.
248      */
249     @GuardedBy("mInstallLock")
250     @DexOptResult
performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)251     private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
252             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
253             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
254             throws LegacyDexoptDisabledException {
255         // ClassLoader only refers non-native (jar) shared libraries and must ignore
256         // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
257         final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
258                 .getNonNativeUsesLibraryInfos();
259         final String[] instructionSets = targetInstructionSets != null ?
260                 targetInstructionSets : getAppDexInstructionSets(
261                 pkgSetting.getPrimaryCpuAbi(),
262                 pkgSetting.getSecondaryCpuAbi());
263         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
264         final List<String> paths = AndroidPackageUtils.getAllCodePaths(pkg);
265 
266         int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
267         if (sharedGid == -1) {
268             Slog.wtf(TAG, "Well this is awkward; package " + pkg.getPackageName() + " had UID "
269                     + pkg.getUid(), new Throwable());
270             sharedGid = android.os.Process.NOBODY_UID;
271         }
272 
273         // Get the class loader context dependencies.
274         // For each code path in the package, this array contains the class loader context that
275         // needs to be passed to dexopt in order to ensure correct optimizations.
276         boolean[] pathsWithCode = new boolean[paths.size()];
277         pathsWithCode[0] = pkg.isDeclaredHavingCode();
278         for (int i = 1; i < paths.size(); i++) {
279             pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
280         }
281         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
282                 pkg, sharedLibraries, pathsWithCode);
283 
284         // Validity check that we do not call dexopt with inconsistent data.
285         if (paths.size() != classLoaderContexts.length) {
286             String[] splitCodePaths = pkg.getSplitCodePaths();
287             throw new IllegalStateException("Inconsistent information "
288                 + "between AndroidPackage and its ApplicationInfo. "
289                 + "pkg.getAllCodePaths=" + paths
290                 + " pkg.getBaseCodePath=" + pkg.getBaseApkPath()
291                 + " pkg.getSplitCodePaths="
292                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
293         }
294 
295         int result = DEX_OPT_SKIPPED;
296         for (int i = 0; i < paths.size(); i++) {
297             // Skip paths that have no code.
298             if (!pathsWithCode[i]) {
299                 continue;
300             }
301             if (classLoaderContexts[i] == null) {
302                 throw new IllegalStateException("Inconsistent information in the "
303                         + "package structure. A split is marked to contain code "
304                         + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
305             }
306 
307             // Append shared libraries with split dependencies for this split.
308             String path = paths.get(i);
309             if (options.getSplitName() != null) {
310                 // We are asked to compile only a specific split. Check that the current path is
311                 // what we are looking for.
312                 if (!options.getSplitName().equals(new File(path).getName())) {
313                     continue;
314                 }
315             }
316 
317             String profileName = ArtManager.getProfileName(
318                     i == 0 ? null : pkg.getSplitNames()[i - 1]);
319 
320             final boolean isUsedByOtherApps;
321             if (options.isDexoptAsSharedLibrary()) {
322                 isUsedByOtherApps = true;
323             } else {
324                 // We get here when collecting dexopt commands in OTA preopt, even when ART Service
325                 // is in use. packageUseInfo isn't useful in that case since the legacy dex use
326                 // database hasn't been updated. So we'd have to query ART Service instead, but it
327                 // doesn't provide that API. Just cop-out and bypass the cloud profile handling.
328                 // That means such apps will get preopted wrong, and we'll leave it to a later
329                 // background dexopt after reboot instead.
330                 isUsedByOtherApps = false;
331             }
332 
333             String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
334             // If the app is used by other apps, we must not use the existing profile because it
335             // may contain user data, unless the profile is newly created on install.
336             final boolean useCloudProfile = isProfileGuidedCompilerFilter(compilerFilter)
337                     && isUsedByOtherApps
338                     && options.getCompilationReason() != PackageManagerService.REASON_INSTALL;
339 
340             String dexMetadataPath = null;
341             if (options.isDexoptInstallWithDexMetadata() || useCloudProfile) {
342                 File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
343                 dexMetadataPath = dexMetadataFile == null
344                         ? null : dexMetadataFile.getAbsolutePath();
345             }
346 
347             // If we don't have to check for profiles updates assume
348             // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
349             // profiles.
350             int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
351             if (options.isCheckForProfileUpdates()) {
352                 profileAnalysisResult =
353                         analyseProfiles(pkg, sharedGid, profileName, compilerFilter);
354             }
355             String cloudProfileName = null;
356             try {
357                 if (useCloudProfile) {
358                     cloudProfileName = "cloud-" + profileName;
359                     if (prepareCloudProfile(pkg, cloudProfileName, path, dexMetadataPath)) {
360                         profileName = cloudProfileName;
361                     } else {
362                         // Fall back to use the shared filter.
363                         compilerFilter =
364                                 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
365                                         PackageManagerService.REASON_SHARED);
366                         profileName = null;
367                     }
368 
369                     // We still run `analyseProfiles` even if `useCloudProfile` is true because it
370                     // merges profiles into the reference profile, which a system API
371                     // `ArtManager.snapshotRuntimeProfile` takes snapshots from. However, we don't
372                     // want the result to affect the decision of whether dexopt is needed.
373                     profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
374                 }
375 
376                 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
377                 // flags.
378                 final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter,
379                         useCloudProfile, options);
380 
381                 for (String dexCodeIsa : dexCodeInstructionSets) {
382                     int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
383                             profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
384                             packageStats, options.isDowngrade(), profileName, dexMetadataPath,
385                             options.getCompilationReason());
386                     // OTAPreopt doesn't have stats so don't report in that case.
387                     if (packageStats != null) {
388                         Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dex2oat-metrics");
389                         try {
390                             long sessionId = sRandom.nextLong();
391                             ArtStatsLogUtils.writeStatsLog(
392                                     mArtStatsLogger,
393                                     sessionId,
394                                     compilerFilter,
395                                     pkg.getUid(),
396                                     packageStats.getCompileTime(path),
397                                     dexMetadataPath,
398                                     options.getCompilationReason(),
399                                     newResult,
400                                     ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(),
401                                             pkg.getSplitCodePaths()),
402                                     dexCodeIsa,
403                                     path);
404                         } finally {
405                             Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
406                         }
407                     }
408 
409                     // Should stop the operation immediately.
410                     if (newResult == DEX_OPT_CANCELLED) {
411                         // Even for the cancellation, return failed if has failed.
412                         if (result == DEX_OPT_FAILED) {
413                             return result;
414                         }
415                         return newResult;
416                     }
417                     // The end result is:
418                     //  - FAILED if any path failed,
419                     //  - PERFORMED if at least one path needed compilation,
420                     //  - SKIPPED when all paths are up to date
421                     if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
422                         result = newResult;
423                     }
424                 }
425             } finally {
426                 // ART Service is always enabled, so we should only arrive here
427                 // during OTA preopt, and there should be no cloud profile.
428                 if (cloudProfileName != null) {
429                     throw new LegacyDexoptDisabledException();
430                 }
431             }
432         }
433         return result;
434     }
435 
436     /**
437      * Creates a profile with the name {@code profileName} from the dex metadata file at {@code
438      * dexMetadataPath} for the dex file at {@code path} belonging to the package {@code pkg}.
439      *
440      * @return true on success, or false otherwise.
441      */
prepareCloudProfile(AndroidPackage pkg, String profileName, String path, @Nullable String dexMetadataPath)442     private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
443             @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
444         if (dexMetadataPath != null) {
445             // ART Service is always enabled, so we should only arrive here
446             // during OTA preopt, i.e. when the installer is isolated.
447             if (!mInstaller.isIsolated()) {
448                 throw new LegacyDexoptDisabledException();
449             }
450             return true;
451         } else {
452             return false;
453         }
454     }
455 
456     /**
457      * Performs dexopt on the {@code path} belonging to the package {@code pkg}.
458      *
459      * @return
460      *      DEX_OPT_FAILED if there was any exception during dexopt
461      *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
462      *      DEX_OPT_SKIPPED if the path does not need to be deopt-ed.
463      */
464     @GuardedBy("mInstallLock")
465     @DexOptResult
dexOptPath(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String path, String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason)466     private int dexOptPath(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
467             String path, String isa, String compilerFilter, int profileAnalysisResult,
468             String classLoaderContext, int dexoptFlags, int uid,
469             CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
470             String dexMetadataPath, int compilationReason) throws LegacyDexoptDisabledException {
471         String oatDir = getPackageOatDirIfSupported(pkgSetting, pkg);
472 
473         int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
474                 classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir);
475         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
476             return DEX_OPT_SKIPPED;
477         }
478 
479         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
480                 + " pkg=" + pkg.getPackageName() + " isa=" + isa
481                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
482                 + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
483                 + " classLoaderContext=" + classLoaderContext);
484 
485         try {
486             long startTime = System.currentTimeMillis();
487 
488             // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
489             // installd only uses downgrade flag for secondary dex files and ignores it for
490             // primary dex files.
491             String seInfo = pkgSetting.getSeInfo();
492             boolean completed = getInstallerLI().dexopt(path, uid, pkg.getPackageName(), isa,
493                     dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.getVolumeUuid(),
494                     classLoaderContext, seInfo, /* downgrade= */ false ,
495                     pkg.getTargetSdkVersion(), profileName, dexMetadataPath,
496                     getAugmentedReasonName(compilationReason, dexMetadataPath != null));
497             if (!completed) {
498                 return DEX_OPT_CANCELLED;
499             }
500             if (packageStats != null) {
501                 long endTime = System.currentTimeMillis();
502                 packageStats.setCompileTime(path, (int)(endTime - startTime));
503             }
504             if (oatDir != null) {
505                 // Release odex/vdex compressed blocks to save user space.
506                 // Compression support will be checked in F2fsUtils.
507                 // The system app may be dexed, oatDir may be null, skip this situation.
508                 final ContentResolver resolver = mContext.getContentResolver();
509                 F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
510             }
511             return DEX_OPT_PERFORMED;
512         } catch (InstallerException e) {
513             Slog.w(TAG, "Failed to dexopt", e);
514             return DEX_OPT_FAILED;
515         }
516     }
517 
getAugmentedReasonName(int compilationReason, boolean useDexMetadata)518     private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
519         String annotation = useDexMetadata
520                 ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
521         return getReasonName(compilationReason) + annotation;
522     }
523 
524     @GuardedBy("mInstallLock")
acquireWakeLockLI(final int uid)525     private long acquireWakeLockLI(final int uid) {
526         // During boot the system doesn't need to instantiate and obtain a wake lock.
527         // PowerManager might not be ready, but that doesn't mean that we can't proceed with
528         // dexopt.
529         if (!mSystemReady) {
530             return -1;
531         }
532         mDexoptWakeLock.setWorkSource(new WorkSource(uid));
533         mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
534         return SystemClock.elapsedRealtime();
535     }
536 
537     @GuardedBy("mInstallLock")
releaseWakeLockLI(final long acquireTime)538     private void releaseWakeLockLI(final long acquireTime) {
539         if (acquireTime < 0) {
540             return;
541         }
542         try {
543             if (mDexoptWakeLock.isHeld()) {
544                 mDexoptWakeLock.release();
545             }
546             final long duration = SystemClock.elapsedRealtime() - acquireTime;
547             if (duration >= WAKELOCK_TIMEOUT_MS) {
548                 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
549                         + " time out. Operation took " + duration + " ms. Thread: "
550                         + Thread.currentThread().getName());
551             }
552         } catch (RuntimeException e) {
553             Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
554         }
555     }
556 
557     /**
558      * Adjust the given dexopt-needed value. Can be overridden to influence the decision to
559      * optimize or not (and in what way).
560      */
adjustDexoptNeeded(int dexoptNeeded)561     protected int adjustDexoptNeeded(int dexoptNeeded) {
562         return dexoptNeeded;
563     }
564 
565     /**
566      * Adjust the given dexopt flags that will be passed to the installer.
567      */
adjustDexoptFlags(int dexoptFlags)568     protected int adjustDexoptFlags(int dexoptFlags) {
569         return dexoptFlags;
570     }
571 
572     /**
573      * Returns the compiler filter that should be used to optimize the secondary dex.
574      * The target filter will be updated if the package code is used by other apps
575      * or if it has the safe mode flag set.
576      */
getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps)577     private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
578             boolean isUsedByOtherApps) {
579         if (info.isEmbeddedDexUsed()) {
580             // Downgrade optimizing filters to "verify", but don't upgrade lower filters.
581             return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
582                                                                            : targetCompilerFilter;
583         }
584 
585         // We force vmSafeMode on debuggable apps as well:
586         //  - the runtime ignores their compiled code
587         //  - they generally have lots of methods that could make the compiler used run
588         //    out of memory (b/130828957)
589         // Note that forcing the compiler filter here applies to all compilations (even if they
590         // are done via adb shell commands). That's ok because right now the runtime will ignore
591         // the compiled code anyway. The alternative would have been to update either
592         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
593         // but that would have the downside of possibly producing a big odex files which would
594         // be ignored anyway.
595         boolean vmSafeModeOrDebuggable = ((info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0)
596                 || ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
597 
598         if (vmSafeModeOrDebuggable) {
599             return getSafeModeCompilerFilter(targetCompilerFilter);
600         }
601 
602         if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
603             // If the dex files is used by other apps, apply the shared filter.
604             return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
605                     PackageManagerService.REASON_SHARED);
606         }
607 
608         return targetCompilerFilter;
609     }
610 
611     /**
612      * Returns the compiler filter that should be used to optimize the primary dex.
613      * The target filter will be updated if the package has the safe mode flag set. Note that this
614      * method does NOT take other app use into account. The caller should be responsible for
615      * handling the case where the package code is used by other apps.
616      */
getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter)617     private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) {
618         if (pkg.isUseEmbeddedDex()) {
619             // Downgrade optimizing filters to "verify", but don't upgrade lower filters.
620             return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
621                                                                            : targetCompilerFilter;
622         }
623 
624         // We force vmSafeMode on debuggable apps as well:
625         //  - the runtime ignores their compiled code
626         //  - they generally have lots of methods that could make the compiler used run
627         //    out of memory (b/130828957)
628         // Note that forcing the compiler filter here applies to all compilations (even if they
629         // are done via adb shell commands). That's ok because right now the runtime will ignore
630         // the compiled code anyway. The alternative would have been to update either
631         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
632         // but that would have the downside of possibly producing a big odex files which would
633         // be ignored anyway.
634         boolean vmSafeModeOrDebuggable = pkg.isVmSafeMode() || pkg.isDebuggable();
635 
636         if (vmSafeModeOrDebuggable) {
637             return getSafeModeCompilerFilter(targetCompilerFilter);
638         }
639 
640         return targetCompilerFilter;
641     }
642 
isAppImageEnabled()643     private boolean isAppImageEnabled() {
644         return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
645     }
646 
getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options)647     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
648         return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
649                 info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
650                 info.requestsIsolatedSplitLoading(), compilerFilter, false /* useCloudProfile */,
651                 options);
652     }
653 
getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String compilerFilter, boolean useCloudProfile, DexoptOptions options)654     private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
655             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
656         return getDexFlags(pkg.isDebuggable(),
657                 AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
658                 pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
659                 useCloudProfile, options);
660     }
661 
662     /**
663      * Computes the dex flags that needs to be pass to installd for the given package and compiler
664      * filter.
665      */
getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, String compilerFilter, boolean useCloudProfile, DexoptOptions options)666     private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
667             SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
668             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
669         // Profile guide compiled oat files should not be public unles they are based
670         // on profiles from dex metadata archives.
671         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
672         // the user does not have an existing profile.
673         // The flag useCloudProfile applies only when the cloud profile should be used.
674         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
675         boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()
676                 || useCloudProfile;
677         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
678         // Some apps are executed with restrictions on hidden API usage. If this app is one
679         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
680         // TODO we should pass the actual flag value to dexopt, rather than assuming denylist
681         // TODO(b/135203078): This flag is no longer set as part of AndroidPackage
682         //  and may not be preserved
683         int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED
684                 ? 0
685                 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
686         // Avoid generating CompactDex for modes that are latency critical.
687         final int compilationReason = options.getCompilationReason();
688         boolean generateCompactDex = true;
689         switch (compilationReason) {
690             case PackageManagerService.REASON_FIRST_BOOT:
691             case PackageManagerService.REASON_BOOT_AFTER_OTA:
692             case PackageManagerService.REASON_POST_BOOT:
693             case PackageManagerService.REASON_INSTALL:
694                  generateCompactDex = false;
695         }
696         // Use app images only if it is enabled and we are compiling
697         // profile-guided (so the app image doesn't conservatively contain all classes).
698         // If the app didn't request for the splits to be loaded in isolation or if it does not
699         // declare inter-split dependencies, then all the splits will be loaded in the base
700         // apk class loader (in the order of their definition, otherwise disable app images
701         // because they are unsupported for multiple class loaders. b/7269679
702         boolean generateAppImage = isProfileGuidedFilter && (splitDependencies == null ||
703                 !requestsIsolatedSplitLoading) && isAppImageEnabled();
704         int dexFlags =
705                 (isPublic ? DEXOPT_PUBLIC : 0)
706                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
707                 | profileFlag
708                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
709                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
710                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
711                 | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
712                 | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
713                 | hiddenApiFlag;
714         return adjustDexoptFlags(dexFlags);
715     }
716 
717     /**
718      * Assesses if there's a need to perform dexopt on {@code path} for the given
719      * configuration (isa, compiler filter, profile).
720      */
getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade, int dexoptFlags, String oatDir)721     private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
722             String classLoaderContext, int profileAnalysisResult, boolean downgrade,
723             int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
724         // Allow calls from OtaDexoptService even when ART Service is in use. The installer is
725         // isolated in that case so later calls to it won't call into installd anyway.
726         if (!mInstaller.isIsolated()) {
727             throw new LegacyDexoptDisabledException();
728         }
729 
730         final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
731         final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
732         boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
733 
734         if (!newProfile && isProfileGuidedFilter && shouldBePublic
735                 && isOdexPrivate(packageName, path, isa, oatDir)) {
736             // The profile that will be used is a cloud profile, while the profile used previously
737             // is a user profile. Typically, this happens after an app starts being used by other
738             // apps.
739             newProfile = true;
740         }
741 
742         int dexoptNeeded;
743         try {
744             // A profile guided optimizations with an empty profile is essentially 'verify' and
745             // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot
746             // check the profiles because system server does not have access to them.
747             // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and
748             // manually adjust the actual filter before checking.
749             //
750             // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change,
751             // and in the interim we can still improve things here.
752             String actualCompilerFilter = compilerFilter;
753             if (compilerFilterDependsOnProfiles(compilerFilter)
754                     && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) {
755                 actualCompilerFilter = "verify";
756             }
757             dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter,
758                     classLoaderContext, newProfile, downgrade);
759         } catch (IOException ioe) {
760             Slog.w(TAG, "IOException reading apk: " + path, ioe);
761             return DEX_OPT_FAILED;
762         } catch (RuntimeException e) {
763             Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e);
764             return DEX_OPT_FAILED;
765         }
766         return adjustDexoptNeeded(dexoptNeeded);
767     }
768 
769     /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */
compilerFilterDependsOnProfiles(String compilerFilter)770     private boolean compilerFilterDependsOnProfiles(String compilerFilter) {
771         return compilerFilter.endsWith("-profile");
772     }
773 
774     /** Returns true if the current artifacts of the app are private to the app itself. */
isOdexPrivate(String packageName, String path, String isa, String oatDir)775     private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir)
776             throws LegacyDexoptDisabledException {
777         throw new LegacyDexoptDisabledException();
778     }
779 
780     /**
781      * Checks if there is an update on the profile information of the {@code pkg}.
782      * If the compiler filter is not profile guided the method returns a safe default:
783      * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA.
784      *
785      * Note that this is a "destructive" operation with side effects. Under the hood the
786      * current profile and the reference profile will be merged and subsequent calls
787      * may return a different result.
788      */
analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter)789     private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
790             String compilerFilter) throws LegacyDexoptDisabledException {
791         throw new LegacyDexoptDisabledException();
792     }
793 
794     /**
795      * Gets oat dir for the specified package if needed and supported.
796      * In certain cases oat directory
797      * <strong>cannot</strong> be created:
798      * <ul>
799      *      <li>{@code pkg} is a system app, which is not updated.</li>
800      *      <li>Package location is not a directory, i.e. monolithic install.</li>
801      * </ul>
802      *
803      * @return Absolute path to the oat directory or null, if oat directories
804      * not needed or unsupported for the package.
805      */
806     @Nullable
getPackageOatDirIfSupported(@onNull PackageState packageState, @NonNull AndroidPackage pkg)807     private String getPackageOatDirIfSupported(@NonNull PackageState packageState,
808             @NonNull AndroidPackage pkg) {
809         if (!AndroidPackageUtils.canHaveOatDir(packageState, pkg)) {
810             return null;
811         }
812         File codePath = new File(pkg.getPath());
813         if (!codePath.isDirectory()) {
814             return null;
815         }
816         return getOatDir(codePath).getAbsolutePath();
817     }
818 
819     /** Returns the oat dir for the given code path */
getOatDir(File codePath)820     public static File getOatDir(File codePath) {
821         return new File(codePath, OAT_DIR_NAME);
822     }
823 
systemReady()824     void systemReady() {
825         mSystemReady = true;
826     }
827 
printDexoptFlags(int flags)828     private String printDexoptFlags(int flags) {
829         ArrayList<String> flagsList = new ArrayList<>();
830 
831         if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) {
832             flagsList.add("boot_complete");
833         }
834         if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) {
835             flagsList.add("debuggable");
836         }
837         if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) {
838             flagsList.add("profile_guided");
839         }
840         if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) {
841             flagsList.add("public");
842         }
843         if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
844             flagsList.add("secondary");
845         }
846         if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
847             flagsList.add("force");
848         }
849         if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
850             flagsList.add("storage_ce");
851         }
852         if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
853             flagsList.add("storage_de");
854         }
855         if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
856             flagsList.add("idle_background_job");
857         }
858         if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
859             flagsList.add("enable_hidden_api_checks");
860         }
861 
862         return String.join(",", flagsList);
863     }
864 
865     /**
866      * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a
867      * dexopt path.
868      */
869     public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer {
870 
ForcedUpdatePackageDexOptimizer(Installer installer, PackageManagerTracedLock installLock, Context context, String wakeLockTag)871         public ForcedUpdatePackageDexOptimizer(Installer installer,
872                 PackageManagerTracedLock installLock, Context context, String wakeLockTag) {
873             super(installer, installLock, context, wakeLockTag);
874         }
875 
ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from)876         public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) {
877             super(from);
878         }
879 
880         @Override
adjustDexoptNeeded(int dexoptNeeded)881         protected int adjustDexoptNeeded(int dexoptNeeded) {
882             if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
883                 // Ensure compilation by pretending a compiler filter change on the
884                 // apk/odex location (the reason for the '-'. A positive value means
885                 // the 'oat' location).
886                 return -DexFile.DEX2OAT_FOR_FILTER;
887             }
888             return dexoptNeeded;
889         }
890 
891         @Override
adjustDexoptFlags(int flags)892         protected int adjustDexoptFlags(int flags) {
893             // Add DEXOPT_FORCE flag to signal installd that it should force compilation
894             // and discard dexoptanalyzer result.
895             return flags | DEXOPT_FORCE;
896         }
897     }
898 
899     /**
900      * Returns {@link #mInstaller} with {@link #mInstallLock}. This should be used for all
901      * {@link #mInstaller} access.
902      */
903     @GuardedBy("mInstallLock")
getInstallerLI()904     private Installer getInstallerLI() {
905         return mInstaller;
906     }
907 
908     /**
909      * Injector for {@link PackageDexOptimizer} dependencies
910      */
911     interface Injector {
getAppHibernationManagerInternal()912         AppHibernationManagerInternal getAppHibernationManagerInternal();
913 
getPowerManager(Context context)914         PowerManager getPowerManager(Context context);
915     }
916 }
917