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