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