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