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