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