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