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