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