1 /* 2 * Copyright (C) 2021 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.PackageManager.INSTALL_REASON_DEVICE_RESTORE; 20 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; 21 import static android.os.Trace.TRACE_TAG_DALVIK; 22 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 23 import static android.os.incremental.IncrementalManager.isIncrementalPath; 24 25 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; 26 import static com.android.server.pm.ApexManager.ActiveApexInfo; 27 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; 28 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 29 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE; 30 import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_OTA; 31 import static com.android.server.pm.PackageManagerService.REASON_CMDLINE; 32 import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT; 33 import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; 34 import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; 35 import static com.android.server.pm.PackageManagerService.TAG; 36 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_APEX_PKG; 37 import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG; 38 import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal; 39 40 import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; 41 42 import android.annotation.NonNull; 43 import android.annotation.Nullable; 44 import android.app.AppGlobals; 45 import android.content.BroadcastReceiver; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.IntentFilter; 49 import android.content.pm.ApexStagedEvent; 50 import android.content.pm.Flags; 51 import android.content.pm.IPackageManagerNative; 52 import android.content.pm.IStagedApexObserver; 53 import android.content.pm.PackageManager; 54 import android.content.pm.ResolveInfo; 55 import android.os.Binder; 56 import android.os.RemoteException; 57 import android.os.ServiceManager; 58 import android.os.SystemClock; 59 import android.os.SystemProperties; 60 import android.os.Trace; 61 import android.os.UserHandle; 62 import android.provider.Settings.Global; 63 import android.text.TextUtils; 64 import android.util.ArraySet; 65 import android.util.Log; 66 import android.util.Slog; 67 68 import com.android.internal.logging.MetricsLogger; 69 import com.android.internal.util.FrameworkStatsLog; 70 import com.android.internal.util.IndentingPrintWriter; 71 import com.android.server.LocalManagerRegistry; 72 import com.android.server.LocalServices; 73 import com.android.server.art.ArtManagerLocal; 74 import com.android.server.art.DexUseManagerLocal; 75 import com.android.server.art.ReasonMapping; 76 import com.android.server.art.model.ArtFlags; 77 import com.android.server.art.model.DexoptParams; 78 import com.android.server.art.model.DexoptResult; 79 import com.android.server.pinner.PinnerService; 80 import com.android.server.pm.PackageDexOptimizer.DexOptResult; 81 import com.android.server.pm.dex.DexManager; 82 import com.android.server.pm.dex.DexoptOptions; 83 import com.android.server.pm.local.PackageManagerLocalImpl; 84 import com.android.server.pm.pkg.AndroidPackage; 85 import com.android.server.pm.pkg.PackageState; 86 import com.android.server.pm.pkg.PackageStateInternal; 87 88 import java.io.File; 89 import java.nio.file.Path; 90 import java.nio.file.Paths; 91 import java.util.ArrayList; 92 import java.util.Arrays; 93 import java.util.Collection; 94 import java.util.Collections; 95 import java.util.Comparator; 96 import java.util.HashSet; 97 import java.util.List; 98 import java.util.Set; 99 import java.util.concurrent.CompletableFuture; 100 import java.util.concurrent.LinkedBlockingQueue; 101 import java.util.concurrent.ThreadPoolExecutor; 102 import java.util.concurrent.TimeUnit; 103 import java.util.function.Predicate; 104 105 /** 106 * Helper class for dex optimization operations in PackageManagerService. 107 */ 108 public final class DexOptHelper { 109 private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; 110 111 @NonNull 112 private static final ThreadPoolExecutor sDexoptExecutor = 113 new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, 114 60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 115 116 private static boolean sArtManagerLocalIsInitialized = false; 117 118 private final PackageManagerService mPm; 119 120 // Start time for the boot dexopt in performPackageDexOptUpgradeIfNeeded when ART Service is 121 // used, to make it available to the onDexoptDone callback. 122 private volatile long mBootDexoptStartTime; 123 124 static { 125 // Recycle the thread if it's not used for `keepAliveTime`. sDexoptExecutor.allowsCoreThreadTimeOut()126 sDexoptExecutor.allowsCoreThreadTimeOut(); 127 } 128 DexOptHelper(PackageManagerService pm)129 DexOptHelper(PackageManagerService pm) { 130 mPm = pm; 131 } 132 133 /* 134 * Return the prebuilt profile path given a package base code path. 135 */ getPrebuildProfilePath(AndroidPackage pkg)136 private static String getPrebuildProfilePath(AndroidPackage pkg) { 137 return pkg.getBaseApkPath() + ".prof"; 138 } 139 140 /** 141 * Called during startup to do any boot time dexopting. This can occasionally be time consuming 142 * (30+ seconds) and the function will block until it is complete. 143 */ performPackageDexOptUpgradeIfNeeded()144 public void performPackageDexOptUpgradeIfNeeded() { 145 PackageManagerServiceUtils.enforceSystemOrRoot( 146 "Only the system can request package update"); 147 148 int reason; 149 if (mPm.isFirstBoot()) { 150 reason = REASON_FIRST_BOOT; // First boot or factory reset. 151 } else if (mPm.isDeviceUpgrading()) { 152 reason = REASON_BOOT_AFTER_OTA; 153 } else if (hasBcpApexesChanged()) { 154 reason = REASON_BOOT_AFTER_MAINLINE_UPDATE; 155 } else { 156 return; 157 } 158 159 Log.i(TAG, 160 "Starting boot dexopt for reason " 161 + DexoptOptions.convertToArtServiceDexoptReason(reason)); 162 163 final long startTime = System.nanoTime(); 164 165 mBootDexoptStartTime = startTime; 166 getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason), 167 null /* progressCallbackExecutor */, null /* progressCallback */); 168 } 169 reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed)170 private void reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed) { 171 final int elapsedTimeSeconds = 172 (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); 173 174 final Computer newSnapshot = mPm.snapshotComputer(); 175 176 MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", numDexopted); 177 MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", numSkipped); 178 MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", numFailed); 179 // TODO(b/251903639): getOptimizablePackages calls PackageDexOptimizer.canOptimizePackage 180 // which duplicates logic in ART Service (com.android.server.art.Utils.canDexoptPackage). 181 MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_total", 182 getOptimizablePackages(newSnapshot).size()); 183 MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds); 184 } 185 getOptimizablePackages(@onNull Computer snapshot)186 public List<String> getOptimizablePackages(@NonNull Computer snapshot) { 187 ArrayList<String> pkgs = new ArrayList<>(); 188 mPm.forEachPackageState(snapshot, packageState -> { 189 final AndroidPackage pkg = packageState.getPkg(); 190 if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { 191 pkgs.add(packageState.getPackageName()); 192 } 193 }); 194 return pkgs; 195 } 196 performDexOpt(DexoptOptions options)197 /*package*/ boolean performDexOpt(DexoptOptions options) { 198 final Computer snapshot = mPm.snapshotComputer(); 199 if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) { 200 return false; 201 } else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) { 202 return false; 203 } 204 var pkg = snapshot.getPackage(options.getPackageName()); 205 if (pkg != null && pkg.isApex()) { 206 // skip APEX 207 return true; 208 } 209 210 @DexOptResult int dexoptStatus; 211 if (options.isDexoptOnlySecondaryDex()) { 212 dexoptStatus = performDexOptWithArtService(options, 0 /* extraFlags */); 213 } else { 214 dexoptStatus = performDexOptWithStatus(options); 215 } 216 return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED; 217 } 218 219 /** 220 * Perform dexopt on the given package and return one of following result: 221 * {@link PackageDexOptimizer#DEX_OPT_SKIPPED} 222 * {@link PackageDexOptimizer#DEX_OPT_PERFORMED} 223 * {@link PackageDexOptimizer#DEX_OPT_CANCELLED} 224 * {@link PackageDexOptimizer#DEX_OPT_FAILED} 225 */ 226 @DexOptResult performDexOptWithStatus(DexoptOptions options)227 /* package */ int performDexOptWithStatus(DexoptOptions options) { 228 return performDexOptTraced(options); 229 } 230 231 @DexOptResult performDexOptTraced(DexoptOptions options)232 private int performDexOptTraced(DexoptOptions options) { 233 Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt"); 234 try { 235 return performDexOptInternal(options); 236 } finally { 237 Trace.traceEnd(TRACE_TAG_DALVIK); 238 } 239 } 240 241 // Run dexopt on a given package. Returns true if dexopt did not fail, i.e. 242 // if the package can now be considered up to date for the given filter. 243 @DexOptResult performDexOptInternal(DexoptOptions options)244 private int performDexOptInternal(DexoptOptions options) { 245 return performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES); 246 } 247 248 /** 249 * Performs dexopt on the given package using ART Service. 250 */ 251 @DexOptResult performDexOptWithArtService(DexoptOptions options, int extraFlags)252 private int performDexOptWithArtService(DexoptOptions options, 253 /*@DexoptFlags*/ int extraFlags) { 254 try (PackageManagerLocal.FilteredSnapshot snapshot = 255 getPackageManagerLocal().withFilteredSnapshot()) { 256 PackageState ops = snapshot.getPackageState(options.getPackageName()); 257 if (ops == null) { 258 return PackageDexOptimizer.DEX_OPT_FAILED; 259 } 260 AndroidPackage oap = ops.getAndroidPackage(); 261 if (oap == null) { 262 return PackageDexOptimizer.DEX_OPT_FAILED; 263 } 264 DexoptParams params = options.convertToDexoptParams(extraFlags); 265 DexoptResult result = 266 getArtManagerLocal().dexoptPackage(snapshot, options.getPackageName(), params); 267 return convertToDexOptResult(result); 268 } 269 } 270 performDexOptMode(@onNull Computer snapshot, String packageName, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)271 public boolean performDexOptMode(@NonNull Computer snapshot, String packageName, 272 String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { 273 if (!PackageManagerServiceUtils.isSystemOrRootOrShell() 274 && !isCallerInstallerForPackage(snapshot, packageName)) { 275 throw new SecurityException("performDexOptMode"); 276 } 277 278 int flags = (force ? DexoptOptions.DEXOPT_FORCE : 0) 279 | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); 280 281 if (isProfileGuidedCompilerFilter(targetCompilerFilter)) { 282 // Set this flag whenever the filter is profile guided, to align with ART Service 283 // behavior. 284 flags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES; 285 } 286 287 return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE, 288 targetCompilerFilter, splitName, flags)); 289 } 290 isCallerInstallerForPackage(@onNull Computer snapshot, String packageName)291 private boolean isCallerInstallerForPackage(@NonNull Computer snapshot, String packageName) { 292 final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); 293 if (packageState == null) { 294 return false; 295 } 296 final InstallSource installSource = packageState.getInstallSource(); 297 298 final PackageStateInternal installerPackageState = 299 snapshot.getPackageStateInternal(installSource.mInstallerPackageName); 300 if (installerPackageState == null) { 301 return false; 302 } 303 final AndroidPackage installerPkg = installerPackageState.getPkg(); 304 return installerPkg.getUid() == Binder.getCallingUid(); 305 } 306 performDexOptSecondary( String packageName, String compilerFilter, boolean force)307 public boolean performDexOptSecondary( 308 String packageName, String compilerFilter, boolean force) { 309 int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX 310 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES 311 | DexoptOptions.DEXOPT_BOOT_COMPLETE 312 | (force ? DexoptOptions.DEXOPT_FORCE : 0); 313 return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE, 314 compilerFilter, null /* splitName */, flags)); 315 } 316 317 // Sort apps by importance for dexopt ordering. Important apps are given 318 // more priority in case the device runs out of space. getPackagesForDexopt( Collection<? extends PackageStateInternal> packages, PackageManagerService packageManagerService)319 public static List<PackageStateInternal> getPackagesForDexopt( 320 Collection<? extends PackageStateInternal> packages, 321 PackageManagerService packageManagerService) { 322 return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT); 323 } 324 getPackagesForDexopt( Collection<? extends PackageStateInternal> pkgSettings, PackageManagerService packageManagerService, boolean debug)325 public static List<PackageStateInternal> getPackagesForDexopt( 326 Collection<? extends PackageStateInternal> pkgSettings, 327 PackageManagerService packageManagerService, 328 boolean debug) { 329 List<PackageStateInternal> result = new ArrayList<>(); 330 ArrayList<PackageStateInternal> remainingPkgSettings = new ArrayList<>(pkgSettings); 331 332 // First, remove all settings without available packages 333 remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG); 334 remainingPkgSettings.removeIf(REMOVE_IF_APEX_PKG); 335 336 ArrayList<PackageStateInternal> sortTemp = new ArrayList<>(remainingPkgSettings.size()); 337 338 final Computer snapshot = packageManagerService.snapshotComputer(); 339 340 // Give priority to core apps. 341 applyPackageFilter(snapshot, pkgSetting -> pkgSetting.getPkg().isCoreApp(), result, 342 remainingPkgSettings, sortTemp, packageManagerService); 343 344 // Give priority to system apps that listen for pre boot complete. 345 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); 346 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); 347 applyPackageFilter(snapshot, pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result, 348 remainingPkgSettings, sortTemp, packageManagerService); 349 350 // Give priority to apps used by other apps. 351 DexManager dexManager = packageManagerService.getDexManager(); 352 applyPackageFilter(snapshot, pkgSetting -> 353 dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName()) 354 .isAnyCodePathUsedByOtherApps(), 355 result, remainingPkgSettings, sortTemp, packageManagerService); 356 357 // Filter out packages that aren't recently used, add all remaining apps. 358 // TODO: add a property to control this? 359 Predicate<PackageStateInternal> remainingPredicate; 360 if (!remainingPkgSettings.isEmpty() 361 && packageManagerService.isHistoricalPackageUsageAvailable()) { 362 if (debug) { 363 Log.i(TAG, "Looking at historical package use"); 364 } 365 // Get the package that was used last. 366 PackageStateInternal lastUsed = Collections.max(remainingPkgSettings, 367 Comparator.comparingLong( 368 pkgSetting -> pkgSetting.getTransientState() 369 .getLatestForegroundPackageUseTimeInMills())); 370 if (debug) { 371 Log.i(TAG, "Taking package " + lastUsed.getPackageName() 372 + " as reference in time use"); 373 } 374 long estimatedPreviousSystemUseTime = lastUsed.getTransientState() 375 .getLatestForegroundPackageUseTimeInMills(); 376 // Be defensive if for some reason package usage has bogus data. 377 if (estimatedPreviousSystemUseTime != 0) { 378 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; 379 remainingPredicate = pkgSetting -> pkgSetting.getTransientState() 380 .getLatestForegroundPackageUseTimeInMills() >= cutoffTime; 381 } else { 382 // No meaningful historical info. Take all. 383 remainingPredicate = pkgSetting -> true; 384 } 385 sortPackagesByUsageDate(remainingPkgSettings, packageManagerService); 386 } else { 387 // No historical info. Take all. 388 remainingPredicate = pkgSetting -> true; 389 } 390 applyPackageFilter(snapshot, remainingPredicate, result, remainingPkgSettings, sortTemp, 391 packageManagerService); 392 393 // Make sure the system server isn't in the result, because it can never be dexopted here. 394 result.removeIf(pkgSetting -> PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPackageName())); 395 396 if (debug) { 397 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); 398 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings)); 399 } 400 401 return result; 402 } 403 404 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the 405 // package will be removed from {@code packages} and added to {@code result} with its 406 // dependencies. If usage data is available, the positive packages will be sorted by usage 407 // data (with {@code sortTemp} as temporary storage). applyPackageFilter(@onNull Computer snapshot, Predicate<PackageStateInternal> filter, Collection<PackageStateInternal> result, Collection<PackageStateInternal> packages, @NonNull List<PackageStateInternal> sortTemp, PackageManagerService packageManagerService)408 private static void applyPackageFilter(@NonNull Computer snapshot, 409 Predicate<PackageStateInternal> filter, 410 Collection<PackageStateInternal> result, 411 Collection<PackageStateInternal> packages, 412 @NonNull List<PackageStateInternal> sortTemp, 413 PackageManagerService packageManagerService) { 414 for (PackageStateInternal pkgSetting : packages) { 415 if (filter.test(pkgSetting)) { 416 sortTemp.add(pkgSetting); 417 } 418 } 419 420 sortPackagesByUsageDate(sortTemp, packageManagerService); 421 packages.removeAll(sortTemp); 422 423 for (PackageStateInternal pkgSetting : sortTemp) { 424 result.add(pkgSetting); 425 426 List<PackageStateInternal> deps = snapshot.findSharedNonSystemLibraries(pkgSetting); 427 if (!deps.isEmpty()) { 428 deps.removeAll(result); 429 result.addAll(deps); 430 packages.removeAll(deps); 431 } 432 } 433 434 sortTemp.clear(); 435 } 436 437 // Sort a list of apps by their last usage, most recently used apps first. The order of 438 // packages without usage data is undefined (but they will be sorted after the packages 439 // that do have usage data). sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings, PackageManagerService packageManagerService)440 private static void sortPackagesByUsageDate(List<PackageStateInternal> pkgSettings, 441 PackageManagerService packageManagerService) { 442 if (!packageManagerService.isHistoricalPackageUsageAvailable()) { 443 return; 444 } 445 446 Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) -> 447 Long.compare( 448 pkgSetting2.getTransientState().getLatestForegroundPackageUseTimeInMills(), 449 pkgSetting1.getTransientState().getLatestForegroundPackageUseTimeInMills()) 450 ); 451 } 452 getPackageNamesForIntent(Intent intent, int userId)453 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { 454 List<ResolveInfo> ris = null; 455 try { 456 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId) 457 .getList(); 458 } catch (RemoteException e) { 459 } 460 ArraySet<String> pkgNames = new ArraySet<String>(); 461 if (ris != null) { 462 for (ResolveInfo ri : ris) { 463 pkgNames.add(ri.activityInfo.packageName); 464 } 465 } 466 return pkgNames; 467 } 468 packagesToString(List<PackageStateInternal> pkgSettings)469 public static String packagesToString(List<PackageStateInternal> pkgSettings) { 470 StringBuilder sb = new StringBuilder(); 471 for (int index = 0; index < pkgSettings.size(); index++) { 472 if (sb.length() > 0) { 473 sb.append(", "); 474 } 475 sb.append(pkgSettings.get(index).getPackageName()); 476 } 477 return sb.toString(); 478 } 479 480 /** 481 * Requests that files preopted on a secondary system partition be copied to the data partition 482 * if possible. Note that the actual copying of the files is accomplished by init for security 483 * reasons. This simply requests that the copy takes place and awaits confirmation of its 484 * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy. 485 */ requestCopyPreoptedFiles()486 public static void requestCopyPreoptedFiles() { 487 final int WAIT_TIME_MS = 100; 488 final String CP_PREOPT_PROPERTY = "sys.cppreopt"; 489 if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { 490 SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); 491 // We will wait for up to 100 seconds. 492 final long timeStart = SystemClock.uptimeMillis(); 493 final long timeEnd = timeStart + 100 * 1000; 494 long timeNow = timeStart; 495 while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { 496 try { 497 Thread.sleep(WAIT_TIME_MS); 498 } catch (InterruptedException e) { 499 // Do nothing 500 } 501 timeNow = SystemClock.uptimeMillis(); 502 if (timeNow > timeEnd) { 503 SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); 504 Slog.wtf(TAG, "cppreopt did not finish!"); 505 break; 506 } 507 } 508 509 Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms"); 510 } 511 } 512 513 /** 514 * Dumps the dexopt state for the given package, or all packages if it is null. 515 */ dumpDexoptState( @onNull IndentingPrintWriter ipw, @Nullable String packageName)516 public static void dumpDexoptState( 517 @NonNull IndentingPrintWriter ipw, @Nullable String packageName) { 518 try (PackageManagerLocal.FilteredSnapshot snapshot = 519 getPackageManagerLocal().withFilteredSnapshot()) { 520 if (packageName != null) { 521 try { 522 DexOptHelper.getArtManagerLocal().dumpPackage(ipw, snapshot, packageName); 523 } catch (IllegalArgumentException e) { 524 // Package isn't found, but that should only happen due to race. 525 ipw.println(e); 526 } 527 } else { 528 DexOptHelper.getArtManagerLocal().dump(ipw, snapshot); 529 } 530 } 531 } 532 533 /** 534 * Returns the module names of the APEXes that contribute to bootclasspath. 535 */ getBcpApexes()536 private static List<String> getBcpApexes() { 537 String bcp = System.getenv("BOOTCLASSPATH"); 538 if (TextUtils.isEmpty(bcp)) { 539 Log.e(TAG, "Unable to get BOOTCLASSPATH"); 540 return List.of(); 541 } 542 543 ArrayList<String> bcpApexes = new ArrayList<>(); 544 for (String pathStr : bcp.split(":")) { 545 Path path = Paths.get(pathStr); 546 // Check if the path is in the format of `/apex/<apex-module-name>/...` and extract the 547 // apex module name from the path. 548 if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) { 549 bcpApexes.add(path.getName(1).toString()); 550 } 551 } 552 553 return bcpApexes; 554 } 555 556 /** 557 * Returns true of any of the APEXes that contribute to bootclasspath has changed during this 558 * boot. 559 */ hasBcpApexesChanged()560 private static boolean hasBcpApexesChanged() { 561 Set<String> bcpApexes = new HashSet<>(getBcpApexes()); 562 ApexManager apexManager = ApexManager.getInstance(); 563 for (ActiveApexInfo apexInfo : apexManager.getActiveApexInfos()) { 564 if (bcpApexes.contains(apexInfo.apexModuleName) && apexInfo.activeApexChanged) { 565 return true; 566 } 567 } 568 return false; 569 } 570 571 /** 572 * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization. 573 */ getDexUseManagerLocal()574 public static @Nullable DexUseManagerLocal getDexUseManagerLocal() { 575 try { 576 return LocalManagerRegistry.getManagerOrThrow(DexUseManagerLocal.class); 577 } catch (ManagerNotFoundException e) { 578 throw new RuntimeException(e); 579 } 580 } 581 582 private class DexoptDoneHandler implements ArtManagerLocal.DexoptDoneCallback { 583 /** 584 * Called after every package dexopt operation done by {@link ArtManagerLocal} (when ART 585 * Service is in use). 586 */ 587 @Override onDexoptDone(@onNull DexoptResult result)588 public void onDexoptDone(@NonNull DexoptResult result) { 589 switch (result.getReason()) { 590 case ReasonMapping.REASON_FIRST_BOOT: 591 case ReasonMapping.REASON_BOOT_AFTER_OTA: 592 case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE: 593 int numDexopted = 0; 594 int numSkipped = 0; 595 int numFailed = 0; 596 for (DexoptResult.PackageDexoptResult pkgRes : 597 result.getPackageDexoptResults()) { 598 switch (pkgRes.getStatus()) { 599 case DexoptResult.DEXOPT_PERFORMED: 600 numDexopted += 1; 601 break; 602 case DexoptResult.DEXOPT_SKIPPED: 603 numSkipped += 1; 604 break; 605 case DexoptResult.DEXOPT_FAILED: 606 numFailed += 1; 607 break; 608 } 609 } 610 611 reportBootDexopt(mBootDexoptStartTime, numDexopted, numSkipped, numFailed); 612 break; 613 } 614 615 for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) { 616 CompilerStats.PackageStats stats = 617 mPm.getOrCreateCompilerPackageStats(pkgRes.getPackageName()); 618 for (DexoptResult.DexContainerFileDexoptResult dexRes : 619 pkgRes.getDexContainerFileDexoptResults()) { 620 stats.setCompileTime( 621 dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis()); 622 } 623 } 624 625 synchronized (mPm.mLock) { 626 mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked()); 627 mPm.mCompilerStats.maybeWriteAsync(); 628 } 629 630 if (result.getReason().equals(ReasonMapping.REASON_INACTIVE)) { 631 for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) { 632 if (pkgRes.getStatus() == DexoptResult.DEXOPT_PERFORMED) { 633 long pkgSizeBytes = 0; 634 long pkgSizeBeforeBytes = 0; 635 for (DexoptResult.DexContainerFileDexoptResult dexRes : 636 pkgRes.getDexContainerFileDexoptResults()) { 637 long dexContainerSize = new File(dexRes.getDexContainerFile()).length(); 638 pkgSizeBytes += dexRes.getSizeBytes() + dexContainerSize; 639 pkgSizeBeforeBytes += dexRes.getSizeBeforeBytes() + dexContainerSize; 640 } 641 FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED, 642 pkgRes.getPackageName(), pkgSizeBeforeBytes, pkgSizeBytes, 643 false /* aggressive */); 644 } 645 } 646 } 647 648 var updatedPackages = new ArraySet<String>(); 649 for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) { 650 if (pkgRes.hasUpdatedArtifacts()) { 651 updatedPackages.add(pkgRes.getPackageName()); 652 } 653 } 654 if (!updatedPackages.isEmpty()) { 655 LocalServices.getService(PinnerService.class) 656 .update(updatedPackages, false /* force */); 657 } 658 } 659 } 660 661 /** 662 * Initializes {@link ArtManagerLocal} before {@link getArtManagerLocal} is called. 663 */ initializeArtManagerLocal( @onNull Context systemContext, @NonNull PackageManagerService pm)664 public static void initializeArtManagerLocal( 665 @NonNull Context systemContext, @NonNull PackageManagerService pm) { 666 ArtManagerLocal artManager = new ArtManagerLocal(systemContext); 667 artManager.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run, 668 pm.getDexOptHelper().new DexoptDoneHandler()); 669 LocalManagerRegistry.addManager(ArtManagerLocal.class, artManager); 670 sArtManagerLocalIsInitialized = true; 671 672 // Schedule the background job when boot is complete. This decouples us from when 673 // JobSchedulerService is initialized. 674 systemContext.registerReceiver(new BroadcastReceiver() { 675 @Override 676 public void onReceive(Context context, Intent intent) { 677 context.unregisterReceiver(this); 678 artManager.scheduleBackgroundDexoptJob(); 679 } 680 }, new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED)); 681 682 StagedApexObserver.registerForStagedApexUpdates(artManager); 683 } 684 685 /** 686 * Returns true if an {@link ArtManagerLocal} instance has been created. 687 * 688 * Avoid this function if at all possible, because it may hide initialization order problems. 689 */ artManagerLocalIsInitialized()690 public static boolean artManagerLocalIsInitialized() { 691 return sArtManagerLocalIsInitialized; 692 } 693 694 /** 695 * Returns the registered {@link ArtManagerLocal} instance, or else throws an unchecked error. 696 */ getArtManagerLocal()697 public static @NonNull ArtManagerLocal getArtManagerLocal() { 698 try { 699 return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class); 700 } catch (ManagerNotFoundException e) { 701 throw new RuntimeException(e); 702 } 703 } 704 705 /** 706 * Converts an ART Service {@link DexoptResult} to {@link DexOptResult}. 707 * 708 * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager. 709 */ 710 @DexOptResult convertToDexOptResult(DexoptResult result)711 private static int convertToDexOptResult(DexoptResult result) { 712 /*@DexoptResultStatus*/ int status = result.getFinalStatus(); 713 switch (status) { 714 case DexoptResult.DEXOPT_SKIPPED: 715 return PackageDexOptimizer.DEX_OPT_SKIPPED; 716 case DexoptResult.DEXOPT_FAILED: 717 return PackageDexOptimizer.DEX_OPT_FAILED; 718 case DexoptResult.DEXOPT_PERFORMED: 719 return PackageDexOptimizer.DEX_OPT_PERFORMED; 720 case DexoptResult.DEXOPT_CANCELLED: 721 return PackageDexOptimizer.DEX_OPT_CANCELLED; 722 default: 723 throw new IllegalArgumentException("DexoptResult for " 724 + result.getPackageDexoptResults().get(0).getPackageName() 725 + " has unsupported status " + status); 726 } 727 } 728 729 /** Returns DexoptOptions by the given InstallRequest. */ getDexoptOptionsByInstallRequest( InstallRequest installRequest, DexManager dexManager)730 private static DexoptOptions getDexoptOptionsByInstallRequest( 731 InstallRequest installRequest, DexManager dexManager) { 732 final PackageSetting ps = installRequest.getScannedPackageSetting(); 733 final String packageName = ps.getPackageName(); 734 final boolean isBackupOrRestore = 735 installRequest.getInstallReason() == INSTALL_REASON_DEVICE_RESTORE 736 || installRequest.getInstallReason() == INSTALL_REASON_DEVICE_SETUP; 737 final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE 738 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES 739 | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE 740 | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0); 741 // Compute the compilation reason from the installation scenario. 742 final int compilationReason = 743 dexManager.getCompilationReasonForInstallScenario( 744 installRequest.getInstallScenario()); 745 final AndroidPackage pkg = ps.getPkg(); 746 var options = new DexoptOptions(packageName, compilationReason, dexoptFlags); 747 if (installRequest.getDexoptCompilerFilter() != null) { 748 options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter()); 749 } else if (shouldSkipDexopt(installRequest)) { 750 options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP); 751 } 752 return options; 753 } 754 755 /** Perform dexopt if needed for the installation */ performDexoptIfNeeded( InstallRequest installRequest, DexManager dexManager, PackageManagerTracedLock.RawLock installLock)756 static void performDexoptIfNeeded( 757 InstallRequest installRequest, 758 DexManager dexManager, 759 PackageManagerTracedLock.RawLock installLock) { 760 if (!shouldCallArtService(installRequest)) { 761 return; 762 } 763 764 // dexopt can take long, and ArtService doesn't require installd, so we release the lock 765 // here and re-acquire the lock after dexopt is finished. 766 if (installLock != null) { 767 installLock.unlock(); 768 } 769 try { 770 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); 771 DexoptOptions dexoptOptions = 772 getDexoptOptionsByInstallRequest(installRequest, dexManager); 773 // Don't fail application installs if the dexopt step fails. 774 DexoptResult dexOptResult = 775 DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions); 776 installRequest.onDexoptFinished(dexOptResult); 777 } finally { 778 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 779 if (installLock != null) { 780 installLock.lock(); 781 } 782 } 783 } 784 785 /** Same as above, but runs asynchronously. */ performDexoptIfNeededAsync( InstallRequest installRequest, DexManager dexManager)786 static CompletableFuture<Void> performDexoptIfNeededAsync( 787 InstallRequest installRequest, DexManager dexManager) { 788 if (!shouldCallArtService(installRequest)) { 789 return CompletableFuture.completedFuture(null); 790 } 791 792 return CompletableFuture.runAsync( 793 () -> { 794 try { 795 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); 796 DexoptOptions dexoptOptions = 797 getDexoptOptionsByInstallRequest( 798 installRequest, dexManager); 799 // Don't fail application installs if the dexopt step fails. 800 // TODO(b/393076925): Make this async in ART Service. 801 DexoptResult dexOptResult = 802 DexOptHelper.dexoptPackageUsingArtService( 803 installRequest, dexoptOptions); 804 installRequest.onDexoptFinished(dexOptResult); 805 } finally { 806 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 807 } 808 }, 809 sDexoptExecutor) 810 .exceptionally( 811 (t) -> { 812 // This should never happen. A normal dexopt failure should result 813 // in a DexoptResult.DEXOPT_FAILED, not an exception. 814 Slog.wtf(TAG, "Dexopt encountered a fatal error", t); 815 return null; 816 }); 817 } 818 819 /** 820 * Use ArtService to perform dexopt by the given InstallRequest. 821 */ 822 static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest, 823 DexoptOptions dexoptOptions) { 824 final PackageSetting ps = installRequest.getScannedPackageSetting(); 825 final String packageName = ps.getPackageName(); 826 827 PackageSetting uncommittedPs = null; 828 if (Flags.improveInstallFreeze()) { 829 uncommittedPs = ps; 830 } 831 832 PackageManagerLocal packageManagerLocal = 833 LocalManagerRegistry.getManager(PackageManagerLocal.class); 834 try (PackageManagerLocal.FilteredSnapshot snapshot = 835 PackageManagerLocalImpl.withFilteredSnapshot(packageManagerLocal, 836 uncommittedPs)) { 837 boolean ignoreDexoptProfile = 838 (installRequest.getInstallFlags() 839 & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE) 840 != 0; 841 /*@DexoptFlags*/ int extraFlags = 842 ignoreDexoptProfile ? ArtFlags.FLAG_IGNORE_PROFILE : 0; 843 DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags); 844 DexoptResult dexOptResult = getArtManagerLocal().dexoptPackage( 845 snapshot, packageName, params); 846 847 return dexOptResult; 848 } 849 } 850 851 private static boolean shouldSkipDexopt(InstallRequest installRequest) { 852 PackageSetting ps = installRequest.getScannedPackageSetting(); 853 AndroidPackage pkg = ps.getPkg(); 854 boolean onIncremental = isIncrementalPath(ps.getPathString()); 855 return pkg == null || pkg.isDebuggable() || onIncremental; 856 } 857 858 /** 859 * Returns whether to call ART Service to perform dexopt for the given InstallRequest. Note that 860 * ART Service may still skip dexopt, depending on the specified compiler filter, compilation 861 * reason, and other conditions. 862 */ 863 private static boolean shouldCallArtService(InstallRequest installRequest) { 864 final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0); 865 // Historically, we did not dexopt instant apps, and we have no plan to do so in the 866 // future, so there is no need to call into ART Service. 867 final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0); 868 final PackageSetting ps = installRequest.getScannedPackageSetting(); 869 final AndroidPackage pkg = ps.getPkg(); 870 final boolean performDexOptForRollback = 871 !(installRequest.isRollback() 872 && installRequest 873 .getInstallSource() 874 .mInitiatingPackageName 875 .equals("android")); 876 877 // THINK TWICE when you add a new condition here. You probably want to add a condition to 878 // `shouldSkipDexopt` instead. In that way, ART Service will be called with the "skip" 879 // compiler filter and it will have the chance to decide whether to skip dexopt. 880 return !instantApp && pkg != null && !isApex && performDexOptForRollback; 881 } 882 883 private static class StagedApexObserver extends IStagedApexObserver.Stub { 884 private final @NonNull ArtManagerLocal mArtManager; 885 886 static void registerForStagedApexUpdates(@NonNull ArtManagerLocal artManager) { 887 IPackageManagerNative packageNative = IPackageManagerNative.Stub.asInterface( 888 ServiceManager.getService("package_native")); 889 if (packageNative == null) { 890 Log.e(TAG, "No IPackageManagerNative"); 891 return; 892 } 893 894 try { 895 packageNative.registerStagedApexObserver(new StagedApexObserver(artManager)); 896 } catch (RemoteException e) { 897 Log.e(TAG, "Failed to register staged apex observer", e); 898 } 899 } 900 901 private StagedApexObserver(@NonNull ArtManagerLocal artManager) { 902 mArtManager = artManager; 903 } 904 905 @Override 906 public void onApexStaged(@NonNull ApexStagedEvent event) { 907 mArtManager.onApexStaged(Arrays.stream(event.stagedApexInfos) 908 .map(info -> info.moduleName).toArray(String[]::new)); 909 } 910 } 911 } 912