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