PackageDexOptimizer.java revision 3b74c41776da66562a68b12a0fed8d20b6952868
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.server.pm; 18 19import android.annotation.Nullable; 20import android.content.Context; 21import android.content.pm.ApplicationInfo; 22import android.content.pm.PackageParser; 23import android.os.FileUtils; 24import android.os.PowerManager; 25import android.os.SystemClock; 26import android.os.UserHandle; 27import android.os.WorkSource; 28import android.util.Log; 29import android.util.Slog; 30 31import com.android.internal.annotations.GuardedBy; 32import com.android.internal.util.IndentingPrintWriter; 33import com.android.server.pm.Installer.InstallerException; 34import com.android.server.pm.dex.DexManager; 35import com.android.server.pm.dex.DexoptOptions; 36import com.android.server.pm.dex.DexoptUtils; 37import com.android.server.pm.dex.PackageDexUsage; 38 39import java.io.File; 40import java.io.IOException; 41import java.util.ArrayList; 42import java.util.List; 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_SECONDARY_DEX; 51import static com.android.server.pm.Installer.DEXOPT_FORCE; 52import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE; 53import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE; 54import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; 55import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; 56 57import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT; 58 59import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter; 60import static dalvik.system.DexFile.getSafeModeCompilerFilter; 61import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; 62 63/** 64 * Helper class for running dexopt command on packages. 65 */ 66public 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, CompilerStats.PackageStats packageStats, 116 PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { 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, 124 packageStats, packageUseInfo, options); 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, CompilerStats.PackageStats packageStats, 138 PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { 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 // Get the class loader context dependencies. 146 // For each code path in the package, this array contains the class loader context that 147 // needs to be passed to dexopt in order to ensure correct optimizations. 148 String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts( 149 pkg.applicationInfo, sharedLibraries); 150 151 int result = DEX_OPT_SKIPPED; 152 for (int i = 0; i < paths.size(); i++) { 153 // Skip paths that have no code. 154 if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) || 155 (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) { 156 continue; 157 } 158 // Append shared libraries with split dependencies for this split. 159 String path = paths.get(i); 160 if (options.getSplitName() != null) { 161 // We are asked to compile only a specific split. Check that the current path is 162 // what we are looking for. 163 if (!options.getSplitName().equals(new File(path).getName())) { 164 continue; 165 } 166 } 167 168 final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() 169 || packageUseInfo.isUsedByOtherApps(); 170 final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo, 171 options.getCompilerFilter(), isUsedByOtherApps); 172 final boolean profileUpdated = options.isCheckForProfileUpdates() && 173 isProfileUpdated(pkg, sharedGid, compilerFilter); 174 175 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct 176 // flags. 177 final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete()); 178 179 for (String dexCodeIsa : dexCodeInstructionSets) { 180 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, 181 profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, 182 packageStats, options.isDowngrade()); 183 // The end result is: 184 // - FAILED if any path failed, 185 // - PERFORMED if at least one path needed compilation, 186 // - SKIPPED when all paths are up to date 187 if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) { 188 result = newResult; 189 } 190 } 191 } 192 return result; 193 } 194 195 /** 196 * Performs dexopt on the {@code path} belonging to the package {@code pkg}. 197 * 198 * @return 199 * DEX_OPT_FAILED if there was any exception during dexopt 200 * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path. 201 * DEX_OPT_SKIPPED if the path does not need to be deopt-ed. 202 */ 203 @GuardedBy("mInstallLock") 204 private int dexOptPath(PackageParser.Package pkg, String path, String isa, 205 String compilerFilter, boolean profileUpdated, String sharedLibrariesPath, 206 int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) { 207 int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade); 208 if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { 209 return DEX_OPT_SKIPPED; 210 } 211 212 // TODO(calin): there's no need to try to create the oat dir over and over again, 213 // especially since it involve an extra installd call. We should create 214 // if (if supported) on the fly during the dexopt call. 215 String oatDir = createOatDirIfSupported(pkg, isa); 216 217 Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path 218 + " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa 219 + " dexoptFlags=" + printDexoptFlags(dexoptFlags) 220 + " target-filter=" + compilerFilter + " oatDir=" + oatDir 221 + " sharedLibraries=" + sharedLibrariesPath); 222 223 try { 224 long startTime = System.currentTimeMillis(); 225 226 // TODO: Consider adding 2 different APIs for primary and secondary dexopt. 227 // installd only uses downgrade flag for secondary dex files and ignores it for 228 // primary dex files. 229 mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags, 230 compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo, 231 false /* downgrade*/); 232 233 if (packageStats != null) { 234 long endTime = System.currentTimeMillis(); 235 packageStats.setCompileTime(path, (int)(endTime - startTime)); 236 } 237 return DEX_OPT_PERFORMED; 238 } catch (InstallerException e) { 239 Slog.w(TAG, "Failed to dexopt", e); 240 return DEX_OPT_FAILED; 241 } 242 } 243 244 /** 245 * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}. 246 * 247 * @return 248 * DEX_OPT_FAILED if there was any exception during dexopt 249 * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path. 250 * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file 251 * didn't need an update. That's because at the moment we don't get more than success/failure 252 * from installd. 253 * 254 * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than 255 * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though 256 * that seems wasteful. 257 */ 258 public int dexOptSecondaryDexPath(ApplicationInfo info, String path, 259 PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { 260 synchronized (mInstallLock) { 261 final long acquireTime = acquireWakeLockLI(info.uid); 262 try { 263 return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options); 264 } finally { 265 releaseWakeLockLI(acquireTime); 266 } 267 } 268 } 269 270 @GuardedBy("mInstallLock") 271 private long acquireWakeLockLI(final int uid) { 272 // During boot the system doesn't need to instantiate and obtain a wake lock. 273 // PowerManager might not be ready, but that doesn't mean that we can't proceed with 274 // dexopt. 275 if (!mSystemReady) { 276 return -1; 277 } 278 mDexoptWakeLock.setWorkSource(new WorkSource(uid)); 279 mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS); 280 return SystemClock.elapsedRealtime(); 281 } 282 283 @GuardedBy("mInstallLock") 284 private void releaseWakeLockLI(final long acquireTime) { 285 if (acquireTime < 0) { 286 return; 287 } 288 try { 289 if (mDexoptWakeLock.isHeld()) { 290 mDexoptWakeLock.release(); 291 } 292 final long duration = SystemClock.elapsedRealtime() - acquireTime; 293 if (duration >= WAKELOCK_TIMEOUT_MS) { 294 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag() 295 + " time out. Operation took " + duration + " ms. Thread: " 296 + Thread.currentThread().getName()); 297 } 298 } catch (Exception e) { 299 Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e); 300 } 301 } 302 303 @GuardedBy("mInstallLock") 304 private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, 305 PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { 306 if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) { 307 // We are asked to optimize only the dex files used by other apps and this is not 308 // on of them: skip it. 309 return DEX_OPT_SKIPPED; 310 } 311 312 String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(), 313 dexUseInfo.isUsedByOtherApps()); 314 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. 315 // Secondary dex files are currently not compiled at boot. 316 int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true) 317 | DEXOPT_SECONDARY_DEX; 318 // Check the app storage and add the appropriate flags. 319 if (info.deviceProtectedDataDir != null && 320 FileUtils.contains(info.deviceProtectedDataDir, path)) { 321 dexoptFlags |= DEXOPT_STORAGE_DE; 322 } else if (info.credentialProtectedDataDir != null && 323 FileUtils.contains(info.credentialProtectedDataDir, path)) { 324 dexoptFlags |= DEXOPT_STORAGE_CE; 325 } else { 326 Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); 327 return DEX_OPT_FAILED; 328 } 329 Log.d(TAG, "Running dexopt on: " + path 330 + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas() 331 + " dexoptFlags=" + printDexoptFlags(dexoptFlags) 332 + " target-filter=" + compilerFilter); 333 334 String classLoaderContext; 335 if (dexUseInfo.isUnknownClassLoaderContext() || 336 dexUseInfo.isUnsupportedClassLoaderContext() || 337 dexUseInfo.isVariableClassLoaderContext()) { 338 // If we have an unknown (not yet set), unsupported (custom class loaders), or a 339 // variable class loader chain, compile without a context and mark the oat file with 340 // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation. 341 // TODO(calin): We should just extract in this case. 342 classLoaderContext = SKIP_SHARED_LIBRARY_CHECK; 343 } else { 344 classLoaderContext = dexUseInfo.getClassLoaderContext(); 345 } 346 try { 347 for (String isa : dexUseInfo.getLoaderIsas()) { 348 // Reuse the same dexopt path as for the primary apks. We don't need all the 349 // arguments as some (dexopNeeded and oatDir) will be computed by installd because 350 // system server cannot read untrusted app content. 351 // TODO(calin): maybe add a separate call. 352 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0, 353 /*oatDir*/ null, dexoptFlags, 354 compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser, 355 options.isDowngrade()); 356 } 357 358 return DEX_OPT_PERFORMED; 359 } catch (InstallerException e) { 360 Slog.w(TAG, "Failed to dexopt", e); 361 return DEX_OPT_FAILED; 362 } 363 } 364 365 /** 366 * Adjust the given dexopt-needed value. Can be overridden to influence the decision to 367 * optimize or not (and in what way). 368 */ 369 protected int adjustDexoptNeeded(int dexoptNeeded) { 370 return dexoptNeeded; 371 } 372 373 /** 374 * Adjust the given dexopt flags that will be passed to the installer. 375 */ 376 protected int adjustDexoptFlags(int dexoptFlags) { 377 return dexoptFlags; 378 } 379 380 /** 381 * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}. 382 */ 383 void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg) { 384 final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); 385 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 386 387 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); 388 389 for (String instructionSet : dexCodeInstructionSets) { 390 pw.println("Instruction Set: " + instructionSet); 391 pw.increaseIndent(); 392 for (String path : paths) { 393 String status = null; 394 try { 395 status = DexFile.getDexFileStatus(path, instructionSet); 396 } catch (IOException ioe) { 397 status = "[Exception]: " + ioe.getMessage(); 398 } 399 pw.println("path: " + path); 400 pw.println("status: " + status); 401 } 402 pw.decreaseIndent(); 403 } 404 } 405 406 /** 407 * Returns the compiler filter that should be used to optimize the package code. 408 * The target filter will be updated if the package code is used by other apps 409 * or if it has the safe mode flag set. 410 */ 411 private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, 412 boolean isUsedByOtherApps) { 413 int flags = info.flags; 414 boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0; 415 if (vmSafeMode) { 416 return getSafeModeCompilerFilter(targetCompilerFilter); 417 } 418 419 if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) { 420 // If the dex files is used by other apps, we cannot use profile-guided compilation. 421 return getNonProfileGuidedCompilerFilter(targetCompilerFilter); 422 } 423 424 return targetCompilerFilter; 425 } 426 427 /** 428 * Computes the dex flags that needs to be pass to installd for the given package and compiler 429 * filter. 430 */ 431 private int getDexFlags(PackageParser.Package pkg, String compilerFilter, 432 boolean bootComplete) { 433 return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete); 434 } 435 436 private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) { 437 int flags = info.flags; 438 boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 439 // Profile guide compiled oat files should not be public. 440 boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter); 441 boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter; 442 int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; 443 int dexFlags = 444 (isPublic ? DEXOPT_PUBLIC : 0) 445 | (debuggable ? DEXOPT_DEBUGGABLE : 0) 446 | profileFlag 447 | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0); 448 return adjustDexoptFlags(dexFlags); 449 } 450 451 /** 452 * Assesses if there's a need to perform dexopt on {@code path} for the given 453 * configuration (isa, compiler filter, profile). 454 */ 455 private int getDexoptNeeded(String path, String isa, String compilerFilter, 456 boolean newProfile, boolean downgrade) { 457 int dexoptNeeded; 458 try { 459 dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile, 460 downgrade); 461 } catch (IOException ioe) { 462 Slog.w(TAG, "IOException reading apk: " + path, ioe); 463 return DEX_OPT_FAILED; 464 } 465 return adjustDexoptNeeded(dexoptNeeded); 466 } 467 468 /** 469 * Checks if there is an update on the profile information of the {@code pkg}. 470 * If the compiler filter is not profile guided the method returns false. 471 * 472 * Note that this is a "destructive" operation with side effects. Under the hood the 473 * current profile and the reference profile will be merged and subsequent calls 474 * may return a different result. 475 */ 476 private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String compilerFilter) { 477 // Check if we are allowed to merge and if the compiler filter is profile guided. 478 if (!isProfileGuidedCompilerFilter(compilerFilter)) { 479 return false; 480 } 481 // Merge profiles. It returns whether or not there was an updated in the profile info. 482 try { 483 return mInstaller.mergeProfiles(uid, pkg.packageName); 484 } catch (InstallerException e) { 485 Slog.w(TAG, "Failed to merge profiles", e); 486 } 487 return false; 488 } 489 490 /** 491 * Creates oat dir for the specified package if needed and supported. 492 * In certain cases oat directory 493 * <strong>cannot</strong> be created: 494 * <ul> 495 * <li>{@code pkg} is a system app, which is not updated.</li> 496 * <li>Package location is not a directory, i.e. monolithic install.</li> 497 * </ul> 498 * 499 * @return Absolute path to the oat directory or null, if oat directory 500 * cannot be created. 501 */ 502 @Nullable 503 private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) { 504 if (!pkg.canHaveOatDir()) { 505 return null; 506 } 507 File codePath = new File(pkg.codePath); 508 if (codePath.isDirectory()) { 509 // TODO(calin): why do we create this only if the codePath is a directory? (i.e for 510 // cluster packages). It seems that the logic for the folder creation is 511 // split between installd and here. 512 File oatDir = getOatDir(codePath); 513 try { 514 mInstaller.createOatDir(oatDir.getAbsolutePath(), dexInstructionSet); 515 } catch (InstallerException e) { 516 Slog.w(TAG, "Failed to create oat dir", e); 517 return null; 518 } 519 return oatDir.getAbsolutePath(); 520 } 521 return null; 522 } 523 524 static File getOatDir(File codePath) { 525 return new File(codePath, OAT_DIR_NAME); 526 } 527 528 void systemReady() { 529 mSystemReady = true; 530 } 531 532 private String printDexoptFlags(int flags) { 533 ArrayList<String> flagsList = new ArrayList<>(); 534 535 if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) { 536 flagsList.add("boot_complete"); 537 } 538 if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) { 539 flagsList.add("debuggable"); 540 } 541 if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) { 542 flagsList.add("profile_guided"); 543 } 544 if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) { 545 flagsList.add("public"); 546 } 547 if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) { 548 flagsList.add("secondary"); 549 } 550 if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) { 551 flagsList.add("force"); 552 } 553 if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) { 554 flagsList.add("storage_ce"); 555 } 556 if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) { 557 flagsList.add("storage_de"); 558 } 559 560 return String.join(",", flagsList); 561 } 562 563 /** 564 * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a 565 * dexopt path. 566 */ 567 public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer { 568 569 public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, 570 Context context, String wakeLockTag) { 571 super(installer, installLock, context, wakeLockTag); 572 } 573 574 public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) { 575 super(from); 576 } 577 578 @Override 579 protected int adjustDexoptNeeded(int dexoptNeeded) { 580 if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { 581 // Ensure compilation by pretending a compiler filter change on the 582 // apk/odex location (the reason for the '-'. A positive value means 583 // the 'oat' location). 584 return -DexFile.DEX2OAT_FOR_FILTER; 585 } 586 return dexoptNeeded; 587 } 588 589 @Override 590 protected int adjustDexoptFlags(int flags) { 591 // Add DEXOPT_FORCE flag to signal installd that it should force compilation 592 // and discard dexoptanalyzer result. 593 return flags | DEXOPT_FORCE; 594 } 595 } 596} 597