• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.pm;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageParser;
23 import android.os.FileUtils;
24 import android.os.PowerManager;
25 import android.os.SystemClock;
26 import android.os.UserHandle;
27 import android.os.WorkSource;
28 import android.util.Log;
29 import android.util.Slog;
30 
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.internal.util.IndentingPrintWriter;
33 import com.android.server.pm.Installer.InstallerException;
34 import com.android.server.pm.dex.DexManager;
35 import com.android.server.pm.dex.DexoptOptions;
36 import com.android.server.pm.dex.DexoptUtils;
37 import com.android.server.pm.dex.PackageDexUsage;
38 
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.List;
44 import java.util.Map;
45 
46 import dalvik.system.DexFile;
47 
48 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
49 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
50 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
51 import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
52 import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
53 import static com.android.server.pm.Installer.DEXOPT_FORCE;
54 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
55 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
56 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
57 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
58 
59 import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
60 
61 import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
62 import static dalvik.system.DexFile.getSafeModeCompilerFilter;
63 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
64 
65 /**
66  * Helper class for running dexopt command on packages.
67  */
68 public class PackageDexOptimizer {
69     private static final String TAG = "PackageManager.DexOptimizer";
70     static final String OAT_DIR_NAME = "oat";
71     // TODO b/19550105 Remove error codes and use exceptions
72     public static final int DEX_OPT_SKIPPED = 0;
73     public static final int DEX_OPT_PERFORMED = 1;
74     public static final int DEX_OPT_FAILED = -1;
75     // One minute over PM WATCHDOG_TIMEOUT
76     private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
77 
78     /** Special library name that skips shared libraries check during compilation. */
79     public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
80 
81     @GuardedBy("mInstallLock")
82     private final Installer mInstaller;
83     private final Object mInstallLock;
84 
85     @GuardedBy("mInstallLock")
86     private final PowerManager.WakeLock mDexoptWakeLock;
87     private volatile boolean mSystemReady;
88 
PackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)89     PackageDexOptimizer(Installer installer, Object installLock, Context context,
90             String wakeLockTag) {
91         this.mInstaller = installer;
92         this.mInstallLock = installLock;
93 
94         PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
95         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
96     }
97 
PackageDexOptimizer(PackageDexOptimizer from)98     protected PackageDexOptimizer(PackageDexOptimizer from) {
99         this.mInstaller = from.mInstaller;
100         this.mInstallLock = from.mInstallLock;
101         this.mDexoptWakeLock = from.mDexoptWakeLock;
102         this.mSystemReady = from.mSystemReady;
103     }
104 
canOptimizePackage(PackageParser.Package pkg)105     static boolean canOptimizePackage(PackageParser.Package pkg) {
106         return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
107     }
108 
109     /**
110      * Performs dexopt on all code paths and libraries of the specified package for specified
111      * instruction sets.
112      *
113      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
114      * synchronized on {@link #mInstallLock}.
115      */
performDexOpt(PackageParser.Package pkg, String[] sharedLibraries, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)116     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
117             String[] instructionSets, CompilerStats.PackageStats packageStats,
118             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
119         if (!canOptimizePackage(pkg)) {
120             return DEX_OPT_SKIPPED;
121         }
122         synchronized (mInstallLock) {
123             final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
124             try {
125                 return performDexOptLI(pkg, sharedLibraries, instructionSets,
126                         packageStats, packageUseInfo, options);
127             } finally {
128                 releaseWakeLockLI(acquireTime);
129             }
130         }
131     }
132 
133     /**
134      * Performs dexopt on all code paths of the given package.
135      * It assumes the install lock is held.
136      */
137     @GuardedBy("mInstallLock")
performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)138     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
139             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
140             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
141         final String[] instructionSets = targetInstructionSets != null ?
142                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
143         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
144         final List<String> paths = pkg.getAllCodePaths();
145         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
146 
147         // Get the class loader context dependencies.
148         // For each code path in the package, this array contains the class loader context that
149         // needs to be passed to dexopt in order to ensure correct optimizations.
150         boolean[] pathsWithCode = new boolean[paths.size()];
151         pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
152         for (int i = 1; i < paths.size(); i++) {
153             pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
154         }
155         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
156                 pkg.applicationInfo, sharedLibraries, pathsWithCode);
157 
158         // Sanity check that we do not call dexopt with inconsistent data.
159         if (paths.size() != classLoaderContexts.length) {
160             String[] splitCodePaths = pkg.applicationInfo.getSplitCodePaths();
161             throw new IllegalStateException("Inconsistent information "
162                 + "between PackageParser.Package and its ApplicationInfo. "
163                 + "pkg.getAllCodePaths=" + paths
164                 + " pkg.applicationInfo.getBaseCodePath=" + pkg.applicationInfo.getBaseCodePath()
165                 + " pkg.applicationInfo.getSplitCodePaths="
166                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
167         }
168 
169         int result = DEX_OPT_SKIPPED;
170         for (int i = 0; i < paths.size(); i++) {
171             // Skip paths that have no code.
172             if (!pathsWithCode[i]) {
173                 continue;
174             }
175             if (classLoaderContexts[i] == null) {
176                 throw new IllegalStateException("Inconsistent information in the "
177                         + "package structure. A split is marked to contain code "
178                         + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
179             }
180 
181             // Append shared libraries with split dependencies for this split.
182             String path = paths.get(i);
183             if (options.getSplitName() != null) {
184                 // We are asked to compile only a specific split. Check that the current path is
185                 // what we are looking for.
186                 if (!options.getSplitName().equals(new File(path).getName())) {
187                     continue;
188                 }
189             }
190 
191             final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
192                     || packageUseInfo.isUsedByOtherApps(path);
193             final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
194                 options.getCompilerFilter(), isUsedByOtherApps);
195             final boolean profileUpdated = options.isCheckForProfileUpdates() &&
196                 isProfileUpdated(pkg, sharedGid, compilerFilter);
197 
198             // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
199             // flags.
200             final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
201 
202             for (String dexCodeIsa : dexCodeInstructionSets) {
203                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
204                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
205                         packageStats, options.isDowngrade());
206                 // The end result is:
207                 //  - FAILED if any path failed,
208                 //  - PERFORMED if at least one path needed compilation,
209                 //  - SKIPPED when all paths are up to date
210                 if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
211                     result = newResult;
212                 }
213             }
214         }
215         return result;
216     }
217 
218     /**
219      * Performs dexopt on the {@code path} belonging to the package {@code pkg}.
220      *
221      * @return
222      *      DEX_OPT_FAILED if there was any exception during dexopt
223      *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
224      *      DEX_OPT_SKIPPED if the path does not need to be deopt-ed.
225      */
226     @GuardedBy("mInstallLock")
dexOptPath(PackageParser.Package pkg, String path, String isa, String compilerFilter, boolean profileUpdated, String sharedLibrariesPath, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade)227     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
228             String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
229             int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
230         int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
231         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
232             return DEX_OPT_SKIPPED;
233         }
234 
235         // TODO(calin): there's no need to try to create the oat dir over and over again,
236         //              especially since it involve an extra installd call. We should create
237         //              if (if supported) on the fly during the dexopt call.
238         String oatDir = createOatDirIfSupported(pkg, isa);
239 
240         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
241                 + " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
242                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
243                 + " target-filter=" + compilerFilter + " oatDir=" + oatDir
244                 + " sharedLibraries=" + sharedLibrariesPath);
245 
246         try {
247             long startTime = System.currentTimeMillis();
248 
249             // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
250             // installd only uses downgrade flag for secondary dex files and ignores it for
251             // primary dex files.
252             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
253                     compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
254                     false /* downgrade*/);
255 
256             if (packageStats != null) {
257                 long endTime = System.currentTimeMillis();
258                 packageStats.setCompileTime(path, (int)(endTime - startTime));
259             }
260             return DEX_OPT_PERFORMED;
261         } catch (InstallerException e) {
262             Slog.w(TAG, "Failed to dexopt", e);
263             return DEX_OPT_FAILED;
264         }
265     }
266 
267     /**
268      * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
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      * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
274      * didn't need an update. That's because at the moment we don't get more than success/failure
275      * from installd.
276      *
277      * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
278      * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
279      * that seems wasteful.
280      */
dexOptSecondaryDexPath(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)281     public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
282             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
283         synchronized (mInstallLock) {
284             final long acquireTime = acquireWakeLockLI(info.uid);
285             try {
286                 return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
287             } finally {
288                 releaseWakeLockLI(acquireTime);
289             }
290         }
291     }
292 
293     @GuardedBy("mInstallLock")
acquireWakeLockLI(final int uid)294     private long acquireWakeLockLI(final int uid) {
295         // During boot the system doesn't need to instantiate and obtain a wake lock.
296         // PowerManager might not be ready, but that doesn't mean that we can't proceed with
297         // dexopt.
298         if (!mSystemReady) {
299             return -1;
300         }
301         mDexoptWakeLock.setWorkSource(new WorkSource(uid));
302         mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
303         return SystemClock.elapsedRealtime();
304     }
305 
306     @GuardedBy("mInstallLock")
releaseWakeLockLI(final long acquireTime)307     private void releaseWakeLockLI(final long acquireTime) {
308         if (acquireTime < 0) {
309             return;
310         }
311         try {
312             if (mDexoptWakeLock.isHeld()) {
313                 mDexoptWakeLock.release();
314             }
315             final long duration = SystemClock.elapsedRealtime() - acquireTime;
316             if (duration >= WAKELOCK_TIMEOUT_MS) {
317                 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
318                         + " time out. Operation took " + duration + " ms. Thread: "
319                         + Thread.currentThread().getName());
320             }
321         } catch (Exception e) {
322             Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
323         }
324     }
325 
326     @GuardedBy("mInstallLock")
dexOptSecondaryDexPathLI(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)327     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
328             PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
329         if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
330             // We are asked to optimize only the dex files used by other apps and this is not
331             // on of them: skip it.
332             return DEX_OPT_SKIPPED;
333         }
334 
335         String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
336                 dexUseInfo.isUsedByOtherApps());
337         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
338         // Secondary dex files are currently not compiled at boot.
339         int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
340                 | DEXOPT_SECONDARY_DEX;
341         // Check the app storage and add the appropriate flags.
342         if (info.deviceProtectedDataDir != null &&
343                 FileUtils.contains(info.deviceProtectedDataDir, path)) {
344             dexoptFlags |= DEXOPT_STORAGE_DE;
345         } else if (info.credentialProtectedDataDir != null &&
346                 FileUtils.contains(info.credentialProtectedDataDir, path)) {
347             dexoptFlags |= DEXOPT_STORAGE_CE;
348         } else {
349             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
350             return DEX_OPT_FAILED;
351         }
352         Log.d(TAG, "Running dexopt on: " + path
353                 + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
354                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
355                 + " target-filter=" + compilerFilter);
356 
357         // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
358         // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
359         // in isolation (and avoid to extract/verify the main apk if it's in the class path).
360         // Note this trades correctness for performance since the resulting slow down is
361         // unacceptable in some cases until b/64530081 is fixed.
362         String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
363 
364         try {
365             for (String isa : dexUseInfo.getLoaderIsas()) {
366                 // Reuse the same dexopt path as for the primary apks. We don't need all the
367                 // arguments as some (dexopNeeded and oatDir) will be computed by installd because
368                 // system server cannot read untrusted app content.
369                 // TODO(calin): maybe add a separate call.
370                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
371                         /*oatDir*/ null, dexoptFlags,
372                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
373                         options.isDowngrade());
374             }
375 
376             return DEX_OPT_PERFORMED;
377         } catch (InstallerException e) {
378             Slog.w(TAG, "Failed to dexopt", e);
379             return DEX_OPT_FAILED;
380         }
381     }
382 
383     /**
384      * Adjust the given dexopt-needed value. Can be overridden to influence the decision to
385      * optimize or not (and in what way).
386      */
adjustDexoptNeeded(int dexoptNeeded)387     protected int adjustDexoptNeeded(int dexoptNeeded) {
388         return dexoptNeeded;
389     }
390 
391     /**
392      * Adjust the given dexopt flags that will be passed to the installer.
393      */
adjustDexoptFlags(int dexoptFlags)394     protected int adjustDexoptFlags(int dexoptFlags) {
395         return dexoptFlags;
396     }
397 
398     /**
399      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
400      */
dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg, PackageDexUsage.PackageUseInfo useInfo)401     void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
402             PackageDexUsage.PackageUseInfo useInfo) {
403         final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
404         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
405 
406         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
407 
408         for (String path : paths) {
409             pw.println("path: " + path);
410             pw.increaseIndent();
411 
412             for (String isa : dexCodeInstructionSets) {
413                 String status = null;
414                 try {
415                     status = DexFile.getDexFileStatus(path, isa);
416                 } catch (IOException ioe) {
417                      status = "[Exception]: " + ioe.getMessage();
418                 }
419                 pw.println(isa + ": " + status);
420             }
421 
422             if (useInfo.isUsedByOtherApps(path)) {
423                 pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
424             }
425 
426             Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
427 
428             if (!dexUseInfoMap.isEmpty()) {
429                 pw.println("known secondary dex files:");
430                 pw.increaseIndent();
431                 for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
432                     String dex = e.getKey();
433                     PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
434                     pw.println(dex);
435                     pw.increaseIndent();
436                     // TODO(calin): get the status of the oat file (needs installd call)
437                     pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
438                     if (dexUseInfo.isUsedByOtherApps()) {
439                         pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
440                     }
441                     pw.decreaseIndent();
442                 }
443                 pw.decreaseIndent();
444             }
445             pw.decreaseIndent();
446         }
447     }
448 
449     /**
450      * Returns the compiler filter that should be used to optimize the package code.
451      * The target filter will be updated if the package code is used by other apps
452      * or if it has the safe mode flag set.
453      */
getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps)454     private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
455             boolean isUsedByOtherApps) {
456         int flags = info.flags;
457         boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
458         if (vmSafeMode) {
459             return getSafeModeCompilerFilter(targetCompilerFilter);
460         }
461 
462         if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
463             // If the dex files is used by other apps, apply the shared filter.
464             return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
465                     PackageManagerService.REASON_SHARED);
466         }
467 
468         return targetCompilerFilter;
469     }
470 
471     /**
472      * Computes the dex flags that needs to be pass to installd for the given package and compiler
473      * filter.
474      */
getDexFlags(PackageParser.Package pkg, String compilerFilter, boolean bootComplete)475     private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
476             boolean bootComplete) {
477         return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete);
478     }
479 
getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete)480     private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) {
481         int flags = info.flags;
482         boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
483         // Profile guide compiled oat files should not be public.
484         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
485         boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
486         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
487         int dexFlags =
488                 (isPublic ? DEXOPT_PUBLIC : 0)
489                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
490                 | profileFlag
491                 | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
492         return adjustDexoptFlags(dexFlags);
493     }
494 
495     /**
496      * Assesses if there's a need to perform dexopt on {@code path} for the given
497      * configuration (isa, compiler filter, profile).
498      */
getDexoptNeeded(String path, String isa, String compilerFilter, boolean newProfile, boolean downgrade)499     private int getDexoptNeeded(String path, String isa, String compilerFilter,
500             boolean newProfile, boolean downgrade) {
501         int dexoptNeeded;
502         try {
503             dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
504                     downgrade);
505         } catch (IOException ioe) {
506             Slog.w(TAG, "IOException reading apk: " + path, ioe);
507             return DEX_OPT_FAILED;
508         }
509         return adjustDexoptNeeded(dexoptNeeded);
510     }
511 
512     /**
513      * Checks if there is an update on the profile information of the {@code pkg}.
514      * If the compiler filter is not profile guided the method returns false.
515      *
516      * Note that this is a "destructive" operation with side effects. Under the hood the
517      * current profile and the reference profile will be merged and subsequent calls
518      * may return a different result.
519      */
isProfileUpdated(PackageParser.Package pkg, int uid, String compilerFilter)520     private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String compilerFilter) {
521         // Check if we are allowed to merge and if the compiler filter is profile guided.
522         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
523             return false;
524         }
525         // Merge profiles. It returns whether or not there was an updated in the profile info.
526         try {
527             return mInstaller.mergeProfiles(uid, pkg.packageName);
528         } catch (InstallerException e) {
529             Slog.w(TAG, "Failed to merge profiles", e);
530         }
531         return false;
532     }
533 
534     /**
535      * Creates oat dir for the specified package if needed and supported.
536      * In certain cases oat directory
537      * <strong>cannot</strong> be created:
538      * <ul>
539      *      <li>{@code pkg} is a system app, which is not updated.</li>
540      *      <li>Package location is not a directory, i.e. monolithic install.</li>
541      * </ul>
542      *
543      * @return Absolute path to the oat directory or null, if oat directory
544      * cannot be created.
545      */
546     @Nullable
createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)547     private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
548         if (!pkg.canHaveOatDir()) {
549             return null;
550         }
551         File codePath = new File(pkg.codePath);
552         if (codePath.isDirectory()) {
553             // TODO(calin): why do we create this only if the codePath is a directory? (i.e for
554             //              cluster packages). It seems that the logic for the folder creation is
555             //              split between installd and here.
556             File oatDir = getOatDir(codePath);
557             try {
558                 mInstaller.createOatDir(oatDir.getAbsolutePath(), dexInstructionSet);
559             } catch (InstallerException e) {
560                 Slog.w(TAG, "Failed to create oat dir", e);
561                 return null;
562             }
563             return oatDir.getAbsolutePath();
564         }
565         return null;
566     }
567 
getOatDir(File codePath)568     static File getOatDir(File codePath) {
569         return new File(codePath, OAT_DIR_NAME);
570     }
571 
systemReady()572     void systemReady() {
573         mSystemReady = true;
574     }
575 
printDexoptFlags(int flags)576     private String printDexoptFlags(int flags) {
577         ArrayList<String> flagsList = new ArrayList<>();
578 
579         if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) {
580             flagsList.add("boot_complete");
581         }
582         if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) {
583             flagsList.add("debuggable");
584         }
585         if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) {
586             flagsList.add("profile_guided");
587         }
588         if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) {
589             flagsList.add("public");
590         }
591         if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
592             flagsList.add("secondary");
593         }
594         if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
595             flagsList.add("force");
596         }
597         if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
598             flagsList.add("storage_ce");
599         }
600         if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
601             flagsList.add("storage_de");
602         }
603 
604         return String.join(",", flagsList);
605     }
606 
607     /**
608      * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a
609      * dexopt path.
610      */
611     public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer {
612 
ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)613         public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock,
614                 Context context, String wakeLockTag) {
615             super(installer, installLock, context, wakeLockTag);
616         }
617 
ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from)618         public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) {
619             super(from);
620         }
621 
622         @Override
adjustDexoptNeeded(int dexoptNeeded)623         protected int adjustDexoptNeeded(int dexoptNeeded) {
624             if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
625                 // Ensure compilation by pretending a compiler filter change on the
626                 // apk/odex location (the reason for the '-'. A positive value means
627                 // the 'oat' location).
628                 return -DexFile.DEX2OAT_FOR_FILTER;
629             }
630             return dexoptNeeded;
631         }
632 
633         @Override
adjustDexoptFlags(int flags)634         protected int adjustDexoptFlags(int flags) {
635             // Add DEXOPT_FORCE flag to signal installd that it should force compilation
636             // and discard dexoptanalyzer result.
637             return flags | DEXOPT_FORCE;
638         }
639     }
640 }
641