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