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