• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.FileUtils;
56 import android.os.PowerManager;
57 import android.os.SystemClock;
58 import android.os.SystemProperties;
59 import android.os.Trace;
60 import android.os.UserHandle;
61 import android.os.WorkSource;
62 import android.os.storage.StorageManager;
63 import android.util.Log;
64 import android.util.Slog;
65 import android.util.SparseArray;
66 
67 import com.android.internal.annotations.GuardedBy;
68 import com.android.internal.annotations.VisibleForTesting;
69 import com.android.internal.content.F2fsUtils;
70 import com.android.internal.util.IndentingPrintWriter;
71 import com.android.server.LocalServices;
72 import com.android.server.apphibernation.AppHibernationManagerInternal;
73 import com.android.server.pm.Installer.InstallerException;
74 import com.android.server.pm.dex.ArtManagerService;
75 import com.android.server.pm.dex.ArtStatsLogUtils;
76 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
77 import com.android.server.pm.dex.DexManager;
78 import com.android.server.pm.dex.DexoptOptions;
79 import com.android.server.pm.dex.DexoptUtils;
80 import com.android.server.pm.dex.PackageDexUsage;
81 import com.android.server.pm.parsing.pkg.AndroidPackage;
82 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
83 import com.android.server.pm.pkg.PackageStateInternal;
84 
85 import dalvik.system.DexFile;
86 
87 import java.io.File;
88 import java.io.IOException;
89 import java.lang.annotation.Retention;
90 import java.lang.annotation.RetentionPolicy;
91 import java.util.ArrayList;
92 import java.util.Arrays;
93 import java.util.List;
94 import java.util.Map;
95 import java.util.Random;
96 
97 /**
98  * Helper class for running dexopt command on packages.
99  */
100 public class PackageDexOptimizer {
101     private static final String TAG = "PackageDexOptimizer";
102     static final String OAT_DIR_NAME = "oat";
103     // TODO b/19550105 Remove error codes and use exceptions
104     /** No need to run dexopt and it was skipped */
105     public static final int DEX_OPT_SKIPPED = 0;
106     /** Dexopt was completed */
107     public static final int DEX_OPT_PERFORMED = 1;
108     /**
109      * Cancelled while running it. This is not an error case as cancel was requested
110      * from the client.
111      */
112     public static final int DEX_OPT_CANCELLED = 2;
113     /** Failed to run dexopt */
114     public static final int DEX_OPT_FAILED = -1;
115 
116     @IntDef(prefix = {"DEX_OPT_"}, value = {
117             DEX_OPT_SKIPPED,
118             DEX_OPT_PERFORMED,
119             DEX_OPT_CANCELLED,
120             DEX_OPT_FAILED,
121     })
122     @Retention(RetentionPolicy.SOURCE)
123     public @interface DexOptResult {
124     }
125 
126     // One minute over PM WATCHDOG_TIMEOUT
127     private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
128 
129     private final Object mInstallLock;
130 
131     /**
132      * This should be accessed only through {@link #getInstallerLI()} with {@link #mInstallLock}
133      * or {@link #getInstallerWithoutLock()} without the lock. Check both methods for further
134      * details on when to use each of them.
135      */
136     private final Installer mInstaller;
137 
138     @GuardedBy("mInstallLock")
139     private final PowerManager.WakeLock mDexoptWakeLock;
140     private volatile boolean mSystemReady;
141 
142     private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
143     private final Injector mInjector;
144 
145 
146     private final Context mContext;
147     private static final Random sRandom = new Random();
148 
PackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)149     PackageDexOptimizer(Installer installer, Object installLock, Context context,
150             String wakeLockTag) {
151         this(new Injector() {
152             @Override
153             public AppHibernationManagerInternal getAppHibernationManagerInternal() {
154                 return LocalServices.getService(AppHibernationManagerInternal.class);
155             }
156 
157             @Override
158             public PowerManager getPowerManager(Context context) {
159                 return context.getSystemService(PowerManager.class);
160             }
161         }, installer, installLock, context, wakeLockTag);
162     }
163 
PackageDexOptimizer(PackageDexOptimizer from)164     protected PackageDexOptimizer(PackageDexOptimizer from) {
165         this.mContext = from.mContext;
166         this.mInstaller = from.mInstaller;
167         this.mInstallLock = from.mInstallLock;
168         this.mDexoptWakeLock = from.mDexoptWakeLock;
169         this.mSystemReady = from.mSystemReady;
170         this.mInjector = from.mInjector;
171     }
172 
173     @VisibleForTesting
PackageDexOptimizer(@onNull Injector injector, Installer installer, Object installLock, Context context, String wakeLockTag)174     PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
175             Context context, String wakeLockTag) {
176         this.mContext = context;
177         this.mInstaller = installer;
178         this.mInstallLock = installLock;
179 
180         PowerManager powerManager = injector.getPowerManager(context);
181         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
182         mInjector = injector;
183     }
184 
canOptimizePackage(@onNull AndroidPackage pkg)185     boolean canOptimizePackage(@NonNull AndroidPackage pkg) {
186         // We do not dexopt a package with no code.
187         // Note that the system package is marked as having no code, however we can
188         // still optimize it via dexoptSystemServerPath.
189         if (!PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName()) && !pkg.isHasCode()) {
190             return false;
191         }
192 
193         // We do not dexopt unused packages.
194         // It's possible for this to be called before app hibernation service is ready due to
195         // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
196         // a hibernating app should have no artifacts to copy in the first place.
197         AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal();
198         if (ahm != null
199                 && ahm.isHibernatingGlobally(pkg.getPackageName())
200                 && ahm.isOatArtifactDeletionEnabled()) {
201             return false;
202         }
203 
204         return true;
205     }
206 
207     /**
208      * Performs dexopt on all code paths and libraries of the specified package for specified
209      * instruction sets.
210      *
211      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
212      * synchronized on {@link #mInstallLock}.
213      */
214     @DexOptResult
performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)215     int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
216             String[] instructionSets, CompilerStats.PackageStats packageStats,
217             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
218         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
219             throw new IllegalArgumentException("System server dexopting should be done via "
220                     + " DexManager and PackageDexOptimizer#dexoptSystemServerPath");
221         }
222         if (pkg.getUid() == -1) {
223             throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName()
224                     + " has invalid uid.");
225         }
226         if (!canOptimizePackage(pkg)) {
227             return DEX_OPT_SKIPPED;
228         }
229         synchronized (mInstallLock) {
230             final long acquireTime = acquireWakeLockLI(pkg.getUid());
231             try {
232                 return performDexOptLI(pkg, pkgSetting, instructionSets,
233                         packageStats, packageUseInfo, options);
234             } finally {
235                 releaseWakeLockLI(acquireTime);
236             }
237         }
238     }
239 
240     /**
241      * Cancels currently running dex optimization.
242      */
controlDexOptBlocking(boolean block)243     void controlDexOptBlocking(boolean block) {
244         // This method should not hold mInstallLock as cancelling should be possible while
245         // the lock is held by other thread running performDexOpt.
246         getInstallerWithoutLock().controlDexOptBlocking(block);
247     }
248 
249     /**
250      * Performs dexopt on all code paths of the given package.
251      * It assumes the install lock is held.
252      */
253     @GuardedBy("mInstallLock")
254     @DexOptResult
performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)255     private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
256             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
257             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
258         // ClassLoader only refers non-native (jar) shared libraries and must ignore
259         // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
260         final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
261                 .getNonNativeUsesLibraryInfos();
262         final String[] instructionSets = targetInstructionSets != null ?
263                 targetInstructionSets : getAppDexInstructionSets(
264                 AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
265                 AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
266         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
267         final List<String> paths = AndroidPackageUtils.getAllCodePaths(pkg);
268 
269         int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
270         if (sharedGid == -1) {
271             Slog.wtf(TAG, "Well this is awkward; package " + pkg.getPackageName() + " had UID "
272                     + pkg.getUid(), new Throwable());
273             sharedGid = android.os.Process.NOBODY_UID;
274         }
275 
276         // Get the class loader context dependencies.
277         // For each code path in the package, this array contains the class loader context that
278         // needs to be passed to dexopt in order to ensure correct optimizations.
279         boolean[] pathsWithCode = new boolean[paths.size()];
280         pathsWithCode[0] = pkg.isHasCode();
281         for (int i = 1; i < paths.size(); i++) {
282             pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
283         }
284         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
285                 pkg, sharedLibraries, pathsWithCode);
286 
287         // Validity check that we do not call dexopt with inconsistent data.
288         if (paths.size() != classLoaderContexts.length) {
289             String[] splitCodePaths = pkg.getSplitCodePaths();
290             throw new IllegalStateException("Inconsistent information "
291                 + "between AndroidPackage and its ApplicationInfo. "
292                 + "pkg.getAllCodePaths=" + paths
293                 + " pkg.getBaseCodePath=" + pkg.getBaseApkPath()
294                 + " pkg.getSplitCodePaths="
295                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
296         }
297 
298         int result = DEX_OPT_SKIPPED;
299         for (int i = 0; i < paths.size(); i++) {
300             // Skip paths that have no code.
301             if (!pathsWithCode[i]) {
302                 continue;
303             }
304             if (classLoaderContexts[i] == null) {
305                 throw new IllegalStateException("Inconsistent information in the "
306                         + "package structure. A split is marked to contain code "
307                         + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
308             }
309 
310             // Append shared libraries with split dependencies for this split.
311             String path = paths.get(i);
312             if (options.getSplitName() != null) {
313                 // We are asked to compile only a specific split. Check that the current path is
314                 // what we are looking for.
315                 if (!options.getSplitName().equals(new File(path).getName())) {
316                     continue;
317                 }
318             }
319 
320             String profileName = ArtManager.getProfileName(
321                     i == 0 ? null : pkg.getSplitNames()[i - 1]);
322             final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
323                     || packageUseInfo.isUsedByOtherApps(path);
324             String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
325             // If the app is used by other apps, we must not use the existing profile because it
326             // may contain user data, unless the profile is newly created on install.
327             final boolean useCloudProfile = isProfileGuidedCompilerFilter(compilerFilter)
328                     && isUsedByOtherApps
329                     && options.getCompilationReason() != PackageManagerService.REASON_INSTALL;
330 
331             String dexMetadataPath = null;
332             if (options.isDexoptInstallWithDexMetadata() || useCloudProfile) {
333                 File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
334                 dexMetadataPath = dexMetadataFile == null
335                         ? null : dexMetadataFile.getAbsolutePath();
336             }
337 
338             // If we don't have to check for profiles updates assume
339             // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
340             // profiles.
341             int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
342             if (options.isCheckForProfileUpdates()) {
343                 profileAnalysisResult =
344                         analyseProfiles(pkg, sharedGid, profileName, compilerFilter);
345             }
346             String cloudProfileName = null;
347             try {
348                 if (useCloudProfile) {
349                     cloudProfileName = "cloud-" + profileName;
350                     if (prepareCloudProfile(pkg, cloudProfileName, path, dexMetadataPath)) {
351                         profileName = cloudProfileName;
352                     } else {
353                         // Fall back to use the shared filter.
354                         compilerFilter =
355                                 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
356                                         PackageManagerService.REASON_SHARED);
357                         profileName = null;
358                     }
359 
360                     // We still run `analyseProfiles` even if `useCloudProfile` is true because it
361                     // merges profiles into the reference profile, which a system API
362                     // `ArtManager.snapshotRuntimeProfile` takes snapshots from. However, we don't
363                     // want the result to affect the decision of whether dexopt is needed.
364                     profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
365                 }
366 
367                 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
368                 // flags.
369                 final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter,
370                         useCloudProfile, options);
371 
372                 for (String dexCodeIsa : dexCodeInstructionSets) {
373                     int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
374                             profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
375                             packageStats, options.isDowngrade(), profileName, dexMetadataPath,
376                             options.getCompilationReason());
377                     // OTAPreopt doesn't have stats so don't report in that case.
378                     if (packageStats != null) {
379                         Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
380                         try {
381                             long sessionId = sRandom.nextLong();
382                             ArtStatsLogUtils.writeStatsLog(
383                                     mArtStatsLogger,
384                                     sessionId,
385                                     compilerFilter,
386                                     pkg.getUid(),
387                                     packageStats.getCompileTime(path),
388                                     dexMetadataPath,
389                                     options.getCompilationReason(),
390                                     newResult,
391                                     ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(),
392                                             pkg.getSplitCodePaths()),
393                                     dexCodeIsa,
394                                     path);
395                         } finally {
396                             Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
397                         }
398                     }
399 
400                     // Should stop the operation immediately.
401                     if (newResult == DEX_OPT_CANCELLED) {
402                         // Even for the cancellation, return failed if has failed.
403                         if (result == DEX_OPT_FAILED) {
404                             return result;
405                         }
406                         return newResult;
407                     }
408                     // The end result is:
409                     //  - FAILED if any path failed,
410                     //  - PERFORMED if at least one path needed compilation,
411                     //  - SKIPPED when all paths are up to date
412                     if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
413                         result = newResult;
414                     }
415                 }
416             } finally {
417                 if (cloudProfileName != null) {
418                     try {
419                         mInstaller.deleteReferenceProfile(pkg.getPackageName(), cloudProfileName);
420                     } catch (InstallerException e) {
421                         Slog.w(TAG, "Failed to cleanup cloud profile", e);
422                     }
423                 }
424             }
425         }
426         return result;
427     }
428 
429     /**
430      * Creates a profile with the name {@code profileName} from the dex metadata file at {@code
431      * dexMetadataPath} for the dex file at {@code path} belonging to the package {@code pkg}.
432      *
433      * @return true on success, or false otherwise.
434      */
435     @GuardedBy("mInstallLock")
prepareCloudProfile(AndroidPackage pkg, String profileName, String path, @Nullable String dexMetadataPath)436     private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
437             @Nullable String dexMetadataPath) {
438         if (dexMetadataPath != null) {
439             try {
440                 // Make sure we don't keep any existing contents.
441                 mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName);
442 
443                 final int appId = UserHandle.getAppId(pkg.getUid());
444                 mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, appId,
445                         profileName, path, dexMetadataPath);
446                 return true;
447             } catch (InstallerException e) {
448                 Slog.w(TAG, "Failed to prepare cloud profile", e);
449                 return false;
450             }
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) {
471         String oatDir = getPackageOatDirIfSupported(pkg,
472                 pkgSetting.getTransientState().isUpdatedSystemApp());
473 
474         int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
475                 classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir);
476         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
477             return DEX_OPT_SKIPPED;
478         }
479 
480         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
481                 + " pkg=" + pkg.getPackageName() + " isa=" + isa
482                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
483                 + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
484                 + " classLoaderContext=" + classLoaderContext);
485 
486         try {
487             long startTime = System.currentTimeMillis();
488 
489             // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
490             // installd only uses downgrade flag for secondary dex files and ignores it for
491             // primary dex files.
492             String seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting);
493             boolean completed = getInstallerLI().dexopt(path, uid, pkg.getPackageName(), isa,
494                     dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.getVolumeUuid(),
495                     classLoaderContext, seInfo, /* downgrade= */ false ,
496                     pkg.getTargetSdkVersion(), profileName, dexMetadataPath,
497                     getAugmentedReasonName(compilationReason, dexMetadataPath != null));
498             if (!completed) {
499                 return DEX_OPT_CANCELLED;
500             }
501             if (packageStats != null) {
502                 long endTime = System.currentTimeMillis();
503                 packageStats.setCompileTime(path, (int)(endTime - startTime));
504             }
505             if (oatDir != null) {
506                 // Release odex/vdex compressed blocks to save user space.
507                 // Compression support will be checked in F2fsUtils.
508                 // The system app may be dexed, oatDir may be null, skip this situation.
509                 final ContentResolver resolver = mContext.getContentResolver();
510                 F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
511             }
512             return DEX_OPT_PERFORMED;
513         } catch (InstallerException e) {
514             Slog.w(TAG, "Failed to dexopt", e);
515             return DEX_OPT_FAILED;
516         }
517     }
518 
519     /**
520      * Perform dexopt (if needed) on a system server code path).
521      */
522     @GuardedBy("mInstallLock")
523     @DexOptResult
dexoptSystemServerPath( String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)524     public int dexoptSystemServerPath(
525             String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
526         int dexoptFlags = DEXOPT_PUBLIC
527                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
528                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
529 
530         int result = DEX_OPT_SKIPPED;
531         for (String isa : dexUseInfo.getLoaderIsas()) {
532             int dexoptNeeded = getDexoptNeeded(
533                     PackageManagerService.PLATFORM_PACKAGE_NAME,
534                     dexPath,
535                     isa,
536                     options.getCompilerFilter(),
537                     dexUseInfo.getClassLoaderContext(),
538                     PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES,
539                     /* downgrade= */ false,
540                     dexoptFlags,
541                     /* oatDir= */ null);
542 
543             if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
544                 continue;
545             }
546             try {
547                 synchronized (mInstallLock) {
548                     boolean completed = getInstallerLI().dexopt(
549                             dexPath,
550                             android.os.Process.SYSTEM_UID,
551                             /* pkgName= */ "android",
552                             isa,
553                             dexoptNeeded,
554                             /* outputPath= */ null,
555                             dexoptFlags,
556                             options.getCompilerFilter(),
557                             StorageManager.UUID_PRIVATE_INTERNAL,
558                             dexUseInfo.getClassLoaderContext(),
559                             /* seInfo= */ null,
560                             /* downgrade= */ false,
561                             /* targetSdkVersion= */ 0,
562                             /* profileName= */ null,
563                             /* dexMetadataPath= */ null,
564                             getReasonName(options.getCompilationReason()));
565                     if (!completed) {
566                         return DEX_OPT_CANCELLED;
567                     }
568                 }
569             } catch (InstallerException e) {
570                 Slog.w(TAG, "Failed to dexopt", e);
571                 return DEX_OPT_FAILED;
572             }
573             result = DEX_OPT_PERFORMED;
574         }
575         return result;
576     }
577 
getAugmentedReasonName(int compilationReason, boolean useDexMetadata)578     private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
579         String annotation = useDexMetadata
580                 ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
581         return getReasonName(compilationReason) + annotation;
582     }
583 
584     /**
585      * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
586      *
587      * @return
588      *      DEX_OPT_FAILED if there was any exception during dexopt
589      *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
590      * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
591      * didn't need an update. That's because at the moment we don't get more than success/failure
592      * from installd.
593      *
594      * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
595      * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
596      * that seems wasteful.
597      */
598     @DexOptResult
dexOptSecondaryDexPath(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)599     public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
600             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
601         if (info.uid == -1) {
602             throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid.");
603         }
604         synchronized (mInstallLock) {
605             final long acquireTime = acquireWakeLockLI(info.uid);
606             try {
607                 return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
608             } finally {
609                 releaseWakeLockLI(acquireTime);
610             }
611         }
612     }
613 
614     @GuardedBy("mInstallLock")
acquireWakeLockLI(final int uid)615     private long acquireWakeLockLI(final int uid) {
616         // During boot the system doesn't need to instantiate and obtain a wake lock.
617         // PowerManager might not be ready, but that doesn't mean that we can't proceed with
618         // dexopt.
619         if (!mSystemReady) {
620             return -1;
621         }
622         mDexoptWakeLock.setWorkSource(new WorkSource(uid));
623         mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
624         return SystemClock.elapsedRealtime();
625     }
626 
627     @GuardedBy("mInstallLock")
releaseWakeLockLI(final long acquireTime)628     private void releaseWakeLockLI(final long acquireTime) {
629         if (acquireTime < 0) {
630             return;
631         }
632         try {
633             if (mDexoptWakeLock.isHeld()) {
634                 mDexoptWakeLock.release();
635             }
636             final long duration = SystemClock.elapsedRealtime() - acquireTime;
637             if (duration >= WAKELOCK_TIMEOUT_MS) {
638                 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
639                         + " time out. Operation took " + duration + " ms. Thread: "
640                         + Thread.currentThread().getName());
641             }
642         } catch (Exception e) {
643             Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
644         }
645     }
646 
647     @GuardedBy("mInstallLock")
648     @DexOptResult
dexOptSecondaryDexPathLI(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)649     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
650             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
651         if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
652             // We are asked to optimize only the dex files used by other apps and this is not
653             // on of them: skip it.
654             return DEX_OPT_SKIPPED;
655         }
656 
657         String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
658                 dexUseInfo.isUsedByOtherApps());
659         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
660         // Secondary dex files are currently not compiled at boot.
661         int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
662         // Check the app storage and add the appropriate flags.
663         if (info.deviceProtectedDataDir != null &&
664                 FileUtils.contains(info.deviceProtectedDataDir, path)) {
665             dexoptFlags |= DEXOPT_STORAGE_DE;
666         } else if (info.credentialProtectedDataDir != null &&
667                 FileUtils.contains(info.credentialProtectedDataDir, path)) {
668             dexoptFlags |= DEXOPT_STORAGE_CE;
669         } else {
670             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
671             return DEX_OPT_FAILED;
672         }
673         String classLoaderContext = null;
674         if (dexUseInfo.isUnsupportedClassLoaderContext()
675                 || dexUseInfo.isVariableClassLoaderContext()) {
676             // If we have an unknown (not yet set), or a variable class loader chain. Just verify
677             // the dex file.
678             compilerFilter = "verify";
679         } else {
680             classLoaderContext = dexUseInfo.getClassLoaderContext();
681         }
682 
683         int reason = options.getCompilationReason();
684         Log.d(TAG, "Running dexopt on: " + path
685                 + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
686                 + " reason=" + getReasonName(reason)
687                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
688                 + " target-filter=" + compilerFilter
689                 + " class-loader-context=" + classLoaderContext);
690 
691         try {
692             for (String isa : dexUseInfo.getLoaderIsas()) {
693                 // Reuse the same dexopt path as for the primary apks. We don't need all the
694                 // arguments as some (dexopNeeded and oatDir) will be computed by installd because
695                 // system server cannot read untrusted app content.
696                 // TODO(calin): maybe add a separate call.
697                 boolean completed = getInstallerLI().dexopt(path, info.uid, info.packageName,
698                         isa, /* dexoptNeeded= */ 0,
699                         /* outputPath= */ null, dexoptFlags,
700                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfo,
701                         options.isDowngrade(), info.targetSdkVersion, /* profileName= */ null,
702                         /* dexMetadataPath= */ null, getReasonName(reason));
703                 if (!completed) {
704                     return DEX_OPT_CANCELLED;
705                 }
706             }
707 
708             return DEX_OPT_PERFORMED;
709         } catch (InstallerException e) {
710             Slog.w(TAG, "Failed to dexopt", e);
711             return DEX_OPT_FAILED;
712         }
713     }
714 
715     /**
716      * Adjust the given dexopt-needed value. Can be overridden to influence the decision to
717      * optimize or not (and in what way).
718      */
adjustDexoptNeeded(int dexoptNeeded)719     protected int adjustDexoptNeeded(int dexoptNeeded) {
720         return dexoptNeeded;
721     }
722 
723     /**
724      * Adjust the given dexopt flags that will be passed to the installer.
725      */
adjustDexoptFlags(int dexoptFlags)726     protected int adjustDexoptFlags(int dexoptFlags) {
727         return dexoptFlags;
728     }
729 
730     /**
731      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
732      */
dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo)733     void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
734             PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo) {
735         final String[] instructionSets = getAppDexInstructionSets(
736                 AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
737                 AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
738         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
739 
740         final List<String> paths = AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
741 
742         for (String path : paths) {
743             pw.println("path: " + path);
744             pw.increaseIndent();
745 
746             for (String isa : dexCodeInstructionSets) {
747                 try {
748                     DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa);
749                     pw.println(isa + ": [status=" + info.getStatus()
750                             +"] [reason=" + info.getReason() + "]");
751                 } catch (IOException ioe) {
752                     pw.println(isa + ": [Exception]: " + ioe.getMessage());
753                 }
754             }
755 
756             if (useInfo.isUsedByOtherApps(path)) {
757                 pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
758             }
759 
760             Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
761 
762             if (!dexUseInfoMap.isEmpty()) {
763                 pw.println("known secondary dex files:");
764                 pw.increaseIndent();
765                 for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
766                     String dex = e.getKey();
767                     PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
768                     pw.println(dex);
769                     pw.increaseIndent();
770                     // TODO(calin): get the status of the oat file (needs installd call)
771                     pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
772                     if (dexUseInfo.isUsedByOtherApps()) {
773                         pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
774                     }
775                     pw.decreaseIndent();
776                 }
777                 pw.decreaseIndent();
778             }
779             pw.decreaseIndent();
780         }
781     }
782 
783     /**
784      * Returns the compiler filter that should be used to optimize the secondary dex.
785      * The target filter will be updated if the package code is used by other apps
786      * or if it has the safe mode flag set.
787      */
getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps)788     private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
789             boolean isUsedByOtherApps) {
790         // When an app or priv app is configured to run out of box, only verify it.
791         if (info.isEmbeddedDexUsed()
792                 || (info.isPrivilegedApp()
793                 && DexManager.isPackageSelectedToRunOob(info.packageName))) {
794             return "verify";
795         }
796 
797         // We force vmSafeMode on debuggable apps as well:
798         //  - the runtime ignores their compiled code
799         //  - they generally have lots of methods that could make the compiler used run
800         //    out of memory (b/130828957)
801         // Note that forcing the compiler filter here applies to all compilations (even if they
802         // are done via adb shell commands). That's ok because right now the runtime will ignore
803         // the compiled code anyway. The alternative would have been to update either
804         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
805         // but that would have the downside of possibly producing a big odex files which would
806         // be ignored anyway.
807         boolean vmSafeModeOrDebuggable = ((info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0)
808                 || ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
809 
810         if (vmSafeModeOrDebuggable) {
811             return getSafeModeCompilerFilter(targetCompilerFilter);
812         }
813 
814         if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
815             // If the dex files is used by other apps, apply the shared filter.
816             return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
817                     PackageManagerService.REASON_SHARED);
818         }
819 
820         return targetCompilerFilter;
821     }
822 
823     /**
824      * Returns the compiler filter that should be used to optimize the primary dex.
825      * The target filter will be updated if the package has the safe mode flag set. Note that this
826      * method does NOT take other app use into account. The caller should be responsible for
827      * handling the case where the package code is used by other apps.
828      */
getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter)829     private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) {
830         // When an app or priv app is configured to run out of box, only verify it.
831         if (pkg.isUseEmbeddedDex()
832                 || (pkg.isPrivileged()
833                     && DexManager.isPackageSelectedToRunOob(pkg.getPackageName()))) {
834             return "verify";
835         }
836 
837         // We force vmSafeMode on debuggable apps as well:
838         //  - the runtime ignores their compiled code
839         //  - they generally have lots of methods that could make the compiler used run
840         //    out of memory (b/130828957)
841         // Note that forcing the compiler filter here applies to all compilations (even if they
842         // are done via adb shell commands). That's ok because right now the runtime will ignore
843         // the compiled code anyway. The alternative would have been to update either
844         // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
845         // but that would have the downside of possibly producing a big odex files which would
846         // be ignored anyway.
847         boolean vmSafeModeOrDebuggable = pkg.isVmSafeMode() || pkg.isDebuggable();
848 
849         if (vmSafeModeOrDebuggable) {
850             return getSafeModeCompilerFilter(targetCompilerFilter);
851         }
852 
853         return targetCompilerFilter;
854     }
855 
isAppImageEnabled()856     private boolean isAppImageEnabled() {
857         return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
858     }
859 
getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options)860     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
861         return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
862                 info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
863                 info.requestsIsolatedSplitLoading(), compilerFilter, false /* useCloudProfile */,
864                 options);
865     }
866 
getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, String compilerFilter, boolean useCloudProfile, DexoptOptions options)867     private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
868             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
869         return getDexFlags(pkg.isDebuggable(),
870                 AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
871                 pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
872                 useCloudProfile, options);
873     }
874 
875     /**
876      * Computes the dex flags that needs to be pass to installd for the given package and compiler
877      * filter.
878      */
getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, String compilerFilter, boolean useCloudProfile, DexoptOptions options)879     private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
880             SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
881             String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
882         // Profile guide compiled oat files should not be public unles they are based
883         // on profiles from dex metadata archives.
884         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
885         // the user does not have an existing profile.
886         // The flag useCloudProfile applies only when the cloud profile should be used.
887         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
888         boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()
889                 || useCloudProfile;
890         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
891         // Some apps are executed with restrictions on hidden API usage. If this app is one
892         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
893         // TODO we should pass the actual flag value to dexopt, rather than assuming denylist
894         // TODO(b/135203078): This flag is no longer set as part of AndroidPackage
895         //  and may not be preserved
896         int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED
897                 ? 0
898                 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
899         // Avoid generating CompactDex for modes that are latency critical.
900         final int compilationReason = options.getCompilationReason();
901         boolean generateCompactDex = true;
902         switch (compilationReason) {
903             case PackageManagerService.REASON_FIRST_BOOT:
904             case PackageManagerService.REASON_BOOT_AFTER_OTA:
905             case PackageManagerService.REASON_POST_BOOT:
906             case PackageManagerService.REASON_INSTALL:
907                  generateCompactDex = false;
908         }
909         // Use app images only if it is enabled and we are compiling
910         // profile-guided (so the app image doesn't conservatively contain all classes).
911         // If the app didn't request for the splits to be loaded in isolation or if it does not
912         // declare inter-split dependencies, then all the splits will be loaded in the base
913         // apk class loader (in the order of their definition, otherwise disable app images
914         // because they are unsupported for multiple class loaders. b/7269679
915         boolean generateAppImage = isProfileGuidedFilter && (splitDependencies == null ||
916                 !requestsIsolatedSplitLoading) && isAppImageEnabled();
917         int dexFlags =
918                 (isPublic ? DEXOPT_PUBLIC : 0)
919                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
920                 | profileFlag
921                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
922                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
923                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
924                 | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
925                 | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
926                 | hiddenApiFlag;
927         return adjustDexoptFlags(dexFlags);
928     }
929 
930     /**
931      * Assesses if there's a need to perform dexopt on {@code path} for the given
932      * configuration (isa, compiler filter, profile).
933      */
934     @GuardedBy("mInstallLock")
getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade, int dexoptFlags, String oatDir)935     private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
936             String classLoaderContext, int profileAnalysisResult, boolean downgrade,
937             int dexoptFlags, String oatDir) {
938         final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
939         final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
940         boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
941 
942         if (!newProfile && isProfileGuidedFilter && shouldBePublic
943                 && isOdexPrivate(packageName, path, isa, oatDir)) {
944             // The profile that will be used is a cloud profile, while the profile used previously
945             // is a user profile. Typically, this happens after an app starts being used by other
946             // apps.
947             newProfile = true;
948         }
949 
950         int dexoptNeeded;
951         try {
952             // A profile guided optimizations with an empty profile is essentially 'verify' and
953             // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot
954             // check the profiles because system server does not have access to them.
955             // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and
956             // manually adjust the actual filter before checking.
957             //
958             // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change,
959             // and in the interim we can still improve things here.
960             String actualCompilerFilter = compilerFilter;
961             if (compilerFilterDependsOnProfiles(compilerFilter)
962                     && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) {
963                 actualCompilerFilter = "verify";
964             }
965             dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter,
966                     classLoaderContext, newProfile, downgrade);
967         } catch (IOException ioe) {
968             Slog.w(TAG, "IOException reading apk: " + path, ioe);
969             return DEX_OPT_FAILED;
970         } catch (Exception e) {
971             Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e);
972             return DEX_OPT_FAILED;
973         }
974         return adjustDexoptNeeded(dexoptNeeded);
975     }
976 
977     /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */
compilerFilterDependsOnProfiles(String compilerFilter)978     private boolean compilerFilterDependsOnProfiles(String compilerFilter) {
979         return compilerFilter.endsWith("-profile");
980     }
981 
982     /** Returns true if the current artifacts of the app are private to the app itself. */
983     @GuardedBy("mInstallLock")
isOdexPrivate(String packageName, String path, String isa, String oatDir)984     private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) {
985         try {
986             return mInstaller.getOdexVisibility(packageName, path, isa, oatDir)
987                     == Installer.ODEX_IS_PRIVATE;
988         } catch (Exception e) {
989             Slog.w(TAG, "Failed to get odex visibility for " + path, e);
990             return false;
991         }
992     }
993 
994     /**
995      * Checks if there is an update on the profile information of the {@code pkg}.
996      * If the compiler filter is not profile guided the method returns a safe default:
997      * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA.
998      *
999      * Note that this is a "destructive" operation with side effects. Under the hood the
1000      * current profile and the reference profile will be merged and subsequent calls
1001      * may return a different result.
1002      */
analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter)1003     private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
1004             String compilerFilter) {
1005         // Check if we are allowed to merge and if the compiler filter is profile guided.
1006         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
1007             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
1008         }
1009         // Merge profiles. It returns whether or not there was an updated in the profile info.
1010         try {
1011             synchronized (mInstallLock) {
1012                 return getInstallerLI().mergeProfiles(uid, pkg.getPackageName(), profileName);
1013             }
1014         } catch (InstallerException e) {
1015             Slog.w(TAG, "Failed to merge profiles", e);
1016             // We don't need to optimize if we failed to merge.
1017             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
1018         }
1019     }
1020 
1021     /**
1022      * Gets oat dir for the specified package if needed and supported.
1023      * In certain cases oat directory
1024      * <strong>cannot</strong> be created:
1025      * <ul>
1026      *      <li>{@code pkg} is a system app, which is not updated.</li>
1027      *      <li>Package location is not a directory, i.e. monolithic install.</li>
1028      * </ul>
1029      *
1030      * @return Absolute path to the oat directory or null, if oat directories
1031      * not needed or unsupported for the package.
1032      */
1033     @Nullable
getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp)1034     private String getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp) {
1035         if (!AndroidPackageUtils.canHaveOatDir(pkg, isUpdatedSystemApp)) {
1036             return null;
1037         }
1038         File codePath = new File(pkg.getPath());
1039         if (!codePath.isDirectory()) {
1040             return null;
1041         }
1042         return getOatDir(codePath).getAbsolutePath();
1043     }
1044 
1045     /** Returns the oat dir for the given code path */
getOatDir(File codePath)1046     public static File getOatDir(File codePath) {
1047         return new File(codePath, OAT_DIR_NAME);
1048     }
1049 
systemReady()1050     void systemReady() {
1051         mSystemReady = true;
1052     }
1053 
printDexoptFlags(int flags)1054     private String printDexoptFlags(int flags) {
1055         ArrayList<String> flagsList = new ArrayList<>();
1056 
1057         if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) {
1058             flagsList.add("boot_complete");
1059         }
1060         if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) {
1061             flagsList.add("debuggable");
1062         }
1063         if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) {
1064             flagsList.add("profile_guided");
1065         }
1066         if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) {
1067             flagsList.add("public");
1068         }
1069         if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
1070             flagsList.add("secondary");
1071         }
1072         if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
1073             flagsList.add("force");
1074         }
1075         if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
1076             flagsList.add("storage_ce");
1077         }
1078         if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
1079             flagsList.add("storage_de");
1080         }
1081         if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
1082             flagsList.add("idle_background_job");
1083         }
1084         if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
1085             flagsList.add("enable_hidden_api_checks");
1086         }
1087 
1088         return String.join(",", flagsList);
1089     }
1090 
1091     /**
1092      * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a
1093      * dexopt path.
1094      */
1095     public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer {
1096 
ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)1097         public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock,
1098                 Context context, String wakeLockTag) {
1099             super(installer, installLock, context, wakeLockTag);
1100         }
1101 
ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from)1102         public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) {
1103             super(from);
1104         }
1105 
1106         @Override
adjustDexoptNeeded(int dexoptNeeded)1107         protected int adjustDexoptNeeded(int dexoptNeeded) {
1108             if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
1109                 // Ensure compilation by pretending a compiler filter change on the
1110                 // apk/odex location (the reason for the '-'. A positive value means
1111                 // the 'oat' location).
1112                 return -DexFile.DEX2OAT_FOR_FILTER;
1113             }
1114             return dexoptNeeded;
1115         }
1116 
1117         @Override
adjustDexoptFlags(int flags)1118         protected int adjustDexoptFlags(int flags) {
1119             // Add DEXOPT_FORCE flag to signal installd that it should force compilation
1120             // and discard dexoptanalyzer result.
1121             return flags | DEXOPT_FORCE;
1122         }
1123     }
1124 
1125     /**
1126      * Returns {@link #mInstaller} with {@link #mInstallLock}. This should be used for all
1127      * {@link #mInstaller} access unless {@link #getInstallerWithoutLock()} is allowed.
1128      */
1129     @GuardedBy("mInstallLock")
getInstallerLI()1130     private Installer getInstallerLI() {
1131         return mInstaller;
1132     }
1133 
1134     /**
1135      * Returns {@link #mInstaller} without lock. This should be used only inside
1136      * {@link #controlDexOptBlocking(boolean)}.
1137      */
getInstallerWithoutLock()1138     private Installer getInstallerWithoutLock() {
1139         return mInstaller;
1140     }
1141 
1142     /**
1143      * Injector for {@link PackageDexOptimizer} dependencies
1144      */
1145     interface Injector {
getAppHibernationManagerInternal()1146         AppHibernationManagerInternal getAppHibernationManagerInternal();
1147 
getPowerManager(Context context)1148         PowerManager getPowerManager(Context context);
1149     }
1150 }
1151