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.art; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo; 20 21 import static com.android.server.art.ArtFileManager.ProfileLists; 22 import static com.android.server.art.ArtFileManager.UsableArtifactLists; 23 import static com.android.server.art.ArtFileManager.WritableArtifactLists; 24 import static com.android.server.art.DexMetadataHelper.DexMetadataInfo; 25 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo; 26 import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo; 27 import static com.android.server.art.ProfilePath.PrimaryCurProfilePath; 28 import static com.android.server.art.ProfilePath.WritableProfilePath; 29 import static com.android.server.art.ReasonMapping.BatchDexoptReason; 30 import static com.android.server.art.ReasonMapping.BootReason; 31 import static com.android.server.art.Utils.Abi; 32 import static com.android.server.art.Utils.InitProfileResult; 33 import static com.android.server.art.model.ArtFlags.GetStatusFlags; 34 import static com.android.server.art.model.ArtFlags.ScheduleStatus; 35 import static com.android.server.art.model.Config.Callback; 36 import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; 37 38 import android.annotation.CallbackExecutor; 39 import android.annotation.NonNull; 40 import android.annotation.Nullable; 41 import android.annotation.SystemApi; 42 import android.annotation.SystemService; 43 import android.app.ActivityManager; 44 import android.app.job.JobInfo; 45 import android.apphibernation.AppHibernationManager; 46 import android.content.BroadcastReceiver; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.content.IntentFilter; 50 import android.os.Binder; 51 import android.os.Build; 52 import android.os.CancellationSignal; 53 import android.os.ParcelFileDescriptor; 54 import android.os.Process; 55 import android.os.RemoteException; 56 import android.os.ServiceSpecificException; 57 import android.os.SystemProperties; 58 import android.os.UserHandle; 59 import android.os.UserManager; 60 import android.os.storage.StorageManager; 61 import android.system.ErrnoException; 62 import android.system.Os; 63 import android.system.OsConstants; 64 import android.text.TextUtils; 65 import android.util.Pair; 66 67 import androidx.annotation.RequiresApi; 68 69 import com.android.internal.annotations.VisibleForTesting; 70 import com.android.modules.utils.build.SdkLevel; 71 import com.android.server.LocalManagerRegistry; 72 import com.android.server.art.model.ArtFlags; 73 import com.android.server.art.model.ArtManagedFileStats; 74 import com.android.server.art.model.BatchDexoptParams; 75 import com.android.server.art.model.Config; 76 import com.android.server.art.model.DeleteResult; 77 import com.android.server.art.model.DetailedDexInfo; 78 import com.android.server.art.model.DexoptParams; 79 import com.android.server.art.model.DexoptResult; 80 import com.android.server.art.model.DexoptStatus; 81 import com.android.server.art.model.OperationProgress; 82 import com.android.server.art.prereboot.PreRebootStatsReporter; 83 import com.android.server.pm.PackageManagerLocal; 84 import com.android.server.pm.pkg.AndroidPackage; 85 import com.android.server.pm.pkg.AndroidPackageSplit; 86 import com.android.server.pm.pkg.PackageState; 87 88 import dalvik.system.DexFile; 89 90 import java.io.File; 91 import java.io.FileNotFoundException; 92 import java.io.IOException; 93 import java.io.PrintWriter; 94 import java.nio.file.Files; 95 import java.nio.file.Path; 96 import java.nio.file.Paths; 97 import java.util.ArrayList; 98 import java.util.Arrays; 99 import java.util.Collections; 100 import java.util.Comparator; 101 import java.util.HashMap; 102 import java.util.HashSet; 103 import java.util.List; 104 import java.util.Map; 105 import java.util.Objects; 106 import java.util.Set; 107 import java.util.concurrent.CompletableFuture; 108 import java.util.concurrent.Executor; 109 import java.util.concurrent.ExecutorService; 110 import java.util.concurrent.Executors; 111 import java.util.concurrent.LinkedBlockingQueue; 112 import java.util.concurrent.ThreadPoolExecutor; 113 import java.util.concurrent.TimeUnit; 114 import java.util.concurrent.locks.ReentrantReadWriteLock; 115 import java.util.function.Consumer; 116 import java.util.stream.Stream; 117 118 /** 119 * This class provides a system API for functionality provided by the ART module. 120 * 121 * Note: Although this class is the entry point of ART services, this class is not a {@link 122 * SystemService}, and it does not publish a binder. Instead, it is a module loaded by the 123 * system_server process, registered in {@link LocalManagerRegistry}. {@link LocalManagerRegistry} 124 * specifies that in-process module interfaces should be named with the suffix {@code ManagerLocal} 125 * for consistency. 126 * 127 * @hide 128 */ 129 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 130 public final class ArtManagerLocal { 131 private static final String[] CLASSPATHS_FOR_BOOT_IMAGE_PROFILE = { 132 "BOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}; 133 134 /** @hide */ 135 @VisibleForTesting public static final long DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES = 500_000_000; 136 137 @NonNull private final Injector mInjector; 138 139 private boolean mShouldCommitPreRebootStagedFiles = false; 140 141 // A temporary object for holding stats while staged files are being committed, used in two 142 // places: `onBoot` and the `BroadcastReceiver` of `ACTION_BOOT_COMPLETED`. 143 @Nullable private PreRebootStatsReporter.AfterRebootSession mStatsAfterRebootSession = null; 144 145 // A lock that prevents the cleanup from cleaning up dexopt temp files while dexopt is running. 146 // The method that does the cleanup should acquire a write lock; the methods that do dexopt 147 // should acquire a read lock. 148 @NonNull private ReentrantReadWriteLock mCleanupLock = new ReentrantReadWriteLock(); 149 150 @Deprecated ArtManagerLocal()151 public ArtManagerLocal() { 152 mInjector = new Injector(); 153 } 154 155 /** 156 * Creates an instance. 157 * 158 * Only {@code SystemServer} should create an instance and register it in {@link 159 * LocalManagerRegistry}. Other API users should obtain the instance from {@link 160 * LocalManagerRegistry}. 161 * 162 * @param context the system server context 163 * @throws NullPointerException if required dependencies are missing 164 */ 165 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) ArtManagerLocal(@onNull Context context)166 public ArtManagerLocal(@NonNull Context context) { 167 mInjector = new Injector(this, context); 168 } 169 170 /** @hide */ 171 @VisibleForTesting ArtManagerLocal(@onNull Injector injector)172 public ArtManagerLocal(@NonNull Injector injector) { 173 mInjector = injector; 174 } 175 176 /** 177 * Handles ART Service commands, which is a subset of `cmd package` commands. 178 * 179 * Note: This method is not an override of {@link Binder#handleShellCommand} because ART 180 * services does not publish a binder. Instead, it handles the commands forwarded by the 181 * `package` service. The semantics of the parameters are the same as {@link 182 * Binder#handleShellCommand}. 183 * 184 * @return zero on success, non-zero on internal error (e.g., I/O error) 185 * @throws SecurityException if the caller is not root 186 * @throws IllegalArgumentException if the arguments are illegal 187 * @see ArtShellCommand#printHelp(PrintWriter) 188 */ 189 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) handleShellCommand(@onNull Binder target, @NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)190 public int handleShellCommand(@NonNull Binder target, @NonNull ParcelFileDescriptor in, 191 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, 192 @NonNull String[] args) { 193 return new ArtShellCommand(this, mInjector.getPackageManagerLocal()) 194 .exec(target, in.getFileDescriptor(), out.getFileDescriptor(), 195 err.getFileDescriptor(), args); 196 } 197 198 /** Prints ART Service shell command help. */ 199 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) printShellCommandHelp(@onNull PrintWriter pw)200 public void printShellCommandHelp(@NonNull PrintWriter pw) { 201 ArtShellCommand.printHelp(pw); 202 } 203 204 /** 205 * Deletes dexopt artifacts (including cloud dexopt artifacts) of a package, for primary dex 206 * files and for secondary dex files. This includes VDEX, ODEX, ART, SDM, and SDC files. 207 * 208 * Also deletes runtime artifacts of the package, though they are not dexopt artifacts. 209 * 210 * @throws IllegalArgumentException if the package is not found or the flags are illegal 211 * @throws IllegalStateException if the operation encounters an error that should never happen 212 * (e.g., an internal logic error). 213 */ 214 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 215 @NonNull deleteDexoptArtifacts( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)216 public DeleteResult deleteDexoptArtifacts( 217 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 218 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 219 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 220 221 try (var pin = mInjector.createArtdPin()) { 222 long freedBytes = 0; 223 WritableArtifactLists list = 224 mInjector.getArtFileManager().getWritableArtifacts(pkgState, pkg, 225 ArtFileManager.Options.builder() 226 .setForPrimaryDex(true) 227 .setForSecondaryDex(true) 228 .build()); 229 for (ArtifactsPath artifacts : list.artifacts()) { 230 freedBytes += mInjector.getArtd().deleteArtifacts(artifacts); 231 } 232 for (RuntimeArtifactsPath runtimeArtifacts : list.runtimeArtifacts()) { 233 freedBytes += mInjector.getArtd().deleteRuntimeArtifacts(runtimeArtifacts); 234 } 235 for (SecureDexMetadataWithCompanionPaths sdmSdcFiles : list.sdmFiles()) { 236 freedBytes += mInjector.getArtd().deleteSdmSdcFiles(sdmSdcFiles); 237 } 238 return DeleteResult.create(freedBytes); 239 } catch (RemoteException e) { 240 Utils.logArtdException(e); 241 return DeleteResult.create(0 /* freedBytes */); 242 } 243 } 244 245 /** 246 * Returns the dexopt status of all known dex container files of a package, even if some of them 247 * aren't readable. 248 * 249 * Uses the default flags ({@link ArtFlags#defaultGetStatusFlags()}). 250 * 251 * @throws IllegalArgumentException if the package is not found or the flags are illegal 252 * @throws IllegalStateException if the operation encounters an error that should never happen 253 * (e.g., an internal logic error). 254 */ 255 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 256 @NonNull getDexoptStatus( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)257 public DexoptStatus getDexoptStatus( 258 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 259 return getDexoptStatus(snapshot, packageName, ArtFlags.defaultGetStatusFlags()); 260 } 261 262 /** 263 * Same as above, but allows to specify flags. 264 * 265 * @see #getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String) 266 */ 267 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 268 @NonNull getDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @GetStatusFlags int flags)269 public DexoptStatus getDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 270 @NonNull String packageName, @GetStatusFlags int flags) { 271 if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0 272 && (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) == 0) { 273 throw new IllegalArgumentException("Nothing to check"); 274 } 275 276 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 277 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 278 List<Pair<DetailedDexInfo, Abi>> dexAndAbis = 279 mInjector.getArtFileManager().getDexAndAbis(pkgState, pkg, 280 ArtFileManager.Options.builder() 281 .setForPrimaryDex((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0) 282 .setForSecondaryDex((flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0) 283 .build()); 284 285 try (var pin = mInjector.createArtdPin()) { 286 List<DexContainerFileDexoptStatus> statuses = new ArrayList<>(); 287 288 for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) { 289 DetailedDexInfo dexInfo = pair.first; 290 Abi abi = pair.second; 291 try { 292 GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus( 293 dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); 294 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 295 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), 296 abi.name(), result.compilerFilter, result.compilationReason, 297 result.locationDebugString)); 298 } catch (ServiceSpecificException e) { 299 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 300 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), 301 abi.name(), "error", "error", e.getMessage())); 302 } 303 } 304 305 return DexoptStatus.create(statuses); 306 } catch (RemoteException e) { 307 Utils.logArtdException(e); 308 List<DexContainerFileDexoptStatus> statuses = new ArrayList<>(); 309 for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) { 310 DetailedDexInfo dexInfo = pair.first; 311 Abi abi = pair.second; 312 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 313 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), abi.name(), 314 "error", "error", e.getMessage())); 315 } 316 return DexoptStatus.create(statuses); 317 } 318 } 319 320 /** 321 * Clear the profiles that are collected locally for the given package, including the profiles 322 * for primary and secondary dex files. More specifically, it clears reference profiles and 323 * current profiles. External profiles (e.g., cloud profiles) will be kept. 324 * 325 * @throws IllegalArgumentException if the package is not found or the flags are illegal 326 * @throws IllegalStateException if the operation encounters an error that should never happen 327 * (e.g., an internal logic error). 328 */ 329 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 330 @NonNull clearAppProfiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)331 public void clearAppProfiles( 332 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 333 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 334 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 335 336 try (var pin = mInjector.createArtdPin()) { 337 // We want to delete as many profiles as possible, so this deletes profiles of all known 338 // secondary dex files. If there are unknown secondary dex files, their profiles will be 339 // deleted by `cleanup`. 340 ProfileLists list = mInjector.getArtFileManager().getProfiles(pkgState, pkg, 341 ArtFileManager.Options.builder() 342 .setForPrimaryDex(true) 343 .setForSecondaryDex(true) 344 .build()); 345 for (ProfilePath profile : list.allProfiles()) { 346 mInjector.getArtd().deleteProfile(profile); 347 } 348 } catch (RemoteException e) { 349 Utils.logArtdException(e); 350 } 351 } 352 353 /** 354 * Dexopts a package. The time this operation takes ranges from a few milliseconds to several 355 * minutes, depending on the params and the code size of the package. 356 * 357 * When dexopt is successfully performed for a dex container file, this operation also deletes 358 * the corresponding runtime artifacts (the ART files in the package's data directory, which are 359 * generated by the runtime, not by dexopt). 360 * 361 * When this operation ends (either completed or cancelled), callbacks added by {@link 362 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called. 363 * 364 * @throws IllegalArgumentException if the package is not found or the params are illegal 365 * @throws IllegalStateException if the operation encounters an error that should never happen 366 * (e.g., an internal logic error). 367 */ 368 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 369 @NonNull dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params)370 public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 371 @NonNull String packageName, @NonNull DexoptParams params) { 372 var cancellationSignal = new CancellationSignal(); 373 return dexoptPackage(snapshot, packageName, params, cancellationSignal); 374 } 375 376 /** 377 * Same as above, but supports cancellation. 378 * 379 * @see #dexoptPackage(PackageManagerLocal.FilteredSnapshot, String, DexoptParams) 380 */ 381 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 382 @NonNull dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal)383 public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 384 @NonNull String packageName, @NonNull DexoptParams params, 385 @NonNull CancellationSignal cancellationSignal) { 386 mCleanupLock.readLock().lock(); 387 try (var pin = mInjector.createArtdPin()) { 388 return mInjector.getDexoptHelper().dexopt( 389 snapshot, List.of(packageName), params, cancellationSignal, Runnable::run); 390 } finally { 391 mCleanupLock.readLock().unlock(); 392 } 393 } 394 395 /** 396 * Resets the dexopt state of the package as if the package is newly installed without cloud 397 * dexopt artifacts (SDM files). 398 * 399 * More specifically, 400 * - It clears current profiles, reference profiles, and all dexopt artifacts (including cloud 401 * dexopt artifacts). 402 * - If there is an external profile (e.g., a cloud profile), the reference profile will be 403 * re-created from that profile, and dexopt artifacts will be regenerated for that profile. 404 * 405 * For secondary dex files, it clears all profiles and dexopt artifacts without regeneration 406 * because secondary dex files are supposed to be unknown at install time. 407 * 408 * @hide 409 */ 410 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 411 @NonNull resetDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull CancellationSignal cancellationSignal)412 public DexoptResult resetDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 413 @NonNull String packageName, @NonNull CancellationSignal cancellationSignal) { 414 // We must delete the artifacts for primary dex files beforehand rather than relying on 415 // `dexoptPackage` to replace them because: 416 // - If dexopt is not needed after the deletion, then we shouldn't run dexopt at all. For 417 // example, when we have a DM file that contains a VDEX file but doesn't contain a cloud 418 // profile, this happens. Note that this is more about correctness rather than 419 // performance. 420 // - We don't want the existing artifacts to affect dexopt. For example, the existing VDEX 421 // file should not be an input VDEX. 422 // 423 // We delete the artifacts for secondary dex files and `dexoptPackage` won't re-generate 424 // them because `dexoptPackage` for `REASON_INSTALL` is for primary dex only. This is 425 // intentional because secondary dex files are supposed to be unknown at install time. 426 deleteDexoptArtifacts(snapshot, packageName); 427 clearAppProfiles(snapshot, packageName); 428 429 // Re-generate artifacts for primary dex files if needed. 430 return dexoptPackage(snapshot, packageName, 431 new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(), cancellationSignal); 432 } 433 434 /** 435 * Runs batch dexopt for the given reason. 436 * 437 * This is called by ART Service automatically during boot / background dexopt. 438 * 439 * The list of packages and options are determined by {@code reason}, and can be overridden by 440 * {@link #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. 441 * 442 * The dexopt is done in a thread pool. The number of packages being dexopted 443 * simultaneously can be configured by system property {@code pm.dexopt.<reason>.concurrency} 444 * (e.g., {@code pm.dexopt.bg-dexopt.concurrency=4}), and the number of threads for each {@code 445 * dex2oat} invocation can be configured by system property {@code dalvik.vm.*dex2oat-threads} 446 * (e.g., {@code dalvik.vm.background-dex2oat-threads=4}). I.e., the maximum number of 447 * concurrent threads is the product of the two system properties. Note that the physical core 448 * usage is always bound by {@code dalvik.vm.*dex2oat-cpu-set} regardless of the number of 449 * threads. 450 * 451 * When this operation ends (either completed or cancelled), callbacks added by {@link 452 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called. 453 * 454 * If the storage is nearly low, and {@code reason} is {@link ReasonMapping#REASON_BG_DEXOPT}, 455 * it may also downgrade some inactive packages to a less optimized compiler filter, specified 456 * by the system property {@code pm.dexopt.inactive} (typically "verify"), to free up some 457 * space. This feature is only enabled when the system property {@code 458 * pm.dexopt.downgrade_after_inactive_days} is set. The space threshold to trigger this feature 459 * is the Storage Manager's low space threshold plus {@link 460 * #DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES}. The concurrency can be configured by system property 461 * {@code pm.dexopt.bg-dexopt.concurrency}. The packages in the list provided by 462 * {@link BatchDexoptStartCallback} for {@link ReasonMapping#REASON_BG_DEXOPT} are never 463 * downgraded. 464 * 465 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 466 * @param reason determines the default list of packages and options 467 * @param cancellationSignal provides the ability to cancel this operation 468 * @param processCallbackExecutor the executor to call {@code progressCallback} 469 * @param progressCallbacks a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to 470 * the callback that is called repeatedly whenever there is an update on the progress 471 * @return a mapping from an integer, in {@link ArtFlags.BatchDexoptPass}, to the dexopt result. 472 * @throws IllegalStateException if the operation encounters an error that should never happen 473 * (e.g., an internal logic error), or the callback set by {@link 474 * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} provides invalid 475 * params. 476 * 477 * @hide 478 */ 479 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 480 @NonNull dexoptPackages( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks)481 public Map<Integer, DexoptResult> dexoptPackages( 482 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 483 @NonNull @BatchDexoptReason String reason, 484 @NonNull CancellationSignal cancellationSignal, 485 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 486 @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks) { 487 return dexoptPackagesWithParams(snapshot, reason, cancellationSignal, 488 progressCallbackExecutor, progressCallbacks, null /* params */); 489 } 490 491 /** @hide */ 492 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 493 @NonNull getBatchDexoptParams( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull CancellationSignal cancellationSignal)494 public BatchDexoptParams getBatchDexoptParams( 495 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 496 @NonNull @BatchDexoptReason String reason, 497 @NonNull CancellationSignal cancellationSignal) { 498 List<String> defaultPackages = 499 Collections.unmodifiableList(getDefaultPackages(snapshot, reason)); 500 DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build(); 501 var builder = new BatchDexoptParams.Builder(defaultPackages, defaultDexoptParams); 502 Callback<BatchDexoptStartCallback, Void> callback = 503 mInjector.getConfig().getBatchDexoptStartCallback(); 504 if (callback != null) { 505 Utils.executeAndWait(callback.executor(), () -> { 506 callback.get().onBatchDexoptStart( 507 snapshot, reason, defaultPackages, builder, cancellationSignal); 508 }); 509 } 510 BatchDexoptParams params = builder.build(); 511 DexoptParams dexoptParams = params.getDexoptParams(); 512 Utils.check(dexoptParams.getReason().equals(reason)); 513 if (dexoptParams.getSplitName() != null) { 514 AsLog.w("`setSplitName` is not supported in `BatchDexoptStartCallback`. The value is " 515 + "ignored"); 516 params = builder.setDexoptParams(dexoptParams.toBuilder().setSplitName(null).build()) 517 .build(); 518 } 519 return params; 520 } 521 522 /** @hide */ 523 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 524 @NonNull dexoptPackagesWithParams( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull CancellationSignal cancellationSignal, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks, @Nullable BatchDexoptParams params)525 public Map<Integer, DexoptResult> dexoptPackagesWithParams( 526 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 527 @NonNull @BatchDexoptReason String reason, 528 @NonNull CancellationSignal cancellationSignal, 529 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 530 @Nullable Map<Integer, Consumer<OperationProgress>> progressCallbacks, 531 @Nullable BatchDexoptParams params) { 532 if (params == null) { 533 params = getBatchDexoptParams(snapshot, reason, cancellationSignal); 534 } 535 ExecutorService dexoptExecutor = 536 Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason)); 537 Map<Integer, DexoptResult> dexoptResults = new HashMap<>(); 538 mCleanupLock.readLock().lock(); 539 try (var pin = mInjector.createArtdPin()) { 540 if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) { 541 DexoptResult downgradeResult = maybeDowngradePackages(snapshot, 542 new HashSet<>(params.getPackages()) /* excludedPackages */, 543 cancellationSignal, dexoptExecutor, progressCallbackExecutor, 544 progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_DOWNGRADE) 545 : null); 546 if (downgradeResult != null) { 547 dexoptResults.put(ArtFlags.PASS_DOWNGRADE, downgradeResult); 548 } 549 } 550 AsLog.i("Dexopting " + params.getPackages().size() + " packages with reason=" + reason); 551 DexoptResult mainResult = mInjector.getDexoptHelper().dexopt(snapshot, 552 params.getPackages(), params.getDexoptParams(), cancellationSignal, 553 dexoptExecutor, progressCallbackExecutor, 554 progressCallbacks != null ? progressCallbacks.get(ArtFlags.PASS_MAIN) : null); 555 dexoptResults.put(ArtFlags.PASS_MAIN, mainResult); 556 if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) { 557 DexoptResult supplementaryResult = maybeDexoptPackagesSupplementaryPass(snapshot, 558 mainResult, params.getDexoptParams(), cancellationSignal, dexoptExecutor, 559 progressCallbackExecutor, 560 progressCallbacks != null 561 ? progressCallbacks.get(ArtFlags.PASS_SUPPLEMENTARY) 562 : null); 563 if (supplementaryResult != null) { 564 dexoptResults.put(ArtFlags.PASS_SUPPLEMENTARY, supplementaryResult); 565 } 566 } 567 return dexoptResults; 568 } finally { 569 mCleanupLock.readLock().unlock(); 570 dexoptExecutor.shutdown(); 571 } 572 } 573 574 /** 575 * Overrides the default params for {@link #dexoptPackages}. This method is thread-safe. 576 * 577 * This method gives users the opportunity to change the behavior of {@link #dexoptPackages}, 578 * which is called by ART Service automatically during boot / background dexopt. 579 * 580 * If this method is not called, the default list of packages and options determined by {@code 581 * reason} will be used. 582 */ 583 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) setBatchDexoptStartCallback(@onNull @allbackExecutor Executor executor, @NonNull BatchDexoptStartCallback callback)584 public void setBatchDexoptStartCallback(@NonNull @CallbackExecutor Executor executor, 585 @NonNull BatchDexoptStartCallback callback) { 586 mInjector.getConfig().setBatchDexoptStartCallback(executor, callback); 587 } 588 589 /** 590 * Clears the callback set by {@link 591 * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. This method is 592 * thread-safe. 593 */ 594 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) clearBatchDexoptStartCallback()595 public void clearBatchDexoptStartCallback() { 596 mInjector.getConfig().clearBatchDexoptStartCallback(); 597 } 598 599 /** 600 * Schedules a background dexopt job. Does nothing if the job is already scheduled. 601 * 602 * Use this method if you want the system to automatically determine the best time to run 603 * dexopt. 604 * 605 * The job will be run by the job scheduler. The job scheduling configuration can be overridden 606 * by {@link 607 * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. By 608 * default, it runs periodically (at most once a day) when all the following constraints are 609 * meet. 610 * 611 * <ul> 612 * <li>The device is idling. (see {@link JobInfo.Builder#setRequiresDeviceIdle(boolean)}) 613 * <li>The device is charging. (see {@link JobInfo.Builder#setRequiresCharging(boolean)}) 614 * <li>The battery level is not low. 615 * (see {@link JobInfo.Builder#setRequiresBatteryNotLow(boolean)}) 616 * </ul> 617 * 618 * When the job is running, it may be cancelled by the job scheduler immediately whenever one of 619 * the constraints above is no longer met or cancelled by the {@link 620 * #cancelBackgroundDexoptJob()} API. The job scheduler retries it with the default retry policy 621 * (30 seconds, exponential, capped at 5hrs). 622 * 623 * See {@link #dexoptPackages} for how to customize the behavior of the job. 624 * 625 * When the job ends (either completed or cancelled), the result is sent to the callbacks added 626 * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the 627 * reason {@link ReasonMapping#REASON_BG_DEXOPT}. 628 * 629 * @throws RuntimeException if called during boot before the job scheduler service has started. 630 */ 631 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) scheduleBackgroundDexoptJob()632 public @ScheduleStatus int scheduleBackgroundDexoptJob() { 633 return mInjector.getBackgroundDexoptJob().schedule(); 634 } 635 636 /** 637 * Unschedules the background dexopt job scheduled by {@link #scheduleBackgroundDexoptJob()}. 638 * Does nothing if the job is not scheduled. 639 * 640 * Use this method if you no longer want the system to automatically run dexopt. 641 * 642 * If the job is already started by the job scheduler and is running, it will be cancelled 643 * immediately, and the result sent to the callbacks added by {@link 644 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link 645 * DexoptResult#DEXOPT_CANCELLED}. Note that a job started by {@link 646 * #startBackgroundDexoptJob()} will not be cancelled by this method. 647 */ 648 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) unscheduleBackgroundDexoptJob()649 public void unscheduleBackgroundDexoptJob() { 650 mInjector.getBackgroundDexoptJob().unschedule(); 651 } 652 653 /** 654 * Overrides the configuration of the background dexopt job. This method is thread-safe. 655 */ 656 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) setScheduleBackgroundDexoptJobCallback(@onNull @allbackExecutor Executor executor, @NonNull ScheduleBackgroundDexoptJobCallback callback)657 public void setScheduleBackgroundDexoptJobCallback(@NonNull @CallbackExecutor Executor executor, 658 @NonNull ScheduleBackgroundDexoptJobCallback callback) { 659 mInjector.getConfig().setScheduleBackgroundDexoptJobCallback(executor, callback); 660 } 661 662 /** 663 * Clears the callback set by {@link 664 * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. This 665 * method is thread-safe. 666 */ 667 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) clearScheduleBackgroundDexoptJobCallback()668 public void clearScheduleBackgroundDexoptJobCallback() { 669 mInjector.getConfig().clearScheduleBackgroundDexoptJobCallback(); 670 } 671 672 /** 673 * Manually starts a background dexopt job. Does nothing if a job is already started by this 674 * method or by the job scheduler. This method is not blocking. 675 * 676 * Unlike the job started by job scheduler, the job started by this method does not respect 677 * constraints described in {@link #scheduleBackgroundDexoptJob()}, and hence will not be 678 * cancelled when they aren't met. 679 * 680 * See {@link #dexoptPackages} for how to customize the behavior of the job. 681 * 682 * When the job ends (either completed or cancelled), the result is sent to the callbacks added 683 * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the 684 * reason {@link ReasonMapping#REASON_BG_DEXOPT}. 685 */ 686 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) startBackgroundDexoptJob()687 public void startBackgroundDexoptJob() { 688 mInjector.getBackgroundDexoptJob().start(); 689 } 690 691 /** 692 * Same as above, but also returns a {@link CompletableFuture}. 693 * 694 * @hide 695 */ 696 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 697 @NonNull startBackgroundDexoptJobAndReturnFuture()698 public CompletableFuture<BackgroundDexoptJob.Result> startBackgroundDexoptJobAndReturnFuture() { 699 return mInjector.getBackgroundDexoptJob().start(); 700 } 701 702 /** 703 * Returns the running background dexopt job, or null of no background dexopt job is running. 704 * 705 * @hide 706 */ 707 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 708 @Nullable getRunningBackgroundDexoptJob()709 public CompletableFuture<BackgroundDexoptJob.Result> getRunningBackgroundDexoptJob() { 710 return mInjector.getBackgroundDexoptJob().get(); 711 } 712 713 /** 714 * Cancels the running background dexopt job started by the job scheduler or by {@link 715 * #startBackgroundDexoptJob()}. Does nothing if the job is not running. This method is not 716 * blocking. 717 * 718 * The result sent to the callbacks added by {@link 719 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link 720 * DexoptResult#DEXOPT_CANCELLED}. 721 */ 722 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) cancelBackgroundDexoptJob()723 public void cancelBackgroundDexoptJob() { 724 mInjector.getBackgroundDexoptJob().cancel(); 725 } 726 727 /** 728 * Adds a global listener that listens to any result of dexopting package(s), no matter run 729 * manually or automatically. Calling this method multiple times with different callbacks is 730 * allowed. Callbacks are executed in the same order as the one in which they were added. This 731 * method is thread-safe. 732 * 733 * @param onlyIncludeUpdates if true, the results passed to the callback will only contain 734 * packages that have any update, and the callback won't be called with results that 735 * don't have any update. 736 * @throws IllegalStateException if the same callback instance is already added 737 */ 738 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) addDexoptDoneCallback(boolean onlyIncludeUpdates, @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback)739 public void addDexoptDoneCallback(boolean onlyIncludeUpdates, 740 @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback) { 741 mInjector.getConfig().addDexoptDoneCallback(onlyIncludeUpdates, executor, callback); 742 } 743 744 /** 745 * Removes the listener added by {@link 746 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)}. Does nothing if the 747 * callback was not added. This method is thread-safe. 748 */ 749 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) removeDexoptDoneCallback(@onNull DexoptDoneCallback callback)750 public void removeDexoptDoneCallback(@NonNull DexoptDoneCallback callback) { 751 mInjector.getConfig().removeDexoptDoneCallback(callback); 752 } 753 754 /** 755 * Snapshots the profile of the given app split. The profile snapshot is the aggregation of all 756 * existing profiles of the app split (all current user profiles and the reference profile). 757 * 758 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 759 * @param packageName the name of the app that owns the profile 760 * @param splitName see {@link AndroidPackageSplit#getName()} 761 * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The 762 * caller is responsible for closing it. Note that the content may be empty. 763 * @throws IllegalArgumentException if the package or the split is not found 764 * @throws IllegalStateException if the operation encounters an error that should never happen 765 * (e.g., an internal logic error). 766 * @throws SnapshotProfileException if the operation encounters an error that the caller should 767 * handle (e.g., an I/O error, a sub-process crash). 768 */ 769 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 770 @NonNull snapshotAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName)771 public ParcelFileDescriptor snapshotAppProfile( 772 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 773 @Nullable String splitName) throws SnapshotProfileException { 774 var options = new MergeProfileOptions(); 775 options.forceMerge = true; 776 return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options); 777 } 778 779 /** 780 * Same as above, but outputs in text format. 781 * 782 * @hide 783 */ 784 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 785 @NonNull dumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, boolean dumpClassesAndMethods)786 public ParcelFileDescriptor dumpAppProfile( 787 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 788 @Nullable String splitName, boolean dumpClassesAndMethods) 789 throws SnapshotProfileException { 790 var options = new MergeProfileOptions(); 791 options.dumpOnly = !dumpClassesAndMethods; 792 options.dumpClassesAndMethods = dumpClassesAndMethods; 793 return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options); 794 } 795 796 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 797 @NonNull snapshotOrDumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, @NonNull MergeProfileOptions options)798 private ParcelFileDescriptor snapshotOrDumpAppProfile( 799 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 800 @Nullable String splitName, @NonNull MergeProfileOptions options) 801 throws SnapshotProfileException { 802 try (var pin = mInjector.createArtdPin()) { 803 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 804 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 805 PrimaryDexInfo dexInfo = PrimaryDexUtils.getDexInfoBySplitName(pkg, splitName); 806 DexMetadataPath dmPath = AidlUtils.buildDexMetadataPath(dexInfo.dexPath()); 807 DexMetadataInfo dmInfo = mInjector.getDexMetadataHelper().getDexMetadataInfo(dmPath); 808 809 List<ProfilePath> profiles = new ArrayList<>(); 810 811 // Doesn't support Pre-reboot. 812 InitProfileResult result = Utils.getOrInitReferenceProfile(mInjector.getArtd(), 813 dexInfo.dexPath(), 814 PrimaryDexUtils.buildRefProfilePathAsInput(pkgState, dexInfo), 815 PrimaryDexUtils.getExternalProfiles(dexInfo), 816 dmInfo.config().getEnableEmbeddedProfile(), 817 PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, 818 Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */)); 819 if (!result.externalProfileErrors().isEmpty()) { 820 AsLog.e("Error occurred when initializing from external profiles: " 821 + result.externalProfileErrors()); 822 } 823 824 ProfilePath refProfile = result.profile(); 825 826 if (refProfile != null) { 827 profiles.add(refProfile); 828 } 829 830 profiles.addAll( 831 PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo)); 832 833 // Doesn't support Pre-reboot. 834 OutputProfile output = 835 PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, 836 Process.SYSTEM_UID, false /* isPublic */, false /* isPreReboot */); 837 838 try { 839 return mergeProfilesAndGetFd(profiles, output, List.of(dexInfo.dexPath()), options); 840 } finally { 841 if (refProfile != null && refProfile.getTag() == ProfilePath.tmpProfilePath) { 842 mInjector.getArtd().deleteProfile(refProfile); 843 } 844 } 845 } catch (RemoteException e) { 846 throw new SnapshotProfileException(e); 847 } 848 } 849 850 /** 851 * Snapshots the boot image profile 852 * (https://source.android.com/docs/core/bootloader/boot-image-profiles). The profile snapshot 853 * is the aggregation of all existing profiles on the device (all current user profiles and 854 * reference profiles) of all apps and the system server filtered by applicable classpaths. 855 * 856 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 857 * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The 858 * caller is responsible for closing it. Note that the content may be empty. 859 * @throws IllegalStateException if the operation encounters an error that should never happen 860 * (e.g., an internal logic error). 861 * @throws SnapshotProfileException if the operation encounters an error that the caller should 862 * handle (e.g., an I/O error, a sub-process crash). 863 */ 864 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 865 @NonNull snapshotBootImageProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot)866 public ParcelFileDescriptor snapshotBootImageProfile( 867 @NonNull PackageManagerLocal.FilteredSnapshot snapshot) 868 throws SnapshotProfileException { 869 if (!Constants.isBootImageProfilingEnabled()) { 870 throw new SnapshotProfileException("Boot image profiling not enabled"); 871 } 872 873 List<ProfilePath> profiles = new ArrayList<>(); 874 875 // System server profiles. 876 profiles.add(AidlUtils.buildProfilePathForPrimaryRefAsInput( 877 Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY)); 878 for (UserHandle handle : 879 mInjector.getUserManager().getUserHandles(true /* excludeDying */)) { 880 profiles.add(AidlUtils.buildProfilePathForPrimaryCur(handle.getIdentifier(), 881 Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY)); 882 } 883 884 // App profiles. 885 snapshot.getPackageStates().forEach((packageName, appPkgState) -> { 886 // Hibernating apps can still provide useful profile contents, so skip the hibernation 887 // check. 888 if (Utils.canDexoptPackage(appPkgState, null /* appHibernationManager */)) { 889 AndroidPackage appPkg = Utils.getPackageOrThrow(appPkgState); 890 ProfileLists list = mInjector.getArtFileManager().getProfiles(appPkgState, appPkg, 891 ArtFileManager.Options.builder().setForPrimaryDex(true).build()); 892 profiles.addAll(list.allProfiles()); 893 } 894 }); 895 896 // Doesn't support Pre-reboot. 897 OutputProfile output = AidlUtils.buildOutputProfileForPrimary(Utils.PLATFORM_PACKAGE_NAME, 898 PrimaryDexUtils.PROFILE_PRIMARY, Process.SYSTEM_UID, Process.SYSTEM_UID, 899 false /* isPublic */, false /* isPreReboot */); 900 901 List<String> dexPaths = Arrays.stream(CLASSPATHS_FOR_BOOT_IMAGE_PROFILE) 902 .map(envVar -> Constants.getenv(envVar)) 903 .filter(classpath -> !TextUtils.isEmpty(classpath)) 904 .flatMap(classpath -> Arrays.stream(classpath.split(":"))) 905 .toList(); 906 907 var options = new MergeProfileOptions(); 908 options.forceMerge = true; 909 options.forBootImage = true; 910 911 try (var pin = mInjector.createArtdPin()) { 912 return mergeProfilesAndGetFd(profiles, output, dexPaths, options); 913 } 914 } 915 916 /** 917 * Notifies ART Service that this is a boot that falls into one of the categories listed in 918 * {@link BootReason}. The current behavior is that ART Service goes through all recently used 919 * packages and dexopts those that are not dexopted. This might change in the future. 920 * 921 * This method is blocking. It takes about 30 seconds to a few minutes. During execution, {@code 922 * progressCallback} is repeatedly called whenever there is an update on the progress. 923 * 924 * See {@link #dexoptPackages} for how to customize the behavior. 925 */ 926 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) onBoot(@onNull @ootReason String bootReason, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)927 public void onBoot(@NonNull @BootReason String bootReason, 928 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 929 @Nullable Consumer<OperationProgress> progressCallback) { 930 AsLog.d("onBoot: reason=" + bootReason); 931 try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) { 932 if ((bootReason.equals(ReasonMapping.REASON_BOOT_AFTER_OTA) 933 || bootReason.equals(ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE)) 934 && SdkLevel.isAtLeastV()) { 935 // The staged files have to be committed in two phases, one during boot, for primary 936 // dex files, and another after boot complete, for secondary dex files. We need to 937 // commit files for primary dex files early because apps will start using them as 938 // soon as the package manager is initialized. We need to wait until boot complete 939 // to commit files for secondary dex files because they are not decrypted before 940 // then. 941 mShouldCommitPreRebootStagedFiles = true; 942 mStatsAfterRebootSession = 943 mInjector.getPreRebootStatsReporter().new AfterRebootSession(); 944 commitPreRebootStagedFiles(snapshot, false /* forSecondary */); 945 } 946 dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor, 947 progressCallback != null ? Map.of(ArtFlags.PASS_MAIN, progressCallback) : null); 948 } 949 } 950 951 /** 952 * Notifies this class that {@link Context#registerReceiver} is ready for use. 953 * 954 * Should be used by {@link DexUseManagerLocal} ONLY. 955 * 956 * @hide 957 */ 958 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) systemReady()959 void systemReady() { 960 AsLog.d("systemReady: mShouldCommitPreRebootStagedFiles=" 961 + mShouldCommitPreRebootStagedFiles); 962 if (mShouldCommitPreRebootStagedFiles) { 963 mInjector.getContext().registerReceiver(new BroadcastReceiver() { 964 @Override 965 public void onReceive(Context context, Intent intent) { 966 AsLog.d("systemReady.onReceive"); 967 context.unregisterReceiver(this); 968 if (!SdkLevel.isAtLeastV()) { 969 throw new IllegalStateException("Broadcast receiver unexpectedly called"); 970 } 971 try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) { 972 commitPreRebootStagedFiles(snapshot, true /* forSecondary */); 973 } 974 mStatsAfterRebootSession.reportAsync(); 975 mStatsAfterRebootSession = null; 976 // OtaPreRebootDexoptTest looks for this log message. 977 AsLog.d("Pre-reboot staged files committed"); 978 } 979 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 980 } 981 } 982 983 /** 984 * Notifies ART Service that there are apexes staged for installation on next reboot (see 985 * <a href="https://source.android.com/docs/core/ota/apex#apex-manager">the update sequence of 986 * an APEX</a>). ART Service may use this to schedule a pre-reboot dexopt job. This might change 987 * in the future. 988 * 989 * This immediately returns after scheduling the job and doesn't wait for the job to run. 990 * 991 * @param stagedApexModuleNames The <b>module names</b> of the staged apexes, corresponding to 992 * the directory beneath /apex, e.g., {@code com.android.art} (not the <b>package 993 * names</b>, e.g., {@code com.google.android.art}). 994 */ 995 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) onApexStaged(@onNull String[] stagedApexModuleNames)996 public void onApexStaged(@NonNull String[] stagedApexModuleNames) { 997 AsLog.d("onApexStaged"); 998 mInjector.getPreRebootDexoptJob().onUpdateReady(null /* otaSlot */); 999 } 1000 1001 /** 1002 * Dumps the dexopt state of all packages in text format for debugging purposes. 1003 * 1004 * There are no stability guarantees for the output format. 1005 * 1006 * @throws IllegalStateException if the operation encounters an error that should never happen 1007 * (e.g., an internal logic error). 1008 */ 1009 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) dump( @onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot)1010 public void dump( 1011 @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) { 1012 try (var pin = mInjector.createArtdPin()) { 1013 new DumpHelper(this).dump(pw, snapshot); 1014 } 1015 } 1016 1017 /** 1018 * Dumps the dexopt state of the given package in text format for debugging purposes. 1019 * 1020 * There are no stability guarantees for the output format. 1021 * 1022 * @throws IllegalArgumentException if the package is not found 1023 * @throws IllegalStateException if the operation encounters an error that should never happen 1024 * (e.g., an internal logic error). 1025 */ 1026 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) dumpPackage(@onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)1027 public void dumpPackage(@NonNull PrintWriter pw, 1028 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 1029 try (var pin = mInjector.createArtdPin()) { 1030 new DumpHelper(this).dumpPackage( 1031 pw, snapshot, Utils.getPackageStateOrThrow(snapshot, packageName)); 1032 } 1033 } 1034 1035 /** 1036 * Returns the statistics of the files managed by ART of a package. 1037 * 1038 * @throws IllegalArgumentException if the package is not found 1039 * @throws IllegalStateException if the operation encounters an error that should never happen 1040 * (e.g., an internal logic error). 1041 */ 1042 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1043 @NonNull getArtManagedFileStats( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)1044 public ArtManagedFileStats getArtManagedFileStats( 1045 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 1046 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 1047 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 1048 1049 try (var pin = mInjector.createArtdPin()) { 1050 long artifactsSize = 0; 1051 long refProfilesSize = 0; 1052 long curProfilesSize = 0; 1053 IArtd artd = mInjector.getArtd(); 1054 1055 UsableArtifactLists artifactLists = 1056 mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg); 1057 for (ArtifactsPath artifacts : artifactLists.artifacts()) { 1058 artifactsSize += artd.getArtifactsSize(artifacts); 1059 } 1060 for (VdexPath vdexFile : artifactLists.vdexFiles()) { 1061 artifactsSize += artd.getVdexFileSize(vdexFile); 1062 } 1063 for (RuntimeArtifactsPath runtimeArtifacts : artifactLists.runtimeArtifacts()) { 1064 artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts); 1065 } 1066 for (SecureDexMetadataWithCompanionPaths sdmFile : artifactLists.sdmFiles()) { 1067 // We don't count SDC files because they are presumed to be tiny. 1068 artifactsSize += artd.getSdmFileSize(sdmFile); 1069 } 1070 1071 ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg, 1072 ArtFileManager.Options.builder() 1073 .setForPrimaryDex(true) 1074 .setForSecondaryDex(true) 1075 .setExcludeForObsoleteDexesAndLoaders(true) 1076 .build()); 1077 for (ProfilePath profile : profileLists.refProfiles()) { 1078 refProfilesSize += artd.getProfileSize(profile); 1079 } 1080 for (ProfilePath profile : profileLists.curProfiles()) { 1081 curProfilesSize += artd.getProfileSize(profile); 1082 } 1083 1084 return new ArtManagedFileStats(artifactsSize, refProfilesSize, curProfilesSize); 1085 } catch (RemoteException e) { 1086 Utils.logArtdException(e); 1087 return new ArtManagedFileStats( 1088 0 /* artifactsSize */, 0 /* refProfilesSize */, 0 /* curProfilesSize */); 1089 } 1090 } 1091 1092 /** 1093 * Overrides the compiler filter of a package. The callback is called whenever a package is 1094 * going to be dexopted. This method is thread-safe. 1095 */ 1096 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) setAdjustCompilerFilterCallback(@onNull @allbackExecutor Executor executor, @NonNull AdjustCompilerFilterCallback callback)1097 public void setAdjustCompilerFilterCallback(@NonNull @CallbackExecutor Executor executor, 1098 @NonNull AdjustCompilerFilterCallback callback) { 1099 mInjector.getConfig().setAdjustCompilerFilterCallback(executor, callback); 1100 } 1101 1102 /** 1103 * Clears the callback set by {@link 1104 * #setAdjustCompilerFilterCallback(Executor, AdjustCompilerFilterCallback)}. This 1105 * method is thread-safe. 1106 */ 1107 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) clearAdjustCompilerFilterCallback()1108 public void clearAdjustCompilerFilterCallback() { 1109 mInjector.getConfig().clearAdjustCompilerFilterCallback(); 1110 } 1111 1112 /** 1113 * Cleans up obsolete profiles and artifacts. 1114 * 1115 * This is done in a mark-and-sweep approach. 1116 * 1117 * @return The amount of the disk space freed by the cleanup, in bytes. 1118 * @hide 1119 */ 1120 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) cleanup(@onNull PackageManagerLocal.FilteredSnapshot snapshot)1121 public long cleanup(@NonNull PackageManagerLocal.FilteredSnapshot snapshot) { 1122 mCleanupLock.writeLock().lock(); 1123 try (var pin = mInjector.createArtdPin()) { 1124 mInjector.getDexUseManager().cleanup(); 1125 1126 // For every primary dex container file or secondary dex container file of every app, if 1127 // it has code, we keep the following types of files: 1128 // - The reference profile and the current profiles, regardless of the hibernation state 1129 // of the app. 1130 // - The dexopt artifacts, if they are up-to-date and the app is not hibernating. 1131 // - Only the VDEX part of the dexopt artifacts, if the dexopt artifacts are outdated 1132 // but the VDEX part is still usable and the app is not hibernating. 1133 // - The SDM and SDC files, if they are up-to-date and the app is not hibernating. 1134 // - The runtime artifacts, if dexopt artifacts are fully or partially usable and the 1135 // usable parts don't contain AOT-compiled code. (This logic must be aligned with the 1136 // one that determines when runtime images can be loaded in 1137 // `OatFileManager::OpenDexFilesFromOat` in `art/runtime/oat_file_manager.cc`.) 1138 List<ProfilePath> profilesToKeep = new ArrayList<>(); 1139 List<ArtifactsPath> artifactsToKeep = new ArrayList<>(); 1140 List<VdexPath> vdexFilesToKeep = new ArrayList<>(); 1141 List<SecureDexMetadataWithCompanionPaths> sdmSdcFilesToKeep = new ArrayList<>(); 1142 List<RuntimeArtifactsPath> runtimeArtifactsToKeep = new ArrayList<>(); 1143 1144 for (PackageState pkgState : snapshot.getPackageStates().values()) { 1145 if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) { 1146 continue; 1147 } 1148 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 1149 ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg, 1150 ArtFileManager.Options.builder() 1151 .setForPrimaryDex(true) 1152 .setForSecondaryDex(true) 1153 .setExcludeForObsoleteDexesAndLoaders(true) 1154 .build()); 1155 profilesToKeep.addAll(profileLists.allProfiles()); 1156 if (!Utils.shouldSkipDexoptDueToHibernation( 1157 pkgState, mInjector.getAppHibernationManager())) { 1158 UsableArtifactLists artifactLists = 1159 mInjector.getArtFileManager().getUsableArtifacts(pkgState, pkg); 1160 artifactsToKeep.addAll(artifactLists.artifacts()); 1161 vdexFilesToKeep.addAll(artifactLists.vdexFiles()); 1162 sdmSdcFilesToKeep.addAll(artifactLists.sdmFiles()); 1163 runtimeArtifactsToKeep.addAll(artifactLists.runtimeArtifacts()); 1164 } 1165 } 1166 return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep, 1167 sdmSdcFilesToKeep, runtimeArtifactsToKeep, 1168 SdkLevel.isAtLeastV() && mInjector.getPreRebootDexoptJob().hasStarted()); 1169 } catch (RemoteException e) { 1170 Utils.logArtdException(e); 1171 return 0; 1172 } finally { 1173 mCleanupLock.writeLock().unlock(); 1174 } 1175 } 1176 1177 /** @param forSecondary true for secondary dex files; false for primary dex files. */ 1178 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) commitPreRebootStagedFiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, boolean forSecondary)1179 private void commitPreRebootStagedFiles( 1180 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, boolean forSecondary) { 1181 try (var pin = mInjector.createArtdPin()) { 1182 // Because we don't know for which packages the Pre-reboot Dexopt job has generated 1183 // staged files, we call artd for all dexoptable packages, which is a superset of the 1184 // packages that we actually expect to have staged files. 1185 for (PackageState pkgState : snapshot.getPackageStates().values()) { 1186 if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) { 1187 continue; 1188 } 1189 1190 AsLog.d("commitPreRebootStagedFiles " + (forSecondary ? "secondary" : "primary") 1191 + " for " + pkgState.getPackageName()); 1192 1193 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 1194 var options = ArtFileManager.Options.builder() 1195 .setForPrimaryDex(!forSecondary) 1196 .setForSecondaryDex(forSecondary) 1197 .setExcludeForObsoleteDexesAndLoaders(true) 1198 .build(); 1199 List<ArtifactsPath> artifacts = 1200 mInjector.getArtFileManager() 1201 .getWritableArtifacts(pkgState, pkg, options) 1202 .artifacts(); 1203 List<WritableProfilePath> profiles = mInjector.getArtFileManager() 1204 .getProfiles(pkgState, pkg, options) 1205 .refProfiles() 1206 .stream() 1207 .map(AidlUtils::toWritableProfilePath) 1208 .toList(); 1209 try { 1210 // The artd method commits all files somewhat transactionally. Here, we are 1211 // committing files transactionally at the package level just for simplicity. In 1212 // fact, we only need transaction on the split level: the artifacts and the 1213 // profile of the same split must be committed transactionally. Consider the 1214 // case where the staged artifacts and profile have less methods than the active 1215 // ones generated by background dexopt, committing the artifacts while failing 1216 // to commit the profile can potentially cause a permanent performance 1217 // regression. 1218 if (mInjector.getArtd().commitPreRebootStagedFiles(artifacts, profiles)) { 1219 mStatsAfterRebootSession.recordPackageWithArtifacts( 1220 pkgState.getPackageName()); 1221 } 1222 } catch (ServiceSpecificException e) { 1223 AsLog.e("Failed to commit Pre-reboot staged files for package '" 1224 + pkgState.getPackageName() + "'", 1225 e); 1226 } 1227 } 1228 } catch (RemoteException e) { 1229 Utils.logArtdException(e); 1230 } 1231 } 1232 1233 /** 1234 * Forces all running processes of the given package to flush profiles to the disk. 1235 * 1236 * @return true on success; false on timeout or artd crash. 1237 * 1238 * @hide 1239 */ 1240 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) flushProfiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)1241 public boolean flushProfiles( 1242 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 1243 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 1244 List<RunningAppProcessInfo> infoList = 1245 Utils.getRunningProcessInfoForPackage(mInjector.getActivityManager(), pkgState); 1246 1247 try (var pin = mInjector.createArtdPin()) { 1248 boolean success = true; 1249 for (RunningAppProcessInfo info : infoList) { 1250 PrimaryCurProfilePath profilePath = AidlUtils.buildPrimaryCurProfilePath( 1251 UserHandle.getUserHandleForUid(info.uid).getIdentifier(), packageName, 1252 PrimaryDexUtils.getProfileName(null /* splitName */)); 1253 IArtdNotification notification = 1254 mInjector.getArtd().initProfileSaveNotification(profilePath, info.pid); 1255 1256 // Check if the process is still there. 1257 if (!Utils.getRunningProcessInfoForPackage(mInjector.getActivityManager(), pkgState) 1258 .stream() 1259 .anyMatch(running_info -> running_info.pid == info.pid)) { 1260 continue; 1261 } 1262 1263 // Send signal and wait one by one, to avoid the race among processes on the same 1264 // profile file. 1265 try { 1266 mInjector.kill(info.pid, OsConstants.SIGUSR1); 1267 success &= notification.wait(1000 /* timeoutMs */); 1268 } catch (ErrnoException | ServiceSpecificException e) { 1269 if (e instanceof ErrnoException ee) { 1270 if (ee.errno == OsConstants.ESRCH) { 1271 continue; 1272 } 1273 } 1274 AsLog.w("Failed to flush profile on pid " + info.pid, e); 1275 } 1276 } 1277 return success; 1278 } catch (RemoteException e) { 1279 Utils.logArtdException(e); 1280 return false; 1281 } 1282 } 1283 1284 /** 1285 * Should be used by {@link BackgroundDexoptJobService} ONLY. 1286 * 1287 * @hide 1288 */ 1289 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1290 @NonNull getBackgroundDexoptJob()1291 BackgroundDexoptJob getBackgroundDexoptJob() { 1292 return mInjector.getBackgroundDexoptJob(); 1293 } 1294 1295 /** 1296 * Should be used by {@link BackgroundDexoptJobService} ONLY. 1297 * 1298 * @hide 1299 */ 1300 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 1301 @NonNull getPreRebootDexoptJob()1302 PreRebootDexoptJob getPreRebootDexoptJob() { 1303 return mInjector.getPreRebootDexoptJob(); 1304 } 1305 1306 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1307 @Nullable maybeDowngradePackages( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal, @NonNull Executor executor, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)1308 private DexoptResult maybeDowngradePackages( 1309 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1310 @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal, 1311 @NonNull Executor executor, 1312 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 1313 @Nullable Consumer<OperationProgress> progressCallback) { 1314 if (shouldDowngrade()) { 1315 List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE) 1316 .stream() 1317 .filter(pkg -> !excludedPackages.contains(pkg)) 1318 .toList(); 1319 if (!packages.isEmpty()) { 1320 AsLog.i("Storage is low. Downgrading " + packages.size() + " inactive packages"); 1321 DexoptParams params = 1322 new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build(); 1323 return mInjector.getDexoptHelper().dexopt(snapshot, packages, params, 1324 cancellationSignal, executor, progressCallbackExecutor, progressCallback); 1325 } else { 1326 AsLog.i("Storage is low, but downgrading is disabled or there's nothing to " 1327 + "downgrade"); 1328 } 1329 } 1330 return null; 1331 } 1332 1333 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) shouldDowngrade()1334 private boolean shouldDowngrade() { 1335 try { 1336 return mInjector.getStorageManager().getAllocatableBytes(StorageManager.UUID_DEFAULT) 1337 < DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES; 1338 } catch (IOException e) { 1339 AsLog.e("Failed to check storage. Assuming storage not low", e); 1340 return false; 1341 } 1342 } 1343 1344 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1345 @Nullable maybeDexoptPackagesSupplementaryPass( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull DexoptResult mainResult, @NonNull DexoptParams mainParams, @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)1346 private DexoptResult maybeDexoptPackagesSupplementaryPass( 1347 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1348 @NonNull DexoptResult mainResult, @NonNull DexoptParams mainParams, 1349 @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor, 1350 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 1351 @Nullable Consumer<OperationProgress> progressCallback) { 1352 if ((mainParams.getFlags() & ArtFlags.FLAG_FORCE_MERGE_PROFILE) != 0) { 1353 return null; 1354 } 1355 1356 // Only pick packages that used a profile-guided filter and were skipped in the main pass. 1357 // This is a very coarse filter to reduce unnecessary iterations on a best-effort basis. 1358 // Packages included in the list may still be skipped by dexopter if the profiles don't have 1359 // any change. 1360 List<String> packageNames = 1361 mainResult.getPackageDexoptResults() 1362 .stream() 1363 .filter(packageResult 1364 -> packageResult.getDexContainerFileDexoptResults() 1365 .stream() 1366 .anyMatch(fileResult 1367 -> DexFile.isProfileGuidedCompilerFilter( 1368 fileResult.getActualCompilerFilter()) 1369 && fileResult.getStatus() 1370 == DexoptResult.DEXOPT_SKIPPED)) 1371 .map(packageResult -> packageResult.getPackageName()) 1372 .toList(); 1373 1374 DexoptParams dexoptParams = mainParams.toBuilder() 1375 .setFlags(ArtFlags.FLAG_FORCE_MERGE_PROFILE, 1376 ArtFlags.FLAG_FORCE_MERGE_PROFILE) 1377 .build(); 1378 1379 AsLog.i("Dexopting " + packageNames.size() 1380 + " packages with reason=" + dexoptParams.getReason() + " (supplementary pass)"); 1381 return mInjector.getDexoptHelper().dexopt(snapshot, packageNames, dexoptParams, 1382 cancellationSignal, dexoptExecutor, progressCallbackExecutor, progressCallback); 1383 } 1384 1385 /** Returns the list of packages to process for the given reason. */ 1386 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1387 @NonNull getDefaultPackages(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String reason)1388 private List<String> getDefaultPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1389 @NonNull /* @BatchDexoptReason|REASON_INACTIVE */ String reason) { 1390 var appHibernationManager = mInjector.getAppHibernationManager(); 1391 1392 // Filter out hibernating packages even if the reason is REASON_INACTIVE. This is because 1393 // artifacts for hibernating packages are already deleted. 1394 Stream<PackageState> packages = snapshot.getPackageStates().values().stream().filter( 1395 pkgState -> Utils.canDexoptPackage(pkgState, appHibernationManager)); 1396 1397 switch (reason) { 1398 case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE: 1399 packages = packages.filter(pkgState 1400 -> mInjector.isSystemUiPackage(pkgState.getPackageName()) 1401 || mInjector.isLauncherPackage(pkgState.getPackageName())); 1402 break; 1403 case ReasonMapping.REASON_INACTIVE: 1404 packages = filterAndSortByLastActiveTime( 1405 packages, false /* keepRecent */, false /* descending */); 1406 break; 1407 case ReasonMapping.REASON_FIRST_BOOT: 1408 // Don't filter the default package list and no need to sort 1409 // as in some cases the system time can advance during bootup 1410 // after package installation and cause filtering to exclude 1411 // all packages when pm.dexopt.downgrade_after_inactive_days 1412 // is set. See aosp/3237478 for more details. 1413 break; 1414 default: 1415 // Actually, the sorting is only needed for background dexopt, but we do it for all 1416 // cases for simplicity. 1417 packages = filterAndSortByLastActiveTime( 1418 packages, true /* keepRecent */, true /* descending */); 1419 } 1420 1421 return packages.map(PackageState::getPackageName).toList(); 1422 } 1423 1424 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1425 @NonNull filterAndSortByLastActiveTime( @onNull Stream<PackageState> packages, boolean keepRecent, boolean descending)1426 private Stream<PackageState> filterAndSortByLastActiveTime( 1427 @NonNull Stream<PackageState> packages, boolean keepRecent, boolean descending) { 1428 // "pm.dexopt.downgrade_after_inactive_days" is repurposed to also determine whether to 1429 // dexopt a package. 1430 long inactiveMs = TimeUnit.DAYS.toMillis(SystemProperties.getInt( 1431 "pm.dexopt.downgrade_after_inactive_days", Integer.MAX_VALUE /* def */)); 1432 long currentTimeMs = mInjector.getCurrentTimeMillis(); 1433 long thresholdTimeMs = currentTimeMs - inactiveMs; 1434 return packages 1435 .map(pkgState 1436 -> Pair.create(pkgState, 1437 Utils.getPackageLastActiveTime(pkgState, 1438 mInjector.getDexUseManager(), mInjector.getUserManager()))) 1439 .filter(keepRecent ? (pair -> pair.second > thresholdTimeMs) 1440 : (pair -> pair.second <= thresholdTimeMs)) 1441 .sorted(descending ? Comparator.comparingLong(pair -> - pair.second) 1442 : Comparator.comparingLong(pair -> pair.second)) 1443 .map(pair -> pair.first); 1444 } 1445 1446 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1447 @NonNull mergeProfilesAndGetFd(@onNull List<ProfilePath> profiles, @NonNull OutputProfile output, @NonNull List<String> dexPaths, @NonNull MergeProfileOptions options)1448 private ParcelFileDescriptor mergeProfilesAndGetFd(@NonNull List<ProfilePath> profiles, 1449 @NonNull OutputProfile output, @NonNull List<String> dexPaths, 1450 @NonNull MergeProfileOptions options) throws SnapshotProfileException { 1451 try { 1452 boolean hasContent = false; 1453 try { 1454 hasContent = mInjector.getArtd().mergeProfiles( 1455 profiles, null /* referenceProfile */, output, dexPaths, options); 1456 } catch (ServiceSpecificException e) { 1457 throw new SnapshotProfileException(e); 1458 } 1459 1460 String path; 1461 Path emptyFile = null; 1462 if (hasContent) { 1463 path = output.profilePath.tmpPath; 1464 } else { 1465 // We cannot use /dev/null because `ParcelFileDescriptor` have an API `getStatSize`, 1466 // which expects the file to be a regular file or a link, and apps may call that 1467 // API. 1468 emptyFile = 1469 Files.createTempFile(Paths.get(mInjector.getTempDir()), "empty", ".tmp"); 1470 path = emptyFile.toString(); 1471 } 1472 ParcelFileDescriptor fd; 1473 try { 1474 fd = ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY); 1475 } catch (FileNotFoundException e) { 1476 throw new IllegalStateException( 1477 String.format("Failed to open profile snapshot '%s'", path), e); 1478 } 1479 1480 // The deletion is done on the open file so that only the FD keeps a reference to the 1481 // file. 1482 if (hasContent) { 1483 mInjector.getArtd().deleteProfile(ProfilePath.tmpProfilePath(output.profilePath)); 1484 } else { 1485 Files.delete(emptyFile); 1486 } 1487 1488 return fd; 1489 } catch (IOException | RemoteException e) { 1490 throw new SnapshotProfileException(e); 1491 } 1492 } 1493 1494 /** @hide */ 1495 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1496 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1497 public interface BatchDexoptStartCallback { 1498 /** 1499 * Mutates {@code builder} to override the default params for {@link #dexoptPackages}. It 1500 * must ignore unknown reasons because more reasons may be added in the future. 1501 * 1502 * This is called before the start of any automatic package dexopt (i.e., not 1503 * including package dexopt initiated by the {@link #dexoptPackage} API call). 1504 * 1505 * If {@code builder.setPackages} is not called, {@code defaultPackages} will be used as the 1506 * list of packages to dexopt. 1507 * 1508 * If {@code builder.setDexoptParams} is not called, the default params built from {@code 1509 * new DexoptParams.Builder(reason)} will to used as the params for dexopting each 1510 * package. 1511 * 1512 * Additionally, {@code cancellationSignal.cancel()} can be called to cancel this operation. 1513 * If this operation is initiated by the job scheduler and the {@code reason} is {@link 1514 * ReasonMapping#REASON_BG_DEXOPT}, the job will be retried with the default retry policy 1515 * (30 seconds, exponential, capped at 5hrs). 1516 * 1517 * Changing the reason is not allowed. Doing so will result in {@link IllegalStateException} 1518 * when {@link #dexoptPackages} is called. 1519 */ onBatchDexoptStart(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages, @NonNull BatchDexoptParams.Builder builder, @NonNull CancellationSignal cancellationSignal)1520 void onBatchDexoptStart(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1521 @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages, 1522 @NonNull BatchDexoptParams.Builder builder, 1523 @NonNull CancellationSignal cancellationSignal); 1524 } 1525 1526 /** @hide */ 1527 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1528 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1529 public interface ScheduleBackgroundDexoptJobCallback { 1530 /** 1531 * Mutates {@code builder} to override the configuration of the background dexopt job. 1532 * 1533 * The default configuration described in {@link 1534 * ArtManagerLocal#scheduleBackgroundDexoptJob()} is passed to the callback as the {@code 1535 * builder} argument. 1536 * 1537 * Setting {@link JobInfo.Builder#setBackoffCriteria} is not allowed. Doing so will result 1538 * in {@link IllegalArgumentException} when {@link #scheduleBackgroundDexoptJob()} is 1539 * called. The job is retried with the default retry policy (30 seconds, exponential, capped 1540 * at 5hrs). Unfortunately, due to the limitation of the job scheduler API, this retry 1541 * policy cannot be changed. 1542 * 1543 * Setting {@link JobInfo.Builder#setRequiresStorageNotLow(boolean)} is not allowed. Doing 1544 * so will result in {@link IllegalStateException} when {@link 1545 * #scheduleBackgroundDexoptJob()} is called. ART Service has its own storage check, which 1546 * skips package dexopt when the storage is low. The storage check is enabled by 1547 * default for background dexopt jobs. {@link 1548 * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} can be used to disable 1549 * the storage check by clearing the {@link ArtFlags#FLAG_SKIP_IF_STORAGE_LOW} flag. 1550 */ onOverrideJobInfo(@onNull JobInfo.Builder builder)1551 void onOverrideJobInfo(@NonNull JobInfo.Builder builder); 1552 } 1553 1554 /** @hide */ 1555 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1556 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1557 public interface DexoptDoneCallback { onDexoptDone(@onNull DexoptResult result)1558 void onDexoptDone(@NonNull DexoptResult result); 1559 } 1560 1561 /** @hide */ 1562 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1563 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1564 public interface AdjustCompilerFilterCallback { 1565 /** 1566 * Returns the adjusted compiler filter for the given package. If a package doesn't need 1567 * adjustment, this callback must return {@code originalCompilerFilter}. The callback must 1568 * be able to handle unknown {@code originalCompilerFilter} and unknown {@code reason} 1569 * because more compiler filters and reasons may be added in the future. 1570 * 1571 * The returned compiler filter overrides any compiler filter set by {@link 1572 * DexoptParams.Builder#setCompilerFilter}, no matter the dexopt is initiated by a 1573 * {@link #dexoptPackage} API call or any automatic batch dexopt (e.g., dexopt on boot and 1574 * background dexopt). 1575 * 1576 * This callback is useful for: 1577 * - Consistently overriding the compiler filter regardless of the dexopt initiator, for 1578 * some performance-sensitive packages. 1579 * - Providing a compiler filter for specific packages during batch dexopt. 1580 * 1581 * The actual compiler filter to be used for dexopt will be determined in the following 1582 * order: 1583 * 1584 * 1. The default compiler filter for the given reason. 1585 * 2. The compiler filter set explicitly by {@link DexoptParams.Builder#setCompilerFilter}. 1586 * 3. ART Service's internal adjustments to upgrade the compiler filter, based on whether 1587 * the package is System UI, etc. (Not applicable if the dexopt is initiated by a shell 1588 * command with an explicit "-m" flag.) 1589 * 4. The adjustments made by this callback. (Not applicable if the dexopt is initiated by a 1590 * shell command with an explicit "-m" flag.) 1591 * 5. ART Service's internal adjustments to downgrade the compiler filter, based on whether 1592 * the profile is available, etc. 1593 * 1594 * @param packageName the name of the package to be dexopted 1595 * @param originalCompilerFilter the compiler filter before adjustment. This is the result 1596 * of step 3 described above. It would be the input to step 5 described above if 1597 * it wasn't for this callback. 1598 * @param reason the compilation reason of this dexopt operation. It is a string defined in 1599 * {@link ReasonMapping} or a custom string passed to {@link 1600 * DexoptParams.Builder#Builder(String)} 1601 * 1602 * @return the compiler filter after adjustment. This will be the input to step 5 described 1603 * above 1604 */ 1605 @NonNull onAdjustCompilerFilter(@onNull String packageName, @NonNull String originalCompilerFilter, @NonNull String reason)1606 String onAdjustCompilerFilter(@NonNull String packageName, 1607 @NonNull String originalCompilerFilter, @NonNull String reason); 1608 } 1609 1610 /** 1611 * Represents an error that happens when snapshotting profiles. 1612 * 1613 * @hide 1614 */ 1615 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1616 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1617 public static class SnapshotProfileException extends Exception { 1618 /** @hide */ SnapshotProfileException(@onNull Throwable cause)1619 public SnapshotProfileException(@NonNull Throwable cause) { 1620 super(cause); 1621 } 1622 1623 /** @hide */ SnapshotProfileException(@onNull String message)1624 public SnapshotProfileException(@NonNull String message) { 1625 super(message); 1626 } 1627 } 1628 1629 /** 1630 * Injector pattern for testing purpose. 1631 * 1632 * @hide 1633 */ 1634 @VisibleForTesting 1635 public static class Injector { 1636 @Nullable private final ArtManagerLocal mArtManagerLocal; 1637 @Nullable private final Context mContext; 1638 @Nullable private final PackageManagerLocal mPackageManagerLocal; 1639 @Nullable private final Config mConfig; 1640 @Nullable private final ThreadPoolExecutor mReporterExecutor; 1641 @Nullable private BackgroundDexoptJob mBgDexoptJob = null; 1642 @Nullable private PreRebootDexoptJob mPrDexoptJob = null; 1643 1644 /** For compatibility with S and T. New code should not use this. */ 1645 @Deprecated Injector()1646 Injector() { 1647 mArtManagerLocal = null; 1648 mContext = null; 1649 mPackageManagerLocal = null; 1650 mConfig = null; 1651 mReporterExecutor = null; 1652 } 1653 1654 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) Injector(@onNull ArtManagerLocal artManagerLocal, @Nullable Context context)1655 Injector(@NonNull ArtManagerLocal artManagerLocal, @Nullable Context context) { 1656 // We only need them on Android U and above, where a context is passed. 1657 mArtManagerLocal = artManagerLocal; 1658 mContext = context; 1659 mPackageManagerLocal = Objects.requireNonNull( 1660 LocalManagerRegistry.getManager(PackageManagerLocal.class)); 1661 mConfig = new Config(); 1662 mReporterExecutor = 1663 new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, 1664 60 /* keepTimeAlive */, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); 1665 mReporterExecutor.allowsCoreThreadTimeOut(); 1666 1667 // Call the getters for the dependencies that aren't optional, to ensure correct 1668 // initialization order. 1669 getDexoptHelper(); 1670 getUserManager(); 1671 getDexUseManager(); 1672 getStorageManager(); 1673 getActivityManager(); 1674 GlobalInjector.getInstance().checkArtModuleServiceManager(); 1675 1676 // `PreRebootDexoptJob` does not depend on external dependencies, so unlike the calls 1677 // above, this call is not for checking the dependencies. Rather, we make this call here 1678 // to trigger the construction of `PreRebootDexoptJob`, which may clean up leftover 1679 // chroot if there is any. 1680 if (SdkLevel.isAtLeastV()) { 1681 getPreRebootDexoptJob(); 1682 } 1683 } 1684 1685 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1686 @NonNull getContext()1687 public Context getContext() { 1688 return Objects.requireNonNull(mContext); 1689 } 1690 1691 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1692 @NonNull getPackageManagerLocal()1693 public PackageManagerLocal getPackageManagerLocal() { 1694 return Objects.requireNonNull(mPackageManagerLocal); 1695 } 1696 1697 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1698 @NonNull getArtd()1699 public IArtd getArtd() { 1700 return ArtdRefCache.getInstance().getArtd(); 1701 } 1702 1703 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1704 @NonNull createArtdPin()1705 public ArtdRefCache.Pin createArtdPin() { 1706 return ArtdRefCache.getInstance().new Pin(); 1707 } 1708 1709 /** Returns a new {@link DexoptHelper} instance. */ 1710 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1711 @NonNull getDexoptHelper()1712 public DexoptHelper getDexoptHelper() { 1713 return new DexoptHelper(getContext(), getConfig(), getReporterExecutor()); 1714 } 1715 1716 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1717 @NonNull getConfig()1718 public Config getConfig() { 1719 return mConfig; 1720 } 1721 1722 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1723 @NonNull getReporterExecutor()1724 public Executor getReporterExecutor() { 1725 return mReporterExecutor; 1726 } 1727 1728 /** Returns the registered {@link AppHibernationManager} instance. */ 1729 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1730 @NonNull getAppHibernationManager()1731 public AppHibernationManager getAppHibernationManager() { 1732 return Objects.requireNonNull(mContext.getSystemService(AppHibernationManager.class)); 1733 } 1734 1735 /** 1736 * Returns the {@link BackgroundDexoptJob} instance. 1737 * 1738 * @throws RuntimeException if called during boot before the job scheduler service has 1739 * started. 1740 */ 1741 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1742 @NonNull getBackgroundDexoptJob()1743 public synchronized BackgroundDexoptJob getBackgroundDexoptJob() { 1744 if (mBgDexoptJob == null) { 1745 mBgDexoptJob = new BackgroundDexoptJob(mContext, mArtManagerLocal, mConfig); 1746 } 1747 return mBgDexoptJob; 1748 } 1749 1750 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 1751 @NonNull getPreRebootDexoptJob()1752 public synchronized PreRebootDexoptJob getPreRebootDexoptJob() { 1753 if (mPrDexoptJob == null) { 1754 mPrDexoptJob = new PreRebootDexoptJob(mContext, mArtManagerLocal); 1755 } 1756 return mPrDexoptJob; 1757 } 1758 1759 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1760 @NonNull getUserManager()1761 public UserManager getUserManager() { 1762 return Objects.requireNonNull(mContext.getSystemService(UserManager.class)); 1763 } 1764 1765 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1766 @NonNull getDexUseManager()1767 public DexUseManagerLocal getDexUseManager() { 1768 return GlobalInjector.getInstance().getDexUseManager(); 1769 } 1770 1771 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) isSystemUiPackage(@onNull String packageName)1772 public boolean isSystemUiPackage(@NonNull String packageName) { 1773 return Utils.isSystemUiPackage(mContext, packageName); 1774 } 1775 1776 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) isLauncherPackage(@onNull String packageName)1777 public boolean isLauncherPackage(@NonNull String packageName) { 1778 return Utils.isLauncherPackage(mContext, packageName); 1779 } 1780 1781 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) getCurrentTimeMillis()1782 public long getCurrentTimeMillis() { 1783 return System.currentTimeMillis(); 1784 } 1785 1786 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1787 @NonNull getStorageManager()1788 public StorageManager getStorageManager() { 1789 return Objects.requireNonNull(mContext.getSystemService(StorageManager.class)); 1790 } 1791 1792 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1793 @NonNull getTempDir()1794 public String getTempDir() { 1795 // This is a path that system_server is known to have full access to. 1796 return "/data/system"; 1797 } 1798 1799 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1800 @NonNull getArtFileManager()1801 public ArtFileManager getArtFileManager() { 1802 return new ArtFileManager(getContext()); 1803 } 1804 1805 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1806 @NonNull getDexMetadataHelper()1807 public DexMetadataHelper getDexMetadataHelper() { 1808 return new DexMetadataHelper(); 1809 } 1810 1811 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 1812 @NonNull getPreRebootStatsReporter()1813 public PreRebootStatsReporter getPreRebootStatsReporter() { 1814 return new PreRebootStatsReporter(); 1815 } 1816 1817 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1818 @NonNull getActivityManager()1819 public ActivityManager getActivityManager() { 1820 return Objects.requireNonNull(mContext.getSystemService(ActivityManager.class)); 1821 } 1822 1823 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) kill(int pid, int signal)1824 public void kill(int pid, int signal) throws ErrnoException { 1825 Os.kill(pid, signal); 1826 } 1827 } 1828 } 1829