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