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