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