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.PowerManager;
     24 import android.os.UserHandle;
     25 import android.os.WorkSource;
     26 import android.util.ArraySet;
     27 import android.util.Log;
     28 import android.util.Slog;
     29 
     30 import java.io.File;
     31 import java.io.FileNotFoundException;
     32 import java.io.IOException;
     33 import java.util.ArrayList;
     34 import java.util.List;
     35 
     36 import dalvik.system.DexFile;
     37 
     38 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
     39 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
     40 
     41 /**
     42  * Helper class for running dexopt command on packages.
     43  */
     44 final class PackageDexOptimizer {
     45     private static final String TAG = "PackageManager.DexOptimizer";
     46     static final String OAT_DIR_NAME = "oat";
     47     // TODO b/19550105 Remove error codes and use exceptions
     48     static final int DEX_OPT_SKIPPED = 0;
     49     static final int DEX_OPT_PERFORMED = 1;
     50     static final int DEX_OPT_DEFERRED = 2;
     51     static final int DEX_OPT_FAILED = -1;
     52 
     53     private final PackageManagerService mPackageManagerService;
     54     private ArraySet<PackageParser.Package> mDeferredDexOpt;
     55 
     56     private final PowerManager.WakeLock mDexoptWakeLock;
     57     private volatile boolean mSystemReady;
     58 
     59     PackageDexOptimizer(PackageManagerService packageManagerService) {
     60         this.mPackageManagerService = packageManagerService;
     61         PowerManager powerManager = (PowerManager)packageManagerService.mContext.getSystemService(
     62                 Context.POWER_SERVICE);
     63         mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*dexopt*");
     64     }
     65 
     66     /**
     67      * Performs dexopt on all code paths and libraries of the specified package for specified
     68      * instruction sets.
     69      *
     70      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} are synchronized on
     71      * {@link PackageManagerService#mInstallLock}.
     72      */
     73     int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
     74             boolean forceDex, boolean defer, boolean inclDependencies) {
     75         ArraySet<String> done;
     76         if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
     77             done = new ArraySet<String>();
     78             done.add(pkg.packageName);
     79         } else {
     80             done = null;
     81         }
     82         synchronized (mPackageManagerService.mInstallLock) {
     83             final boolean useLock = mSystemReady;
     84             if (useLock) {
     85                 mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
     86                 mDexoptWakeLock.acquire();
     87             }
     88             try {
     89                 return performDexOptLI(pkg, instructionSets, forceDex, defer, done);
     90             } finally {
     91                 if (useLock) {
     92                     mDexoptWakeLock.release();
     93                 }
     94             }
     95         }
     96     }
     97 
     98     private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
     99             boolean forceDex, boolean defer, ArraySet<String> done) {
    100         final String[] instructionSets = targetInstructionSets != null ?
    101                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
    102 
    103         if (done != null) {
    104             done.add(pkg.packageName);
    105             if (pkg.usesLibraries != null) {
    106                 performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer, done);
    107             }
    108             if (pkg.usesOptionalLibraries != null) {
    109                 performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
    110                         done);
    111             }
    112         }
    113 
    114         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
    115             return DEX_OPT_SKIPPED;
    116         }
    117 
    118         final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
    119         final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    120 
    121         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
    122         boolean performedDexOpt = false;
    123         // There are three basic cases here:
    124         // 1.) we need to dexopt, either because we are forced or it is needed
    125         // 2.) we are deferring a needed dexopt
    126         // 3.) we are skipping an unneeded dexopt
    127         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
    128         for (String dexCodeInstructionSet : dexCodeInstructionSets) {
    129             if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
    130                 continue;
    131             }
    132 
    133             for (String path : paths) {
    134                 final int dexoptNeeded;
    135                 if (forceDex) {
    136                     dexoptNeeded = DexFile.DEX2OAT_NEEDED;
    137                 } else {
    138                     try {
    139                         dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
    140                                 dexCodeInstructionSet, defer);
    141                     } catch (IOException ioe) {
    142                         Slog.w(TAG, "IOException reading apk: " + path, ioe);
    143                         return DEX_OPT_FAILED;
    144                     }
    145                 }
    146 
    147                 if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
    148                     // We're deciding to defer a needed dexopt. Don't bother dexopting for other
    149                     // paths and instruction sets. We'll deal with them all together when we process
    150                     // our list of deferred dexopts.
    151                     addPackageForDeferredDexopt(pkg);
    152                     return DEX_OPT_DEFERRED;
    153                 }
    154 
    155                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
    156                     final String dexoptType;
    157                     String oatDir = null;
    158                     if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
    159                         dexoptType = "dex2oat";
    160                         try {
    161                             oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
    162                         } catch (IOException ioe) {
    163                             Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName);
    164                             return DEX_OPT_FAILED;
    165                         }
    166                     } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
    167                         dexoptType = "patchoat";
    168                     } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
    169                         dexoptType = "self patchoat";
    170                     } else {
    171                         throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
    172                     }
    173 
    174                     Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
    175                             + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
    176                             + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
    177                             + " oatDir = " + oatDir);
    178                     final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
    179                     final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
    180                             !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
    181                             dexoptNeeded, vmSafeMode, debuggable, oatDir);
    182 
    183                     // Dex2oat might fail due to compiler / verifier errors. We soldier on
    184                     // regardless, and attempt to interpret the app as a safety net.
    185                     if (ret == 0) {
    186                         performedDexOpt = true;
    187                     }
    188                 }
    189             }
    190 
    191             // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
    192             // either have either succeeded dexopt, or have had getDexOptNeeded tell us
    193             // it isn't required. We therefore mark that this package doesn't need dexopt unless
    194             // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
    195             // it.
    196             pkg.mDexOptPerformed.add(dexCodeInstructionSet);
    197         }
    198 
    199         // If we've gotten here, we're sure that no error occurred and that we haven't
    200         // deferred dex-opt. We've either dex-opted one more paths or instruction sets or
    201         // we've skipped all of them because they are up to date. In both cases this
    202         // package doesn't need dexopt any longer.
    203         return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
    204     }
    205 
    206     /**
    207      * Creates oat dir for the specified package. In certain cases oat directory
    208      * <strong>cannot</strong> be created:
    209      * <ul>
    210      *      <li>{@code pkg} is a system app, which is not updated.</li>
    211      *      <li>Package location is not a directory, i.e. monolithic install.</li>
    212      * </ul>
    213      *
    214      * @return Absolute path to the oat directory or null, if oat directory
    215      * cannot be created.
    216      */
    217     @Nullable
    218     private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
    219             throws IOException {
    220         if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
    221                 || pkg.applicationInfo.isExternalAsec()) {
    222             return null;
    223         }
    224         File codePath = new File(pkg.codePath);
    225         if (codePath.isDirectory()) {
    226             File oatDir = getOatDir(codePath);
    227             mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
    228                     dexInstructionSet);
    229             return oatDir.getAbsolutePath();
    230         }
    231         return null;
    232     }
    233 
    234     static File getOatDir(File codePath) {
    235         return new File(codePath, OAT_DIR_NAME);
    236     }
    237 
    238     private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
    239             boolean forceDex, boolean defer, ArraySet<String> done) {
    240         for (String libName : libs) {
    241             PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
    242                     libName);
    243             if (libPkg != null && !done.contains(libName)) {
    244                 performDexOptLI(libPkg, instructionSets, forceDex, defer, done);
    245             }
    246         }
    247     }
    248 
    249     /**
    250      * Clears set of deferred dexopt packages.
    251      * @return content of dexopt set if it was not empty
    252      */
    253     public ArraySet<PackageParser.Package> clearDeferredDexOptPackages() {
    254         ArraySet<PackageParser.Package> result = mDeferredDexOpt;
    255         mDeferredDexOpt = null;
    256         return result;
    257     }
    258 
    259     public void addPackageForDeferredDexopt(PackageParser.Package pkg) {
    260         if (mDeferredDexOpt == null) {
    261             mDeferredDexOpt = new ArraySet<>();
    262         }
    263         mDeferredDexOpt.add(pkg);
    264     }
    265 
    266     void systemReady() {
    267         mSystemReady = true;
    268     }
    269 }
    270