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