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_FOR_RESTORE; 26 import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE; 27 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX; 28 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB; 29 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED; 30 import static com.android.server.pm.Installer.DEXOPT_PUBLIC; 31 import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX; 32 import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE; 33 import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE; 34 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES; 35 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; 36 import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE; 37 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; 38 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; 39 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 40 import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT; 41 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName; 42 43 import static dalvik.system.DexFile.getSafeModeCompilerFilter; 44 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; 45 46 import android.annotation.NonNull; 47 import android.annotation.Nullable; 48 import android.content.Context; 49 import android.content.pm.ApplicationInfo; 50 import android.content.pm.SharedLibraryInfo; 51 import android.content.pm.dex.ArtManager; 52 import android.content.pm.dex.DexMetadataHelper; 53 import android.os.FileUtils; 54 import android.os.PowerManager; 55 import android.os.SystemClock; 56 import android.os.SystemProperties; 57 import android.os.Trace; 58 import android.os.UserHandle; 59 import android.os.WorkSource; 60 import android.os.storage.StorageManager; 61 import android.util.Log; 62 import android.util.Slog; 63 import android.util.SparseArray; 64 65 import com.android.internal.annotations.GuardedBy; 66 import com.android.internal.annotations.VisibleForTesting; 67 import com.android.internal.util.IndentingPrintWriter; 68 import com.android.server.LocalServices; 69 import com.android.server.apphibernation.AppHibernationManagerInternal; 70 import com.android.server.pm.Installer.InstallerException; 71 import com.android.server.pm.dex.ArtManagerService; 72 import com.android.server.pm.dex.ArtStatsLogUtils; 73 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger; 74 import com.android.server.pm.dex.DexManager; 75 import com.android.server.pm.dex.DexoptOptions; 76 import com.android.server.pm.dex.DexoptUtils; 77 import com.android.server.pm.dex.PackageDexUsage; 78 import com.android.server.pm.parsing.pkg.AndroidPackage; 79 import com.android.server.pm.parsing.pkg.AndroidPackageUtils; 80 81 import dalvik.system.DexFile; 82 83 import java.io.File; 84 import java.io.IOException; 85 import java.util.ArrayList; 86 import java.util.Arrays; 87 import java.util.List; 88 import java.util.Map; 89 import java.util.Random; 90 91 /** 92 * Helper class for running dexopt command on packages. 93 */ 94 public class PackageDexOptimizer { 95 private static final String TAG = "PackageDexOptimizer"; 96 static final String OAT_DIR_NAME = "oat"; 97 // TODO b/19550105 Remove error codes and use exceptions 98 public static final int DEX_OPT_SKIPPED = 0; 99 public static final int DEX_OPT_PERFORMED = 1; 100 public static final int DEX_OPT_FAILED = -1; 101 // One minute over PM WATCHDOG_TIMEOUT 102 private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60; 103 104 @GuardedBy("mInstallLock") 105 private final Installer mInstaller; 106 private final Object mInstallLock; 107 108 @GuardedBy("mInstallLock") 109 private final PowerManager.WakeLock mDexoptWakeLock; 110 private volatile boolean mSystemReady; 111 112 private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger(); 113 private final Injector mInjector; 114 115 116 private static final Random sRandom = new Random(); 117 PackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)118 PackageDexOptimizer(Installer installer, Object installLock, Context context, 119 String wakeLockTag) { 120 this(new Injector() { 121 @Override 122 public AppHibernationManagerInternal getAppHibernationManagerInternal() { 123 return LocalServices.getService(AppHibernationManagerInternal.class); 124 } 125 126 @Override 127 public PowerManager getPowerManager(Context context) { 128 return context.getSystemService(PowerManager.class); 129 } 130 }, installer, installLock, context, wakeLockTag); 131 } 132 PackageDexOptimizer(PackageDexOptimizer from)133 protected PackageDexOptimizer(PackageDexOptimizer from) { 134 this.mInstaller = from.mInstaller; 135 this.mInstallLock = from.mInstallLock; 136 this.mDexoptWakeLock = from.mDexoptWakeLock; 137 this.mSystemReady = from.mSystemReady; 138 this.mInjector = from.mInjector; 139 } 140 141 @VisibleForTesting PackageDexOptimizer(@onNull Injector injector, Installer installer, Object installLock, Context context, String wakeLockTag)142 PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock, 143 Context context, String wakeLockTag) { 144 this.mInstaller = installer; 145 this.mInstallLock = installLock; 146 147 PowerManager powerManager = injector.getPowerManager(context); 148 mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag); 149 mInjector = injector; 150 } 151 canOptimizePackage(AndroidPackage pkg)152 boolean canOptimizePackage(AndroidPackage pkg) { 153 // We do not dexopt a package with no code. 154 // Note that the system package is marked as having no code, however we can 155 // still optimize it via dexoptSystemServerPath. 156 if (!PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName()) && !pkg.isHasCode()) { 157 return false; 158 } 159 160 // We do not dexopt unused packages. 161 // It's possible for this to be called before app hibernation service is ready due to 162 // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since 163 // a hibernating app should have no artifacts to copy in the first place. 164 AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal(); 165 if (ahm != null 166 && ahm.isHibernatingGlobally(pkg.getPackageName()) 167 && ahm.isOatArtifactDeletionEnabled()) { 168 return false; 169 } 170 171 return true; 172 } 173 174 /** 175 * Performs dexopt on all code paths and libraries of the specified package for specified 176 * instruction sets. 177 * 178 * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are 179 * synchronized on {@link #mInstallLock}. 180 */ performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)181 int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, 182 String[] instructionSets, CompilerStats.PackageStats packageStats, 183 PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { 184 if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) { 185 throw new IllegalArgumentException("System server dexopting should be done via " 186 + " DexManager and PackageDexOptimizer#dexoptSystemServerPath"); 187 } 188 if (pkg.getUid() == -1) { 189 throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName() 190 + " has invalid uid."); 191 } 192 if (!canOptimizePackage(pkg)) { 193 return DEX_OPT_SKIPPED; 194 } 195 synchronized (mInstallLock) { 196 final long acquireTime = acquireWakeLockLI(pkg.getUid()); 197 try { 198 return performDexOptLI(pkg, pkgSetting, instructionSets, 199 packageStats, packageUseInfo, options); 200 } finally { 201 releaseWakeLockLI(acquireTime); 202 } 203 } 204 } 205 206 /** 207 * Performs dexopt on all code paths of the given package. 208 * It assumes the install lock is held. 209 */ 210 @GuardedBy("mInstallLock") performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)211 private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, 212 String[] targetInstructionSets, CompilerStats.PackageStats packageStats, 213 PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { 214 // ClassLoader only refers non-native (jar) shared libraries and must ignore 215 // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader(). 216 final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getPkgState() 217 .getNonNativeUsesLibraryInfos(); 218 final String[] instructionSets = targetInstructionSets != null ? 219 targetInstructionSets : getAppDexInstructionSets( 220 AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), 221 AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting)); 222 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 223 final List<String> paths = AndroidPackageUtils.getAllCodePaths(pkg); 224 225 int sharedGid = UserHandle.getSharedAppGid(pkg.getUid()); 226 if (sharedGid == -1) { 227 Slog.wtf(TAG, "Well this is awkward; package " + pkg.getPackageName() + " had UID " 228 + pkg.getUid(), new Throwable()); 229 sharedGid = android.os.Process.NOBODY_UID; 230 } 231 232 // Get the class loader context dependencies. 233 // For each code path in the package, this array contains the class loader context that 234 // needs to be passed to dexopt in order to ensure correct optimizations. 235 boolean[] pathsWithCode = new boolean[paths.size()]; 236 pathsWithCode[0] = pkg.isHasCode(); 237 for (int i = 1; i < paths.size(); i++) { 238 pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0; 239 } 240 String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts( 241 pkg, sharedLibraries, pathsWithCode); 242 243 // Validity check that we do not call dexopt with inconsistent data. 244 if (paths.size() != classLoaderContexts.length) { 245 String[] splitCodePaths = pkg.getSplitCodePaths(); 246 throw new IllegalStateException("Inconsistent information " 247 + "between PackageParser.Package and its ApplicationInfo. " 248 + "pkg.getAllCodePaths=" + paths 249 + " pkg.getBaseCodePath=" + pkg.getBaseApkPath() 250 + " pkg.getSplitCodePaths=" 251 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths))); 252 } 253 254 int result = DEX_OPT_SKIPPED; 255 for (int i = 0; i < paths.size(); i++) { 256 // Skip paths that have no code. 257 if (!pathsWithCode[i]) { 258 continue; 259 } 260 if (classLoaderContexts[i] == null) { 261 throw new IllegalStateException("Inconsistent information in the " 262 + "package structure. A split is marked to contain code " 263 + "but has no dependency listed. Index=" + i + " path=" + paths.get(i)); 264 } 265 266 // Append shared libraries with split dependencies for this split. 267 String path = paths.get(i); 268 if (options.getSplitName() != null) { 269 // We are asked to compile only a specific split. Check that the current path is 270 // what we are looking for. 271 if (!options.getSplitName().equals(new File(path).getName())) { 272 continue; 273 } 274 } 275 276 String profileName = ArtManager.getProfileName( 277 i == 0 ? null : pkg.getSplitNames()[i - 1]); 278 279 String dexMetadataPath = null; 280 if (options.isDexoptInstallWithDexMetadata()) { 281 File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path)); 282 dexMetadataPath = dexMetadataFile == null 283 ? null : dexMetadataFile.getAbsolutePath(); 284 } 285 286 final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() 287 || packageUseInfo.isUsedByOtherApps(path); 288 final String compilerFilter = getRealCompilerFilter(pkg, 289 options.getCompilerFilter(), isUsedByOtherApps); 290 // If we don't have to check for profiles updates assume 291 // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to 292 // profiles. 293 final int profileAnalysisResult = options.isCheckForProfileUpdates() 294 ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter) 295 : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; 296 297 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct 298 // flags. 299 final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, options); 300 301 for (String dexCodeIsa : dexCodeInstructionSets) { 302 int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter, 303 profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid, 304 packageStats, options.isDowngrade(), profileName, dexMetadataPath, 305 options.getCompilationReason()); 306 307 // OTAPreopt doesn't have stats so don't report in that case. 308 if (packageStats != null) { 309 Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics"); 310 try { 311 long sessionId = sRandom.nextLong(); 312 ArtStatsLogUtils.writeStatsLog( 313 mArtStatsLogger, 314 sessionId, 315 compilerFilter, 316 pkg.getUid(), 317 packageStats.getCompileTime(path), 318 dexMetadataPath, 319 options.getCompilationReason(), 320 newResult, 321 ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(), 322 pkg.getSplitCodePaths()), 323 dexCodeIsa, 324 path); 325 } finally { 326 Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER); 327 } 328 } 329 330 // The end result is: 331 // - FAILED if any path failed, 332 // - PERFORMED if at least one path needed compilation, 333 // - SKIPPED when all paths are up to date 334 if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) { 335 result = newResult; 336 } 337 } 338 } 339 return result; 340 } 341 342 /** 343 * Performs dexopt on the {@code path} belonging to the package {@code pkg}. 344 * 345 * @return 346 * DEX_OPT_FAILED if there was any exception during dexopt 347 * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path. 348 * DEX_OPT_SKIPPED if the path does not need to be deopt-ed. 349 */ 350 @GuardedBy("mInstallLock") dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path, String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason)351 private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path, 352 String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext, 353 int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, 354 String profileName, String dexMetadataPath, int compilationReason) { 355 int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, 356 profileAnalysisResult, downgrade); 357 if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { 358 return DEX_OPT_SKIPPED; 359 } 360 361 String oatDir = getPackageOatDirIfSupported(pkg, 362 pkgSetting.getPkgState().isUpdatedSystemApp()); 363 364 Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path 365 + " pkg=" + pkg.getPackageName() + " isa=" + isa 366 + " dexoptFlags=" + printDexoptFlags(dexoptFlags) 367 + " targetFilter=" + compilerFilter + " oatDir=" + oatDir 368 + " classLoaderContext=" + classLoaderContext); 369 370 try { 371 long startTime = System.currentTimeMillis(); 372 373 // TODO: Consider adding 2 different APIs for primary and secondary dexopt. 374 // installd only uses downgrade flag for secondary dex files and ignores it for 375 // primary dex files. 376 String seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting); 377 mInstaller.dexopt(path, uid, pkg.getPackageName(), isa, dexoptNeeded, oatDir, 378 dexoptFlags, compilerFilter, pkg.getVolumeUuid(), classLoaderContext, 379 seInfo, false /* downgrade*/, pkg.getTargetSdkVersion(), 380 profileName, dexMetadataPath, 381 getAugmentedReasonName(compilationReason, dexMetadataPath != null)); 382 383 if (packageStats != null) { 384 long endTime = System.currentTimeMillis(); 385 packageStats.setCompileTime(path, (int)(endTime - startTime)); 386 } 387 return DEX_OPT_PERFORMED; 388 } catch (InstallerException e) { 389 Slog.w(TAG, "Failed to dexopt", e); 390 return DEX_OPT_FAILED; 391 } 392 } 393 394 /** 395 * Perform dexopt (if needed) on a system server code path). 396 */ dexoptSystemServerPath( String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)397 public int dexoptSystemServerPath( 398 String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { 399 int dexoptFlags = DEXOPT_PUBLIC 400 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0) 401 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0); 402 403 int result = DEX_OPT_SKIPPED; 404 for (String isa : dexUseInfo.getLoaderIsas()) { 405 int dexoptNeeded = getDexoptNeeded( 406 dexPath, 407 isa, 408 options.getCompilerFilter(), 409 dexUseInfo.getClassLoaderContext(), 410 PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES, 411 /* downgrade= */ false); 412 413 if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { 414 continue; 415 } 416 try { 417 mInstaller.dexopt( 418 dexPath, 419 android.os.Process.SYSTEM_UID, 420 /* packageName= */ "android", 421 isa, 422 dexoptNeeded, 423 /* oatDir= */ null, 424 dexoptFlags, 425 options.getCompilerFilter(), 426 StorageManager.UUID_PRIVATE_INTERNAL, 427 dexUseInfo.getClassLoaderContext(), 428 /* seInfo= */ null, 429 /* downgrade= */ false , 430 /* targetSdk= */ 0, 431 /* profileName */ null, 432 /* dexMetadataPath */ null, 433 getReasonName(options.getCompilationReason())); 434 } catch (InstallerException e) { 435 Slog.w(TAG, "Failed to dexopt", e); 436 return DEX_OPT_FAILED; 437 } 438 result = DEX_OPT_PERFORMED; 439 } 440 return result; 441 } 442 getAugmentedReasonName(int compilationReason, boolean useDexMetadata)443 private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) { 444 String annotation = useDexMetadata 445 ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : ""; 446 return getReasonName(compilationReason) + annotation; 447 } 448 449 /** 450 * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}. 451 * 452 * @return 453 * DEX_OPT_FAILED if there was any exception during dexopt 454 * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path. 455 * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file 456 * didn't need an update. That's because at the moment we don't get more than success/failure 457 * from installd. 458 * 459 * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than 460 * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though 461 * that seems wasteful. 462 */ dexOptSecondaryDexPath(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)463 public int dexOptSecondaryDexPath(ApplicationInfo info, String path, 464 PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { 465 if (info.uid == -1) { 466 throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid."); 467 } 468 synchronized (mInstallLock) { 469 final long acquireTime = acquireWakeLockLI(info.uid); 470 try { 471 return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options); 472 } finally { 473 releaseWakeLockLI(acquireTime); 474 } 475 } 476 } 477 478 @GuardedBy("mInstallLock") acquireWakeLockLI(final int uid)479 private long acquireWakeLockLI(final int uid) { 480 // During boot the system doesn't need to instantiate and obtain a wake lock. 481 // PowerManager might not be ready, but that doesn't mean that we can't proceed with 482 // dexopt. 483 if (!mSystemReady) { 484 return -1; 485 } 486 mDexoptWakeLock.setWorkSource(new WorkSource(uid)); 487 mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS); 488 return SystemClock.elapsedRealtime(); 489 } 490 491 @GuardedBy("mInstallLock") releaseWakeLockLI(final long acquireTime)492 private void releaseWakeLockLI(final long acquireTime) { 493 if (acquireTime < 0) { 494 return; 495 } 496 try { 497 if (mDexoptWakeLock.isHeld()) { 498 mDexoptWakeLock.release(); 499 } 500 final long duration = SystemClock.elapsedRealtime() - acquireTime; 501 if (duration >= WAKELOCK_TIMEOUT_MS) { 502 Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag() 503 + " time out. Operation took " + duration + " ms. Thread: " 504 + Thread.currentThread().getName()); 505 } 506 } catch (Exception e) { 507 Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e); 508 } 509 } 510 511 @GuardedBy("mInstallLock") dexOptSecondaryDexPathLI(ApplicationInfo info, String path, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)512 private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, 513 PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { 514 if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) { 515 // We are asked to optimize only the dex files used by other apps and this is not 516 // on of them: skip it. 517 return DEX_OPT_SKIPPED; 518 } 519 520 String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(), 521 dexUseInfo.isUsedByOtherApps()); 522 // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. 523 // Secondary dex files are currently not compiled at boot. 524 int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX; 525 // Check the app storage and add the appropriate flags. 526 if (info.deviceProtectedDataDir != null && 527 FileUtils.contains(info.deviceProtectedDataDir, path)) { 528 dexoptFlags |= DEXOPT_STORAGE_DE; 529 } else if (info.credentialProtectedDataDir != null && 530 FileUtils.contains(info.credentialProtectedDataDir, path)) { 531 dexoptFlags |= DEXOPT_STORAGE_CE; 532 } else { 533 Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); 534 return DEX_OPT_FAILED; 535 } 536 String classLoaderContext = null; 537 if (dexUseInfo.isUnsupportedClassLoaderContext() 538 || dexUseInfo.isVariableClassLoaderContext()) { 539 // If we have an unknown (not yet set), or a variable class loader chain. Just verify 540 // the dex file. 541 compilerFilter = "verify"; 542 } else { 543 classLoaderContext = dexUseInfo.getClassLoaderContext(); 544 } 545 546 int reason = options.getCompilationReason(); 547 Log.d(TAG, "Running dexopt on: " + path 548 + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas() 549 + " reason=" + getReasonName(reason) 550 + " dexoptFlags=" + printDexoptFlags(dexoptFlags) 551 + " target-filter=" + compilerFilter 552 + " class-loader-context=" + classLoaderContext); 553 554 try { 555 for (String isa : dexUseInfo.getLoaderIsas()) { 556 // Reuse the same dexopt path as for the primary apks. We don't need all the 557 // arguments as some (dexopNeeded and oatDir) will be computed by installd because 558 // system server cannot read untrusted app content. 559 // TODO(calin): maybe add a separate call. 560 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0, 561 /*oatDir*/ null, dexoptFlags, 562 compilerFilter, info.volumeUuid, classLoaderContext, info.seInfo, 563 options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null, 564 /*dexMetadataPath*/ null, getReasonName(reason)); 565 } 566 567 return DEX_OPT_PERFORMED; 568 } catch (InstallerException e) { 569 Slog.w(TAG, "Failed to dexopt", e); 570 return DEX_OPT_FAILED; 571 } 572 } 573 574 /** 575 * Adjust the given dexopt-needed value. Can be overridden to influence the decision to 576 * optimize or not (and in what way). 577 */ adjustDexoptNeeded(int dexoptNeeded)578 protected int adjustDexoptNeeded(int dexoptNeeded) { 579 return dexoptNeeded; 580 } 581 582 /** 583 * Adjust the given dexopt flags that will be passed to the installer. 584 */ adjustDexoptFlags(int dexoptFlags)585 protected int adjustDexoptFlags(int dexoptFlags) { 586 return dexoptFlags; 587 } 588 589 /** 590 * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}. 591 */ dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageSetting pkgSetting, PackageDexUsage.PackageUseInfo useInfo)592 void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageSetting pkgSetting, 593 PackageDexUsage.PackageUseInfo useInfo) { 594 final String[] instructionSets = getAppDexInstructionSets( 595 AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), 596 AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting)); 597 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 598 599 final List<String> paths = AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg); 600 601 for (String path : paths) { 602 pw.println("path: " + path); 603 pw.increaseIndent(); 604 605 for (String isa : dexCodeInstructionSets) { 606 try { 607 DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa); 608 pw.println(isa + ": [status=" + info.getStatus() 609 +"] [reason=" + info.getReason() + "]"); 610 } catch (IOException ioe) { 611 pw.println(isa + ": [Exception]: " + ioe.getMessage()); 612 } 613 } 614 615 if (useInfo.isUsedByOtherApps(path)) { 616 pw.println("used by other apps: " + useInfo.getLoadingPackages(path)); 617 } 618 619 Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap(); 620 621 if (!dexUseInfoMap.isEmpty()) { 622 pw.println("known secondary dex files:"); 623 pw.increaseIndent(); 624 for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) { 625 String dex = e.getKey(); 626 PackageDexUsage.DexUseInfo dexUseInfo = e.getValue(); 627 pw.println(dex); 628 pw.increaseIndent(); 629 // TODO(calin): get the status of the oat file (needs installd call) 630 pw.println("class loader context: " + dexUseInfo.getClassLoaderContext()); 631 if (dexUseInfo.isUsedByOtherApps()) { 632 pw.println("used by other apps: " + dexUseInfo.getLoadingPackages()); 633 } 634 pw.decreaseIndent(); 635 } 636 pw.decreaseIndent(); 637 } 638 pw.decreaseIndent(); 639 } 640 } 641 642 /** 643 * Returns the compiler filter that should be used to optimize the package code. 644 * The target filter will be updated if the package code is used by other apps 645 * or if it has the safe mode flag set. 646 */ getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps)647 private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, 648 boolean isUsedByOtherApps) { 649 // When an app or priv app is configured to run out of box, only verify it. 650 if (info.isEmbeddedDexUsed() 651 || (info.isPrivilegedApp() 652 && DexManager.isPackageSelectedToRunOob(info.packageName))) { 653 return "verify"; 654 } 655 656 // We force vmSafeMode on debuggable apps as well: 657 // - the runtime ignores their compiled code 658 // - they generally have lots of methods that could make the compiler used run 659 // out of memory (b/130828957) 660 // Note that forcing the compiler filter here applies to all compilations (even if they 661 // are done via adb shell commands). That's ok because right now the runtime will ignore 662 // the compiled code anyway. The alternative would have been to update either 663 // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages 664 // but that would have the downside of possibly producing a big odex files which would 665 // be ignored anyway. 666 boolean vmSafeModeOrDebuggable = ((info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0) 667 || ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); 668 669 if (vmSafeModeOrDebuggable) { 670 return getSafeModeCompilerFilter(targetCompilerFilter); 671 } 672 673 if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) { 674 // If the dex files is used by other apps, apply the shared filter. 675 return PackageManagerServiceCompilerMapping.getCompilerFilterForReason( 676 PackageManagerService.REASON_SHARED); 677 } 678 679 return targetCompilerFilter; 680 } 681 682 /** 683 * Returns the compiler filter that should be used to optimize the package code. 684 * The target filter will be updated if the package code is used by other apps 685 * or if it has the safe mode flag set. 686 */ getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter, boolean isUsedByOtherApps)687 private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter, 688 boolean isUsedByOtherApps) { 689 // When an app or priv app is configured to run out of box, only verify it. 690 if (pkg.isUseEmbeddedDex() 691 || (pkg.isPrivileged() 692 && DexManager.isPackageSelectedToRunOob(pkg.getPackageName()))) { 693 return "verify"; 694 } 695 696 // We force vmSafeMode on debuggable apps as well: 697 // - the runtime ignores their compiled code 698 // - they generally have lots of methods that could make the compiler used run 699 // out of memory (b/130828957) 700 // Note that forcing the compiler filter here applies to all compilations (even if they 701 // are done via adb shell commands). That's ok because right now the runtime will ignore 702 // the compiled code anyway. The alternative would have been to update either 703 // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages 704 // but that would have the downside of possibly producing a big odex files which would 705 // be ignored anyway. 706 boolean vmSafeModeOrDebuggable = pkg.isVmSafeMode() || pkg.isDebuggable(); 707 708 if (vmSafeModeOrDebuggable) { 709 return getSafeModeCompilerFilter(targetCompilerFilter); 710 } 711 712 if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) { 713 // If the dex files is used by other apps, apply the shared filter. 714 return PackageManagerServiceCompilerMapping.getCompilerFilterForReason( 715 PackageManagerService.REASON_SHARED); 716 } 717 718 return targetCompilerFilter; 719 } 720 isAppImageEnabled()721 private boolean isAppImageEnabled() { 722 return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0; 723 } 724 getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options)725 private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) { 726 return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0, 727 info.getHiddenApiEnforcementPolicy(), info.splitDependencies, 728 info.requestsIsolatedSplitLoading(), compilerFilter, options); 729 } getDexFlags(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String compilerFilter, DexoptOptions options)730 private int getDexFlags(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, 731 String compilerFilter, DexoptOptions options) { 732 return getDexFlags(pkg.isDebuggable(), 733 AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting), 734 pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter, 735 options); 736 } 737 738 /** 739 * Computes the dex flags that needs to be pass to installd for the given package and compiler 740 * filter. 741 */ getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, String compilerFilter, DexoptOptions options)742 private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, 743 SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, 744 String compilerFilter, DexoptOptions options) { 745 // Profile guide compiled oat files should not be public unles they are based 746 // on profiles from dex metadata archives. 747 // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that 748 // the user does not have an existing profile. 749 boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter); 750 boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata(); 751 int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; 752 // Some apps are executed with restrictions on hidden API usage. If this app is one 753 // of them, pass a flag to dexopt to enable the same restrictions during compilation. 754 // TODO we should pass the actual flag value to dexopt, rather than assuming denylist 755 // TODO(b/135203078): This flag is no longer set as part of AndroidPackage 756 // and may not be preserved 757 int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED 758 ? 0 759 : DEXOPT_ENABLE_HIDDEN_API_CHECKS; 760 // Avoid generating CompactDex for modes that are latency critical. 761 final int compilationReason = options.getCompilationReason(); 762 boolean generateCompactDex = true; 763 switch (compilationReason) { 764 case PackageManagerService.REASON_FIRST_BOOT: 765 case PackageManagerService.REASON_BOOT_AFTER_OTA: 766 case PackageManagerService.REASON_POST_BOOT: 767 case PackageManagerService.REASON_INSTALL: 768 generateCompactDex = false; 769 } 770 // Use app images only if it is enabled and we are compiling 771 // profile-guided (so the app image doesn't conservatively contain all classes). 772 // If the app didn't request for the splits to be loaded in isolation or if it does not 773 // declare inter-split dependencies, then all the splits will be loaded in the base 774 // apk class loader (in the order of their definition, otherwise disable app images 775 // because they are unsupported for multiple class loaders. b/7269679 776 boolean generateAppImage = isProfileGuidedFilter && (splitDependencies == null || 777 !requestsIsolatedSplitLoading) && isAppImageEnabled(); 778 int dexFlags = 779 (isPublic ? DEXOPT_PUBLIC : 0) 780 | (debuggable ? DEXOPT_DEBUGGABLE : 0) 781 | profileFlag 782 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0) 783 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0) 784 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0) 785 | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0) 786 | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0) 787 | hiddenApiFlag; 788 return adjustDexoptFlags(dexFlags); 789 } 790 791 /** 792 * Assesses if there's a need to perform dexopt on {@code path} for the given 793 * configuration (isa, compiler filter, profile). 794 */ getDexoptNeeded(String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade)795 private int getDexoptNeeded(String path, String isa, String compilerFilter, 796 String classLoaderContext, int profileAnalysisResult, boolean downgrade) { 797 int dexoptNeeded; 798 try { 799 // A profile guided optimizations with an empty profile is essentially 'verify' and 800 // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot 801 // check the profiles because system server does not have access to them. 802 // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and 803 // manually adjust the actual filter before checking. 804 // 805 // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change, 806 // and in the interim we can still improve things here. 807 String actualCompilerFilter = compilerFilter; 808 if (compilerFilterDependsOnProfiles(compilerFilter) 809 && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) { 810 actualCompilerFilter = "verify"; 811 } 812 boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE; 813 dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter, 814 classLoaderContext, newProfile, downgrade); 815 } catch (IOException ioe) { 816 Slog.w(TAG, "IOException reading apk: " + path, ioe); 817 return DEX_OPT_FAILED; 818 } catch (Exception e) { 819 Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e); 820 return DEX_OPT_FAILED; 821 } 822 return adjustDexoptNeeded(dexoptNeeded); 823 } 824 825 /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */ compilerFilterDependsOnProfiles(String compilerFilter)826 private boolean compilerFilterDependsOnProfiles(String compilerFilter) { 827 return compilerFilter.endsWith("-profile"); 828 } 829 830 /** 831 * Checks if there is an update on the profile information of the {@code pkg}. 832 * If the compiler filter is not profile guided the method returns a safe default: 833 * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA. 834 * 835 * Note that this is a "destructive" operation with side effects. Under the hood the 836 * current profile and the reference profile will be merged and subsequent calls 837 * may return a different result. 838 */ analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter)839 private int analyseProfiles(AndroidPackage pkg, int uid, String profileName, 840 String compilerFilter) { 841 // Check if we are allowed to merge and if the compiler filter is profile guided. 842 if (!isProfileGuidedCompilerFilter(compilerFilter)) { 843 return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; 844 } 845 // Merge profiles. It returns whether or not there was an updated in the profile info. 846 try { 847 return mInstaller.mergeProfiles(uid, pkg.getPackageName(), profileName); 848 } catch (InstallerException e) { 849 Slog.w(TAG, "Failed to merge profiles", e); 850 // We don't need to optimize if we failed to merge. 851 return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; 852 } 853 } 854 855 /** 856 * Gets oat dir for the specified package if needed and supported. 857 * In certain cases oat directory 858 * <strong>cannot</strong> be created: 859 * <ul> 860 * <li>{@code pkg} is a system app, which is not updated.</li> 861 * <li>Package location is not a directory, i.e. monolithic install.</li> 862 * </ul> 863 * 864 * @return Absolute path to the oat directory or null, if oat directories 865 * not needed or unsupported for the package. 866 */ 867 @Nullable getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp)868 private String getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp) { 869 if (!AndroidPackageUtils.canHaveOatDir(pkg, isUpdatedSystemApp)) { 870 return null; 871 } 872 File codePath = new File(pkg.getPath()); 873 if (!codePath.isDirectory()) { 874 return null; 875 } 876 return getOatDir(codePath).getAbsolutePath(); 877 } 878 879 /** Returns the oat dir for the given code path */ getOatDir(File codePath)880 public static File getOatDir(File codePath) { 881 return new File(codePath, OAT_DIR_NAME); 882 } 883 systemReady()884 void systemReady() { 885 mSystemReady = true; 886 } 887 printDexoptFlags(int flags)888 private String printDexoptFlags(int flags) { 889 ArrayList<String> flagsList = new ArrayList<>(); 890 891 if ((flags & DEXOPT_BOOTCOMPLETE) == DEXOPT_BOOTCOMPLETE) { 892 flagsList.add("boot_complete"); 893 } 894 if ((flags & DEXOPT_DEBUGGABLE) == DEXOPT_DEBUGGABLE) { 895 flagsList.add("debuggable"); 896 } 897 if ((flags & DEXOPT_PROFILE_GUIDED) == DEXOPT_PROFILE_GUIDED) { 898 flagsList.add("profile_guided"); 899 } 900 if ((flags & DEXOPT_PUBLIC) == DEXOPT_PUBLIC) { 901 flagsList.add("public"); 902 } 903 if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) { 904 flagsList.add("secondary"); 905 } 906 if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) { 907 flagsList.add("force"); 908 } 909 if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) { 910 flagsList.add("storage_ce"); 911 } 912 if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) { 913 flagsList.add("storage_de"); 914 } 915 if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) { 916 flagsList.add("idle_background_job"); 917 } 918 if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) { 919 flagsList.add("enable_hidden_api_checks"); 920 } 921 922 return String.join(",", flagsList); 923 } 924 925 /** 926 * A specialized PackageDexOptimizer that overrides already-installed checks, forcing a 927 * dexopt path. 928 */ 929 public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer { 930 ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag)931 public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, 932 Context context, String wakeLockTag) { 933 super(installer, installLock, context, wakeLockTag); 934 } 935 ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from)936 public ForcedUpdatePackageDexOptimizer(PackageDexOptimizer from) { 937 super(from); 938 } 939 940 @Override adjustDexoptNeeded(int dexoptNeeded)941 protected int adjustDexoptNeeded(int dexoptNeeded) { 942 if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { 943 // Ensure compilation by pretending a compiler filter change on the 944 // apk/odex location (the reason for the '-'. A positive value means 945 // the 'oat' location). 946 return -DexFile.DEX2OAT_FOR_FILTER; 947 } 948 return dexoptNeeded; 949 } 950 951 @Override adjustDexoptFlags(int flags)952 protected int adjustDexoptFlags(int flags) { 953 // Add DEXOPT_FORCE flag to signal installd that it should force compilation 954 // and discard dexoptanalyzer result. 955 return flags | DEXOPT_FORCE; 956 } 957 } 958 959 /** 960 * Injector for {@link PackageDexOptimizer} dependencies 961 */ 962 interface Injector { getAppHibernationManagerInternal()963 AppHibernationManagerInternal getAppHibernationManagerInternal(); 964 getPowerManager(Context context)965 PowerManager getPowerManager(Context context); 966 } 967 } 968