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