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