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