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