PackageDexOptimizer.java revision 785d618b1a4c66d885cafec34ed1981daee6736d
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
17package com.android.server.pm;
18
19import android.annotation.Nullable;
20import android.content.Context;
21import android.content.pm.ApplicationInfo;
22import android.content.pm.PackageParser;
23import android.content.pm.dex.ArtManager;
24import android.content.pm.dex.DexMetadataHelper;
25import android.os.FileUtils;
26import android.os.PowerManager;
27import android.os.SystemClock;
28import android.os.SystemProperties;
29import android.os.UserHandle;
30import android.os.WorkSource;
31import android.util.Log;
32import android.util.Slog;
33
34import com.android.internal.annotations.GuardedBy;
35import com.android.internal.util.IndentingPrintWriter;
36import com.android.server.pm.Installer.InstallerException;
37import com.android.server.pm.dex.DexManager;
38import com.android.server.pm.dex.DexoptOptions;
39import com.android.server.pm.dex.DexoptUtils;
40import com.android.server.pm.dex.PackageDexUsage;
41
42import java.io.File;
43import java.io.IOException;
44import java.util.ArrayList;
45import java.util.Arrays;
46import java.util.List;
47import java.util.Map;
48
49import dalvik.system.DexFile;
50
51import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_NONE;
52
53import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
54import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
55import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
56import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
57import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
58import static com.android.server.pm.Installer.DEXOPT_FORCE;
59import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
60import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
61import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
62import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
63import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
64import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
65import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
66import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
67
68import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
69
70import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
71
72import static dalvik.system.DexFile.getSafeModeCompilerFilter;
73import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
74
75/**
76 * Helper class for running dexopt command on packages.
77 */
78public class PackageDexOptimizer {
79    private static final String TAG = "PackageManager.DexOptimizer";
80    static final String OAT_DIR_NAME = "oat";
81    // TODO b/19550105 Remove error codes and use exceptions
82    public static final int DEX_OPT_SKIPPED = 0;
83    public static final int DEX_OPT_PERFORMED = 1;
84    public static final int DEX_OPT_FAILED = -1;
85    // One minute over PM WATCHDOG_TIMEOUT
86    private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
87
88    /** Special library name that skips shared libraries check during compilation. */
89    public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
90
91    @GuardedBy("mInstallLock")
92    private final Installer mInstaller;
93    private final Object mInstallLock;
94
95    @GuardedBy("mInstallLock")
96    private final PowerManager.WakeLock mDexoptWakeLock;
97    private volatile boolean mSystemReady;
98
99    PackageDexOptimizer(Installer installer, Object installLock, Context context,
100            String wakeLockTag) {
101        this.mInstaller = installer;
102        this.mInstallLock = installLock;
103
104        PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
105        mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
106    }
107
108    protected PackageDexOptimizer(PackageDexOptimizer from) {
109        this.mInstaller = from.mInstaller;
110        this.mInstallLock = from.mInstallLock;
111        this.mDexoptWakeLock = from.mDexoptWakeLock;
112        this.mSystemReady = from.mSystemReady;
113    }
114
115    static boolean canOptimizePackage(PackageParser.Package pkg) {
116        // We do not dexopt a package with no code.
117        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
118            return false;
119        }
120
121        return true;
122    }
123
124    /**
125     * Performs dexopt on all code paths and libraries of the specified package for specified
126     * instruction sets.
127     *
128     * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
129     * synchronized on {@link #mInstallLock}.
130     */
131    int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
132            String[] instructionSets, CompilerStats.PackageStats packageStats,
133            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
134        if (pkg.applicationInfo.uid == -1) {
135            throw new IllegalArgumentException("Dexopt for " + pkg.packageName
136                    + " has invalid uid.");
137        }
138        if (!canOptimizePackage(pkg)) {
139            return DEX_OPT_SKIPPED;
140        }
141        synchronized (mInstallLock) {
142            final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
143            try {
144                return performDexOptLI(pkg, sharedLibraries, instructionSets,
145                        packageStats, packageUseInfo, options);
146            } finally {
147                releaseWakeLockLI(acquireTime);
148            }
149        }
150    }
151
152    /**
153     * Performs dexopt on all code paths of the given package.
154     * It assumes the install lock is held.
155     */
156    @GuardedBy("mInstallLock")
157    private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
158            String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
159            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
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")
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, getReasonName(compilationReason));
293
294            if (packageStats != null) {
295                long endTime = System.currentTimeMillis();
296                packageStats.setCompileTime(path, (int)(endTime - startTime));
297            }
298            return DEX_OPT_PERFORMED;
299        } catch (InstallerException e) {
300            Slog.w(TAG, "Failed to dexopt", e);
301            return DEX_OPT_FAILED;
302        }
303    }
304
305    /**
306     * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
307     *
308     * @return
309     *      DEX_OPT_FAILED if there was any exception during dexopt
310     *      DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
311     * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
312     * didn't need an update. That's because at the moment we don't get more than success/failure
313     * from installd.
314     *
315     * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
316     * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
317     * that seems wasteful.
318     */
319    public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
320            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
321        if (info.uid == -1) {
322            throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid.");
323        }
324        synchronized (mInstallLock) {
325            final long acquireTime = acquireWakeLockLI(info.uid);
326            try {
327                return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
328            } finally {
329                releaseWakeLockLI(acquireTime);
330            }
331        }
332    }
333
334    @GuardedBy("mInstallLock")
335    private long acquireWakeLockLI(final int uid) {
336        // During boot the system doesn't need to instantiate and obtain a wake lock.
337        // PowerManager might not be ready, but that doesn't mean that we can't proceed with
338        // dexopt.
339        if (!mSystemReady) {
340            return -1;
341        }
342        mDexoptWakeLock.setWorkSource(new WorkSource(uid));
343        mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
344        return SystemClock.elapsedRealtime();
345    }
346
347    @GuardedBy("mInstallLock")
348    private void releaseWakeLockLI(final long acquireTime) {
349        if (acquireTime < 0) {
350            return;
351        }
352        try {
353            if (mDexoptWakeLock.isHeld()) {
354                mDexoptWakeLock.release();
355            }
356            final long duration = SystemClock.elapsedRealtime() - acquireTime;
357            if (duration >= WAKELOCK_TIMEOUT_MS) {
358                Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
359                        + " time out. Operation took " + duration + " ms. Thread: "
360                        + Thread.currentThread().getName());
361            }
362        } catch (Exception e) {
363            Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
364        }
365    }
366
367    @GuardedBy("mInstallLock")
368    private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
369            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
370        if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
371            // We are asked to optimize only the dex files used by other apps and this is not
372            // on of them: skip it.
373            return DEX_OPT_SKIPPED;
374        }
375
376        String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
377                dexUseInfo.isUsedByOtherApps());
378        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
379        // Secondary dex files are currently not compiled at boot.
380        int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
381        // Check the app storage and add the appropriate flags.
382        if (info.deviceProtectedDataDir != null &&
383                FileUtils.contains(info.deviceProtectedDataDir, path)) {
384            dexoptFlags |= DEXOPT_STORAGE_DE;
385        } else if (info.credentialProtectedDataDir != null &&
386                FileUtils.contains(info.credentialProtectedDataDir, path)) {
387            dexoptFlags |= DEXOPT_STORAGE_CE;
388        } else {
389            Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
390            return DEX_OPT_FAILED;
391        }
392        Log.d(TAG, "Running dexopt on: " + path
393                + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
394                + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
395                + " target-filter=" + compilerFilter);
396
397        // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
398        // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
399        // in isolation (and avoid to extract/verify the main apk if it's in the class path).
400        // Note this trades correctness for performance since the resulting slow down is
401        // unacceptable in some cases until b/64530081 is fixed.
402        String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
403        int reason = options.getCompilationReason();
404        try {
405            for (String isa : dexUseInfo.getLoaderIsas()) {
406                // Reuse the same dexopt path as for the primary apks. We don't need all the
407                // arguments as some (dexopNeeded and oatDir) will be computed by installd because
408                // system server cannot read untrusted app content.
409                // TODO(calin): maybe add a separate call.
410                mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
411                        /*oatDir*/ null, dexoptFlags,
412                        compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
413                        options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
414                        /*dexMetadataPath*/ null, getReasonName(reason));
415            }
416
417            return DEX_OPT_PERFORMED;
418        } catch (InstallerException e) {
419            Slog.w(TAG, "Failed to dexopt", e);
420            return DEX_OPT_FAILED;
421        }
422    }
423
424    /**
425     * Adjust the given dexopt-needed value. Can be overridden to influence the decision to
426     * optimize or not (and in what way).
427     */
428    protected int adjustDexoptNeeded(int dexoptNeeded) {
429        return dexoptNeeded;
430    }
431
432    /**
433     * Adjust the given dexopt flags that will be passed to the installer.
434     */
435    protected int adjustDexoptFlags(int dexoptFlags) {
436        return dexoptFlags;
437    }
438
439    /**
440     * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
441     */
442    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
443            PackageDexUsage.PackageUseInfo useInfo) {
444        final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
445        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
446
447        final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
448
449        for (String path : paths) {
450            pw.println("path: " + path);
451            pw.increaseIndent();
452
453            for (String isa : dexCodeInstructionSets) {
454                try {
455                    DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa);
456                    pw.println(isa + ": [status=" + info.getStatus()
457                            +"] [reason=" + info.getReason() + "]");
458                } catch (IOException ioe) {
459                    pw.println(isa + ": [Exception]: " + ioe.getMessage());
460                }
461            }
462
463            if (useInfo.isUsedByOtherApps(path)) {
464                pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
465            }
466
467            Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
468
469            if (!dexUseInfoMap.isEmpty()) {
470                pw.println("known secondary dex files:");
471                pw.increaseIndent();
472                for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
473                    String dex = e.getKey();
474                    PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
475                    pw.println(dex);
476                    pw.increaseIndent();
477                    // TODO(calin): get the status of the oat file (needs installd call)
478                    pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
479                    if (dexUseInfo.isUsedByOtherApps()) {
480                        pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
481                    }
482                    pw.decreaseIndent();
483                }
484                pw.decreaseIndent();
485            }
486            pw.decreaseIndent();
487        }
488    }
489
490    /**
491     * Returns the compiler filter that should be used to optimize the package code.
492     * The target filter will be updated if the package code is used by other apps
493     * or if it has the safe mode flag set.
494     */
495    private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
496            boolean isUsedByOtherApps) {
497        int flags = info.flags;
498        boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
499        // When a priv app is configured to run out of box, only verify it.
500        if (info.isPrivilegedApp() && DexManager.isPackageSelectedToRunOob(info.packageName)) {
501            return "verify";
502        }
503        if (vmSafeMode) {
504            return getSafeModeCompilerFilter(targetCompilerFilter);
505        }
506
507        if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
508            // If the dex files is used by other apps, apply the shared filter.
509            return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
510                    PackageManagerService.REASON_SHARED);
511        }
512
513        return targetCompilerFilter;
514    }
515
516    /**
517     * Computes the dex flags that needs to be pass to installd for the given package and compiler
518     * filter.
519     */
520    private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
521            DexoptOptions options) {
522        return getDexFlags(pkg.applicationInfo, compilerFilter, options);
523    }
524
525    private boolean isAppImageEnabled() {
526        return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
527    }
528
529    private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
530        int flags = info.flags;
531        boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
532        // Profile guide compiled oat files should not be public unles they are based
533        // on profiles from dex metadata archives.
534        // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
535        // the user does not have an existing profile.
536        boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
537        boolean isPublic = !info.isForwardLocked() &&
538                (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
539        int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
540        // Some apps are executed with restrictions on hidden API usage. If this app is one
541        // of them, pass a flag to dexopt to enable the same restrictions during compilation.
542        // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
543        int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_NONE
544                ? 0
545                : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
546        // Avoid generating CompactDex for modes that are latency critical.
547        final int compilationReason = options.getCompilationReason();
548        boolean generateCompactDex = true;
549        switch (compilationReason) {
550            case PackageManagerService.REASON_FIRST_BOOT:
551            case PackageManagerService.REASON_BOOT:
552            case PackageManagerService.REASON_INSTALL:
553                 generateCompactDex = false;
554        }
555        // Use app images only if it is enabled and we are compiling
556        // profile-guided (so the app image doesn't conservatively contain all classes).
557        // If the app didn't request for the splits to be loaded in isolation or if it does not
558        // declare inter-split dependencies, then all the splits will be loaded in the base
559        // apk class loader (in the order of their definition, otherwise disable app images
560        // because they are unsupported for multiple class loaders. b/7269679
561        boolean generateAppImage = isProfileGuidedFilter && (info.splitDependencies == null ||
562                !info.requestsIsolatedSplitLoading()) && isAppImageEnabled();
563        int dexFlags =
564                (isPublic ? DEXOPT_PUBLIC : 0)
565                | (debuggable ? DEXOPT_DEBUGGABLE : 0)
566                | profileFlag
567                | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
568                | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
569                | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
570                | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
571                | hiddenApiFlag;
572        return adjustDexoptFlags(dexFlags);
573    }
574
575    /**
576     * Assesses if there's a need to perform dexopt on {@code path} for the given
577     * configuration (isa, compiler filter, profile).
578     */
579    private int getDexoptNeeded(String path, String isa, String compilerFilter,
580            String classLoaderContext, boolean newProfile, boolean downgrade) {
581        int dexoptNeeded;
582        try {
583            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext,
584                    newProfile, downgrade);
585        } catch (IOException ioe) {
586            Slog.w(TAG, "IOException reading apk: " + path, ioe);
587            return DEX_OPT_FAILED;
588        }
589        return adjustDexoptNeeded(dexoptNeeded);
590    }
591
592    /**
593     * Checks if there is an update on the profile information of the {@code pkg}.
594     * If the compiler filter is not profile guided the method returns false.
595     *
596     * Note that this is a "destructive" operation with side effects. Under the hood the
597     * current profile and the reference profile will be merged and subsequent calls
598     * may return a different result.
599     */
600    private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String profileName,
601            String compilerFilter) {
602        // Check if we are allowed to merge and if the compiler filter is profile guided.
603        if (!isProfileGuidedCompilerFilter(compilerFilter)) {
604            return false;
605        }
606        // Merge profiles. It returns whether or not there was an updated in the profile info.
607        try {
608            return mInstaller.mergeProfiles(uid, pkg.packageName, profileName);
609        } catch (InstallerException e) {
610            Slog.w(TAG, "Failed to merge profiles", e);
611        }
612        return false;
613    }
614
615    /**
616     * Creates oat dir for the specified package if needed and supported.
617     * In certain cases oat directory
618     * <strong>cannot</strong> be created:
619     * <ul>
620     *      <li>{@code pkg} is a system app, which is not updated.</li>
621     *      <li>Package location is not a directory, i.e. monolithic install.</li>
622     * </ul>
623     *
624     * @return Absolute path to the oat directory or null, if oat directory
625     * cannot be created.
626     */
627    @Nullable
628    private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
629        if (!pkg.canHaveOatDir()) {
630            return null;
631        }
632        File codePath = new File(pkg.codePath);
633        if (codePath.isDirectory()) {
634            // TODO(calin): why do we create this only if the codePath is a directory? (i.e for
635            //              cluster packages). It seems that the logic for the folder creation is
636            //              split between installd and here.
637            File oatDir = getOatDir(codePath);
638            try {
639                mInstaller.createOatDir(oatDir.getAbsolutePath(), dexInstructionSet);
640            } catch (InstallerException e) {
641                Slog.w(TAG, "Failed to create oat dir", e);
642                return null;
643            }
644            return oatDir.getAbsolutePath();
645        }
646        return null;
647    }
648
649    static File getOatDir(File codePath) {
650        return new File(codePath, OAT_DIR_NAME);
651    }
652
653    void systemReady() {
654        mSystemReady = true;
655    }
656
657    private String printDexoptFlags(int flags) {
658        ArrayList<String> flagsList = new ArrayList<>();
659
660        if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) {
661            flagsList.add("boot_complete");
662        }
663        if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) {
664            flagsList.add("debuggable");
665        }
666        if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) {
667            flagsList.add("profile_guided");
668        }
669        if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) {
670            flagsList.add("public");
671        }
672        if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
673            flagsList.add("secondary");
674        }
675        if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
676            flagsList.add("force");
677        }
678        if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
679            flagsList.add("storage_ce");
680        }
681        if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
682            flagsList.add("storage_de");
683        }
684        if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
685            flagsList.add("idle_background_job");
686        }
687        if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
688            flagsList.add("enable_hidden_api_checks");
689        }
690
691        return String.join(",", flagsList);
692    }
693
694    /**
695     * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a
696     * dexopt path.
697     */
698    public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer {
699
700        public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock,
701                Context context, String wakeLockTag) {
702            super(installer, installLock, context, wakeLockTag);
703        }
704
705        public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) {
706            super(from);
707        }
708
709        @Override
710        protected int adjustDexoptNeeded(int dexoptNeeded) {
711            if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
712                // Ensure compilation by pretending a compiler filter change on the
713                // apk/odex location (the reason for the '-'. A positive value means
714                // the 'oat' location).
715                return -DexFile.DEX2OAT_FOR_FILTER;
716            }
717            return dexoptNeeded;
718        }
719
720        @Override
721        protected int adjustDexoptFlags(int flags) {
722            // Add DEXOPT_FORCE flag to signal installd that it should force compilation
723            // and discard dexoptanalyzer result.
724            return flags | DEXOPT_FORCE;
725        }
726    }
727}
728