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