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