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