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