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.DexUseManagerLocal.DetailedSecondaryDexInfo; 20 import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo; 21 import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo; 22 import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo; 23 import static com.android.server.art.ReasonMapping.BatchDexoptReason; 24 import static com.android.server.art.ReasonMapping.BootReason; 25 import static com.android.server.art.Utils.Abi; 26 import static com.android.server.art.model.ArtFlags.GetStatusFlags; 27 import static com.android.server.art.model.ArtFlags.ScheduleStatus; 28 import static com.android.server.art.model.Config.Callback; 29 import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; 30 31 import android.annotation.CallbackExecutor; 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.annotation.SuppressLint; 35 import android.annotation.SystemApi; 36 import android.annotation.SystemService; 37 import android.app.job.JobInfo; 38 import android.apphibernation.AppHibernationManager; 39 import android.content.Context; 40 import android.os.Binder; 41 import android.os.Build; 42 import android.os.CancellationSignal; 43 import android.os.ParcelFileDescriptor; 44 import android.os.Process; 45 import android.os.RemoteException; 46 import android.os.ServiceSpecificException; 47 import android.os.SystemProperties; 48 import android.os.UserHandle; 49 import android.os.UserManager; 50 import android.os.storage.StorageManager; 51 import android.text.TextUtils; 52 import android.util.Log; 53 import android.util.Pair; 54 55 import androidx.annotation.RequiresApi; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.server.LocalManagerRegistry; 59 import com.android.server.art.model.ArtFlags; 60 import com.android.server.art.model.BatchDexoptParams; 61 import com.android.server.art.model.Config; 62 import com.android.server.art.model.DeleteResult; 63 import com.android.server.art.model.DetailedDexInfo; 64 import com.android.server.art.model.DexoptParams; 65 import com.android.server.art.model.DexoptResult; 66 import com.android.server.art.model.DexoptStatus; 67 import com.android.server.art.model.OperationProgress; 68 import com.android.server.pm.PackageManagerLocal; 69 import com.android.server.pm.pkg.AndroidPackage; 70 import com.android.server.pm.pkg.AndroidPackageSplit; 71 import com.android.server.pm.pkg.PackageState; 72 73 import dalvik.system.DexFile; 74 75 import java.io.File; 76 import java.io.FileNotFoundException; 77 import java.io.IOException; 78 import java.io.PrintWriter; 79 import java.nio.file.Files; 80 import java.nio.file.Path; 81 import java.nio.file.Paths; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.Collections; 85 import java.util.Comparator; 86 import java.util.HashSet; 87 import java.util.List; 88 import java.util.Objects; 89 import java.util.Set; 90 import java.util.concurrent.CompletableFuture; 91 import java.util.concurrent.Executor; 92 import java.util.concurrent.ExecutorService; 93 import java.util.concurrent.Executors; 94 import java.util.concurrent.TimeUnit; 95 import java.util.function.Consumer; 96 import java.util.stream.Collectors; 97 import java.util.stream.Stream; 98 99 /** 100 * This class provides a system API for functionality provided by the ART module. 101 * 102 * Note: Although this class is the entry point of ART services, this class is not a {@link 103 * SystemService}, and it does not publish a binder. Instead, it is a module loaded by the 104 * system_server process, registered in {@link LocalManagerRegistry}. {@link LocalManagerRegistry} 105 * specifies that in-process module interfaces should be named with the suffix {@code ManagerLocal} 106 * for consistency. 107 * 108 * @hide 109 */ 110 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 111 public final class ArtManagerLocal { 112 /** @hide */ 113 public static final String TAG = "ArtService"; 114 115 private static final String[] CLASSPATHS_FOR_BOOT_IMAGE_PROFILE = { 116 "BOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}; 117 118 /** @hide */ 119 @VisibleForTesting public static final long DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES = 500_000_000; 120 121 @NonNull private final Injector mInjector; 122 123 @Deprecated ArtManagerLocal()124 public ArtManagerLocal() { 125 mInjector = new Injector(this, null /* context */); 126 } 127 128 /** 129 * Creates an instance. 130 * 131 * Only {@code SystemServer} should create an instance and register it in {@link 132 * LocalManagerRegistry}. Other API users should obtain the instance from {@link 133 * LocalManagerRegistry}. 134 * 135 * @param context the system server context 136 * @throws NullPointerException if required dependencies are missing 137 */ 138 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) ArtManagerLocal(@onNull Context context)139 public ArtManagerLocal(@NonNull Context context) { 140 mInjector = new Injector(this, context); 141 } 142 143 /** @hide */ 144 @VisibleForTesting ArtManagerLocal(@onNull Injector injector)145 public ArtManagerLocal(@NonNull Injector injector) { 146 mInjector = injector; 147 } 148 149 /** 150 * Handles ART Service commands, which is a subset of `cmd package` commands. 151 * 152 * Note: This method is not an override of {@link Binder#handleShellCommand} because ART 153 * services does not publish a binder. Instead, it handles the commands forwarded by the 154 * `package` service. The semantics of the parameters are the same as {@link 155 * Binder#handleShellCommand}. 156 * 157 * @return zero on success, non-zero on internal error (e.g., I/O error) 158 * @throws SecurityException if the caller is not root 159 * @throws IllegalArgumentException if the arguments are illegal 160 * @see ArtShellCommand#printHelp(PrintWriter) 161 */ 162 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) handleShellCommand(@onNull Binder target, @NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)163 public int handleShellCommand(@NonNull Binder target, @NonNull ParcelFileDescriptor in, 164 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, 165 @NonNull String[] args) { 166 return new ArtShellCommand(this, mInjector.getPackageManagerLocal()) 167 .exec(target, in.getFileDescriptor(), out.getFileDescriptor(), 168 err.getFileDescriptor(), args); 169 } 170 171 /** Prints ART Service shell command help. */ 172 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) printShellCommandHelp(@onNull PrintWriter pw)173 public void printShellCommandHelp(@NonNull PrintWriter pw) { 174 ArtShellCommand.printHelp(pw); 175 } 176 177 /** 178 * Deletes dexopt artifacts of a package, including the artifacts for primary dex files and the 179 * ones for secondary dex files. This includes VDEX, ODEX, and ART files. 180 * 181 * @throws IllegalArgumentException if the package is not found or the flags are illegal 182 * @throws IllegalStateException if the operation encounters an error that should never happen 183 * (e.g., an internal logic error). 184 */ 185 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 186 @NonNull deleteDexoptArtifacts( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)187 public DeleteResult deleteDexoptArtifacts( 188 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 189 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 190 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 191 192 try { 193 long freedBytes = 0; 194 195 boolean isInDalvikCache = Utils.isInDalvikCache(pkgState, mInjector.getArtd()); 196 for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { 197 if (!dexInfo.hasCode()) { 198 continue; 199 } 200 for (Abi abi : Utils.getAllAbis(pkgState)) { 201 freedBytes += mInjector.getArtd().deleteArtifacts(AidlUtils.buildArtifactsPath( 202 dexInfo.dexPath(), abi.isa(), isInDalvikCache)); 203 } 204 } 205 206 for (SecondaryDexInfo dexInfo : 207 mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) { 208 for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { 209 freedBytes += mInjector.getArtd().deleteArtifacts(AidlUtils.buildArtifactsPath( 210 dexInfo.dexPath(), abi.isa(), false /* isInDalvikCache */)); 211 } 212 } 213 214 return DeleteResult.create(freedBytes); 215 } catch (RemoteException e) { 216 Utils.logArtdException(e); 217 return DeleteResult.create(0 /* freedBytes */); 218 } 219 } 220 221 /** 222 * Returns the dexopt status of a package. 223 * 224 * Uses the default flags ({@link ArtFlags#defaultGetStatusFlags()}). 225 * 226 * @throws IllegalArgumentException if the package is not found or the flags are illegal 227 * @throws IllegalStateException if the operation encounters an error that should never happen 228 * (e.g., an internal logic error). 229 */ 230 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 231 @NonNull getDexoptStatus( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)232 public DexoptStatus getDexoptStatus( 233 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 234 return getDexoptStatus(snapshot, packageName, ArtFlags.defaultGetStatusFlags()); 235 } 236 237 /** 238 * Same as above, but allows to specify flags. 239 * 240 * @see #getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String) 241 */ 242 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 243 @NonNull getDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @GetStatusFlags int flags)244 public DexoptStatus getDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 245 @NonNull String packageName, @GetStatusFlags int flags) { 246 if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0 247 && (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) == 0) { 248 throw new IllegalArgumentException("Nothing to check"); 249 } 250 251 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 252 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 253 254 List<Pair<DetailedDexInfo, Abi>> dexAndAbis = new ArrayList<>(); 255 256 if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0) { 257 for (DetailedPrimaryDexInfo dexInfo : 258 PrimaryDexUtils.getDetailedDexInfo(pkgState, pkg)) { 259 if (!dexInfo.hasCode()) { 260 continue; 261 } 262 for (Abi abi : Utils.getAllAbis(pkgState)) { 263 dexAndAbis.add(Pair.create(dexInfo, abi)); 264 } 265 } 266 } 267 268 if ((flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0) { 269 for (SecondaryDexInfo dexInfo : 270 mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) { 271 for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { 272 dexAndAbis.add(Pair.create(dexInfo, abi)); 273 } 274 } 275 } 276 277 try { 278 List<DexContainerFileDexoptStatus> statuses = new ArrayList<>(); 279 280 for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) { 281 DetailedDexInfo dexInfo = pair.first; 282 Abi abi = pair.second; 283 try { 284 GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus( 285 dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); 286 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 287 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), 288 abi.name(), result.compilerFilter, result.compilationReason, 289 result.locationDebugString)); 290 } catch (ServiceSpecificException e) { 291 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 292 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), 293 abi.name(), "error", "error", e.getMessage())); 294 } 295 } 296 297 return DexoptStatus.create(statuses); 298 } catch (RemoteException e) { 299 Utils.logArtdException(e); 300 List<DexContainerFileDexoptStatus> statuses = new ArrayList<>(); 301 for (Pair<DetailedDexInfo, Abi> pair : dexAndAbis) { 302 DetailedDexInfo dexInfo = pair.first; 303 Abi abi = pair.second; 304 statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(), 305 dexInfo instanceof DetailedPrimaryDexInfo, abi.isPrimaryAbi(), abi.name(), 306 "error", "error", e.getMessage())); 307 } 308 return DexoptStatus.create(statuses); 309 } 310 } 311 312 /** 313 * Clear the profiles that are collected locally for the given package, including the profiles 314 * for primary and secondary dex files. More specifically, it clears reference profiles and 315 * current profiles. External profiles (e.g., cloud profiles) will be kept. 316 * 317 * @throws IllegalArgumentException if the package is not found or the flags are illegal 318 * @throws IllegalStateException if the operation encounters an error that should never happen 319 * (e.g., an internal logic error). 320 */ 321 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 322 @NonNull clearAppProfiles( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)323 public void clearAppProfiles( 324 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 325 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 326 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 327 328 try { 329 for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) { 330 if (!dexInfo.hasCode()) { 331 continue; 332 } 333 mInjector.getArtd().deleteProfile( 334 PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo)); 335 for (ProfilePath profile : PrimaryDexUtils.getCurProfiles( 336 mInjector.getUserManager(), pkgState, dexInfo)) { 337 mInjector.getArtd().deleteProfile(profile); 338 } 339 } 340 341 // This only deletes the profiles of known secondary dex files. If there are unknown 342 // secondary dex files, their profiles will be deleted by `cleanup`. 343 for (SecondaryDexInfo dexInfo : 344 mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) { 345 mInjector.getArtd().deleteProfile( 346 AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath())); 347 mInjector.getArtd().deleteProfile( 348 AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); 349 } 350 } catch (RemoteException e) { 351 Utils.logArtdException(e); 352 } 353 } 354 355 /** 356 * Dexopts a package. The time this operation takes ranges from a few milliseconds to several 357 * minutes, depending on the params and the code size of the package. 358 * 359 * When this operation ends (either completed or cancelled), callbacks added by {@link 360 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called. 361 * 362 * @throws IllegalArgumentException if the package is not found or the params are illegal 363 * @throws IllegalStateException if the operation encounters an error that should never happen 364 * (e.g., an internal logic error). 365 */ 366 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 367 @NonNull dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params)368 public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 369 @NonNull String packageName, @NonNull DexoptParams params) { 370 var cancellationSignal = new CancellationSignal(); 371 return dexoptPackage(snapshot, packageName, params, cancellationSignal); 372 } 373 374 /** 375 * Same as above, but supports cancellation. 376 * 377 * @see #dexoptPackage(PackageManagerLocal.FilteredSnapshot, String, DexoptParams) 378 */ 379 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 380 @NonNull dexoptPackage(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal)381 public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 382 @NonNull String packageName, @NonNull DexoptParams params, 383 @NonNull CancellationSignal cancellationSignal) { 384 return mInjector.getDexoptHelper().dexopt( 385 snapshot, List.of(packageName), params, cancellationSignal, Runnable::run); 386 } 387 388 /** 389 * Resets the dexopt state of the package as if the package is newly installed. 390 * 391 * More specifically, it clears reference profiles, current profiles, and any code compiled from 392 * those local profiles. If there is an external profile (e.g., a cloud profile), the code 393 * compiled from that profile will be kept. 394 * 395 * For secondary dex files, it also clears all dexopt artifacts. 396 * 397 * @hide 398 */ 399 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 400 @NonNull resetDexoptStatus(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @NonNull CancellationSignal cancellationSignal)401 public DexoptResult resetDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 402 @NonNull String packageName, @NonNull CancellationSignal cancellationSignal) { 403 // We must delete the artifacts for primary dex files beforehand rather than relying on 404 // `dexoptPackage` to replace them because: 405 // - If dexopt is not needed after the deletion, then we shouldn't run dexopt at all. For 406 // example, when we have a DM file that contains a VDEX file but doesn't contain a cloud 407 // profile, this happens. Note that this is more about correctness rather than 408 // performance. 409 // - We don't want the existing artifacts to affect dexopt. For example, the existing VDEX 410 // file should not be an input VDEX. 411 // 412 // We delete the artifacts for secondary dex files and `dexoptPackage` won't re-generate 413 // them because `dexoptPackage` for `REASON_INSTALL` is for primary dex only. This is 414 // intentional because secondary dex files are supposed to be unknown at install time. 415 deleteDexoptArtifacts(snapshot, packageName); 416 clearAppProfiles(snapshot, packageName); 417 418 // Re-generate artifacts for primary dex files if needed. 419 return dexoptPackage(snapshot, packageName, 420 new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(), cancellationSignal); 421 } 422 423 /** 424 * Runs batch dexopt for the given reason. 425 * 426 * This is called by ART Service automatically during boot / background dexopt. 427 * 428 * The list of packages and options are determined by {@code reason}, and can be overridden by 429 * {@link #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. 430 * 431 * The dexopt is done in a thread pool. The number of packages being dexopted 432 * simultaneously can be configured by system property {@code pm.dexopt.<reason>.concurrency} 433 * (e.g., {@code pm.dexopt.bg-dexopt.concurrency=4}), and the number of threads for each {@code 434 * dex2oat} invocation can be configured by system property {@code dalvik.vm.*dex2oat-threads} 435 * (e.g., {@code dalvik.vm.background-dex2oat-threads=4}). I.e., the maximum number of 436 * concurrent threads is the product of the two system properties. Note that the physical core 437 * usage is always bound by {@code dalvik.vm.*dex2oat-cpu-set} regardless of the number of 438 * threads. 439 * 440 * When this operation ends (either completed or cancelled), callbacks added by {@link 441 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called. 442 * 443 * If the storage is nearly low, and {@code reason} is {@link ReasonMapping#REASON_BG_DEXOPT}, 444 * it may also downgrade some inactive packages to a less optimized compiler filter, specified 445 * by the system property {@code pm.dexopt.inactive} (typically "verify"), to free up some 446 * space. This feature is only enabled when the system property {@code 447 * pm.dexopt.downgrade_after_inactive_days} is set. The space threshold to trigger this feature 448 * is the Storage Manager's low space threshold plus {@link 449 * #DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES}. The concurrency can be configured by system property 450 * {@code pm.dexopt.bg-dexopt.concurrency}. The packages in the list provided by 451 * {@link BatchDexoptStartCallback} for {@link ReasonMapping#REASON_BG_DEXOPT} are never 452 * downgraded. 453 * 454 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 455 * @param reason determines the default list of packages and options 456 * @param cancellationSignal provides the ability to cancel this operation 457 * @param processCallbackExecutor the executor to call {@code progressCallback} 458 * @param progressCallback called repeatedly whenever there is an update on the progress 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 Consumer<OperationProgress> progressCallback)468 public DexoptResult dexoptPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 469 @NonNull @BatchDexoptReason String reason, 470 @NonNull CancellationSignal cancellationSignal, 471 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 472 @Nullable Consumer<OperationProgress> progressCallback) { 473 List<String> defaultPackages = 474 Collections.unmodifiableList(getDefaultPackages(snapshot, reason)); 475 DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build(); 476 var builder = new BatchDexoptParams.Builder(defaultPackages, defaultDexoptParams); 477 Callback<BatchDexoptStartCallback, Void> callback = 478 mInjector.getConfig().getBatchDexoptStartCallback(); 479 if (callback != null) { 480 Utils.executeAndWait(callback.executor(), () -> { 481 callback.get().onBatchDexoptStart( 482 snapshot, reason, defaultPackages, builder, cancellationSignal); 483 }); 484 } 485 BatchDexoptParams params = builder.build(); 486 Utils.check(params.getDexoptParams().getReason().equals(reason)); 487 488 ExecutorService dexoptExecutor = 489 Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason)); 490 try { 491 if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) { 492 maybeDowngradePackages(snapshot, 493 new HashSet<>(params.getPackages()) /* excludedPackages */, 494 cancellationSignal, dexoptExecutor); 495 } 496 Log.i(TAG, "Dexopting packages"); 497 return mInjector.getDexoptHelper().dexopt(snapshot, params.getPackages(), 498 params.getDexoptParams(), cancellationSignal, dexoptExecutor, 499 progressCallbackExecutor, progressCallback); 500 } finally { 501 dexoptExecutor.shutdown(); 502 } 503 } 504 505 /** 506 * Overrides the default params for {@link #dexoptPackages}. This method is thread-safe. 507 * 508 * This method gives users the opportunity to change the behavior of {@link #dexoptPackages}, 509 * which is called by ART Service automatically during boot / background dexopt. 510 * 511 * If this method is not called, the default list of packages and options determined by {@code 512 * reason} will be used. 513 */ 514 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) setBatchDexoptStartCallback(@onNull @allbackExecutor Executor executor, @NonNull BatchDexoptStartCallback callback)515 public void setBatchDexoptStartCallback(@NonNull @CallbackExecutor Executor executor, 516 @NonNull BatchDexoptStartCallback callback) { 517 mInjector.getConfig().setBatchDexoptStartCallback(executor, callback); 518 } 519 520 /** 521 * Clears the callback set by {@link 522 * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. This method is 523 * thread-safe. 524 */ 525 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) clearBatchDexoptStartCallback()526 public void clearBatchDexoptStartCallback() { 527 mInjector.getConfig().clearBatchDexoptStartCallback(); 528 } 529 530 /** 531 * Schedules a background dexopt job. Does nothing if the job is already scheduled. 532 * 533 * Use this method if you want the system to automatically determine the best time to run 534 * dexopt. 535 * 536 * The job will be run by the job scheduler. The job scheduling configuration can be overridden 537 * by {@link 538 * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. By 539 * default, it runs periodically (at most once a day) when all the following constraints are 540 * meet. 541 * 542 * <ul> 543 * <li>The device is idling. (see {@link JobInfo.Builder#setRequiresDeviceIdle(boolean)}) 544 * <li>The device is charging. (see {@link JobInfo.Builder#setRequiresCharging(boolean)}) 545 * <li>The battery level is not low. 546 * (see {@link JobInfo.Builder#setRequiresBatteryNotLow(boolean)}) 547 * </ul> 548 * 549 * When the job is running, it may be cancelled by the job scheduler immediately whenever one of 550 * the constraints above is no longer met or cancelled by the {@link 551 * #cancelBackgroundDexoptJob()} API. The job scheduler retries it in the next <i>maintenance 552 * window</i>. For information about <i>maintenance window</i>, see 553 * https://developer.android.com/training/monitoring-device-state/doze-standby. 554 * 555 * See {@link #dexoptPackages} for how to customize the behavior of the job. 556 * 557 * When the job ends (either completed or cancelled), the result is sent to the callbacks added 558 * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the 559 * reason {@link ReasonMapping#REASON_BG_DEXOPT}. 560 * 561 * @throws RuntimeException if called during boot before the job scheduler service has started. 562 */ 563 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) scheduleBackgroundDexoptJob()564 public @ScheduleStatus int scheduleBackgroundDexoptJob() { 565 return mInjector.getBackgroundDexoptJob().schedule(); 566 } 567 568 /** 569 * Unschedules the background dexopt job scheduled by {@link #scheduleBackgroundDexoptJob()}. 570 * Does nothing if the job is not scheduled. 571 * 572 * Use this method if you no longer want the system to automatically run dexopt. 573 * 574 * If the job is already started by the job scheduler and is running, it will be cancelled 575 * immediately, and the result sent to the callbacks added by {@link 576 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link 577 * DexoptResult#DEXOPT_CANCELLED}. Note that a job started by {@link 578 * #startBackgroundDexoptJob()} will not be cancelled by this method. 579 */ 580 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) unscheduleBackgroundDexoptJob()581 public void unscheduleBackgroundDexoptJob() { 582 mInjector.getBackgroundDexoptJob().unschedule(); 583 } 584 585 /** 586 * Overrides the configuration of the background dexopt job. This method is thread-safe. 587 */ 588 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) setScheduleBackgroundDexoptJobCallback(@onNull @allbackExecutor Executor executor, @NonNull ScheduleBackgroundDexoptJobCallback callback)589 public void setScheduleBackgroundDexoptJobCallback(@NonNull @CallbackExecutor Executor executor, 590 @NonNull ScheduleBackgroundDexoptJobCallback callback) { 591 mInjector.getConfig().setScheduleBackgroundDexoptJobCallback(executor, callback); 592 } 593 594 /** 595 * Clears the callback set by {@link 596 * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. This 597 * method is thread-safe. 598 */ 599 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) clearScheduleBackgroundDexoptJobCallback()600 public void clearScheduleBackgroundDexoptJobCallback() { 601 mInjector.getConfig().clearScheduleBackgroundDexoptJobCallback(); 602 } 603 604 /** 605 * Manually starts a background dexopt job. Does nothing if a job is already started by this 606 * method or by the job scheduler. This method is not blocking. 607 * 608 * Unlike the job started by job scheduler, the job started by this method does not respect 609 * constraints described in {@link #scheduleBackgroundDexoptJob()}, and hence will not be 610 * cancelled when they aren't met. 611 * 612 * See {@link #dexoptPackages} for how to customize the behavior of the job. 613 * 614 * When the job ends (either completed or cancelled), the result is sent to the callbacks added 615 * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the 616 * reason {@link ReasonMapping#REASON_BG_DEXOPT}. 617 */ 618 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) startBackgroundDexoptJob()619 public void startBackgroundDexoptJob() { 620 mInjector.getBackgroundDexoptJob().start(); 621 } 622 623 /** 624 * Same as above, but also returns a {@link CompletableFuture}. 625 * 626 * @hide 627 */ 628 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 629 @NonNull startBackgroundDexoptJobAndReturnFuture()630 public CompletableFuture<BackgroundDexoptJob.Result> startBackgroundDexoptJobAndReturnFuture() { 631 return mInjector.getBackgroundDexoptJob().start(); 632 } 633 634 /** 635 * Returns the running background dexopt job, or null of no background dexopt job is running. 636 * 637 * @hide 638 */ 639 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 640 @Nullable getRunningBackgroundDexoptJob()641 public CompletableFuture<BackgroundDexoptJob.Result> getRunningBackgroundDexoptJob() { 642 return mInjector.getBackgroundDexoptJob().get(); 643 } 644 645 /** 646 * Cancels the running background dexopt job started by the job scheduler or by {@link 647 * #startBackgroundDexoptJob()}. Does nothing if the job is not running. This method is not 648 * blocking. 649 * 650 * The result sent to the callbacks added by {@link 651 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link 652 * DexoptResult#DEXOPT_CANCELLED}. 653 */ 654 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) cancelBackgroundDexoptJob()655 public void cancelBackgroundDexoptJob() { 656 mInjector.getBackgroundDexoptJob().cancel(); 657 } 658 659 /** 660 * Adds a global listener that listens to any result of dexopting package(s), no matter run 661 * manually or automatically. Calling this method multiple times with different callbacks is 662 * allowed. Callbacks are executed in the same order as the one in which they were added. This 663 * method is thread-safe. 664 * 665 * @param onlyIncludeUpdates if true, the results passed to the callback will only contain 666 * packages that have any update, and the callback won't be called with results that 667 * don't have any update. 668 * @throws IllegalStateException if the same callback instance is already added 669 */ 670 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) addDexoptDoneCallback(boolean onlyIncludeUpdates, @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback)671 public void addDexoptDoneCallback(boolean onlyIncludeUpdates, 672 @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback) { 673 mInjector.getConfig().addDexoptDoneCallback(onlyIncludeUpdates, executor, callback); 674 } 675 676 /** 677 * Removes the listener added by {@link 678 * #addDexoptDoneCallback(Executor, DexoptDoneCallback)}. Does nothing if the 679 * callback was not added. This method is thread-safe. 680 */ 681 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) removeDexoptDoneCallback(@onNull DexoptDoneCallback callback)682 public void removeDexoptDoneCallback(@NonNull DexoptDoneCallback callback) { 683 mInjector.getConfig().removeDexoptDoneCallback(callback); 684 } 685 686 /** 687 * Snapshots the profile of the given app split. The profile snapshot is the aggregation of all 688 * existing profiles of the app split (all current user profiles and the reference profile). 689 * 690 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 691 * @param packageName the name of the app that owns the profile 692 * @param splitName see {@link AndroidPackageSplit#getName()} 693 * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The 694 * caller is responsible for closing it. Note that the content may be empty. 695 * @throws IllegalArgumentException if the package or the split is not found 696 * @throws IllegalStateException if the operation encounters an error that should never happen 697 * (e.g., an internal logic error). 698 * @throws SnapshotProfileException if the operation encounters an error that the caller should 699 * handle (e.g., an I/O error, a sub-process crash). 700 */ 701 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 702 @NonNull snapshotAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName)703 public ParcelFileDescriptor snapshotAppProfile( 704 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 705 @Nullable String splitName) throws SnapshotProfileException { 706 var options = new MergeProfileOptions(); 707 options.forceMerge = true; 708 return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options); 709 } 710 711 /** 712 * Same as above, but outputs in text format. 713 * 714 * @hide 715 */ 716 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 717 @NonNull dumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, boolean dumpClassesAndMethods)718 public ParcelFileDescriptor dumpAppProfile( 719 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 720 @Nullable String splitName, boolean dumpClassesAndMethods) 721 throws SnapshotProfileException { 722 var options = new MergeProfileOptions(); 723 options.dumpOnly = !dumpClassesAndMethods; 724 options.dumpClassesAndMethods = dumpClassesAndMethods; 725 return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options); 726 } 727 728 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 729 @NonNull snapshotOrDumpAppProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, @Nullable String splitName, @NonNull MergeProfileOptions options)730 private ParcelFileDescriptor snapshotOrDumpAppProfile( 731 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName, 732 @Nullable String splitName, @NonNull MergeProfileOptions options) 733 throws SnapshotProfileException { 734 try { 735 PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName); 736 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 737 PrimaryDexInfo dexInfo = PrimaryDexUtils.getDexInfoBySplitName(pkg, splitName); 738 739 List<ProfilePath> profiles = new ArrayList<>(); 740 741 Pair<ProfilePath, Boolean> pair = Utils.getOrInitReferenceProfile(mInjector.getArtd(), 742 dexInfo.dexPath(), PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo), 743 PrimaryDexUtils.getExternalProfiles(dexInfo), 744 PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, Process.SYSTEM_UID, 745 Process.SYSTEM_UID, false /* isPublic */)); 746 ProfilePath refProfile = pair != null ? pair.first : null; 747 748 if (refProfile != null) { 749 profiles.add(refProfile); 750 } 751 752 profiles.addAll( 753 PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo)); 754 755 OutputProfile output = PrimaryDexUtils.buildOutputProfile(pkgState, dexInfo, 756 Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */); 757 758 try { 759 return mergeProfilesAndGetFd(profiles, output, List.of(dexInfo.dexPath()), options); 760 } finally { 761 if (refProfile != null && refProfile.getTag() == ProfilePath.tmpProfilePath) { 762 mInjector.getArtd().deleteProfile(refProfile); 763 } 764 } 765 } catch (RemoteException e) { 766 throw new SnapshotProfileException(e); 767 } 768 } 769 770 /** 771 * Snapshots the boot image profile 772 * (https://source.android.com/docs/core/bootloader/boot-image-profiles). The profile snapshot 773 * is the aggregation of all existing profiles on the device (all current user profiles and 774 * reference profiles) of all apps and the system server filtered by applicable classpaths. 775 * 776 * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on 777 * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The 778 * caller is responsible for closing it. Note that the content may be empty. 779 * @throws IllegalStateException if the operation encounters an error that should never happen 780 * (e.g., an internal logic error). 781 * @throws SnapshotProfileException if the operation encounters an error that the caller should 782 * handle (e.g., an I/O error, a sub-process crash). 783 */ 784 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 785 @NonNull snapshotBootImageProfile( @onNull PackageManagerLocal.FilteredSnapshot snapshot)786 public ParcelFileDescriptor snapshotBootImageProfile( 787 @NonNull PackageManagerLocal.FilteredSnapshot snapshot) 788 throws SnapshotProfileException { 789 if (!Constants.isBootImageProfilingEnabled()) { 790 throw new SnapshotProfileException("Boot image profiling not enabled"); 791 } 792 793 List<ProfilePath> profiles = new ArrayList<>(); 794 795 // System server profiles. 796 profiles.add(AidlUtils.buildProfilePathForPrimaryRef( 797 Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY)); 798 for (UserHandle handle : 799 mInjector.getUserManager().getUserHandles(true /* excludeDying */)) { 800 profiles.add(AidlUtils.buildProfilePathForPrimaryCur(handle.getIdentifier(), 801 Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY)); 802 } 803 804 // App profiles. 805 snapshot.getPackageStates().forEach((packageName, appPkgState) -> { 806 // Hibernating apps can still provide useful profile contents, so skip the hibernation 807 // check. 808 if (Utils.canDexoptPackage(appPkgState, null /* appHibernationManager */)) { 809 AndroidPackage appPkg = Utils.getPackageOrThrow(appPkgState); 810 for (PrimaryDexInfo appDexInfo : PrimaryDexUtils.getDexInfo(appPkg)) { 811 if (!appDexInfo.hasCode()) { 812 continue; 813 } 814 profiles.add(PrimaryDexUtils.buildRefProfilePath(appPkgState, appDexInfo)); 815 profiles.addAll(PrimaryDexUtils.getCurProfiles( 816 mInjector.getUserManager(), appPkgState, appDexInfo)); 817 } 818 } 819 }); 820 821 OutputProfile output = AidlUtils.buildOutputProfileForPrimary(Utils.PLATFORM_PACKAGE_NAME, 822 PrimaryDexUtils.PROFILE_PRIMARY, Process.SYSTEM_UID, Process.SYSTEM_UID, 823 false /* isPublic */); 824 825 List<String> dexPaths = Arrays.stream(CLASSPATHS_FOR_BOOT_IMAGE_PROFILE) 826 .map(envVar -> Constants.getenv(envVar)) 827 .filter(classpath -> !TextUtils.isEmpty(classpath)) 828 .flatMap(classpath -> Arrays.stream(classpath.split(":"))) 829 .collect(Collectors.toList()); 830 831 var options = new MergeProfileOptions(); 832 options.forceMerge = true; 833 options.forBootImage = true; 834 return mergeProfilesAndGetFd(profiles, output, dexPaths, options); 835 } 836 837 /** 838 * Notifies ART Service that this is a boot that falls into one of the categories listed in 839 * {@link BootReason}. The current behavior is that ART Service goes through all recently used 840 * packages and dexopts those that are not dexopted. This might change in the future. 841 * 842 * This method is blocking. It takes about 30 seconds to a few minutes. During execution, {@code 843 * progressCallback} is repeatedly called whenever there is an update on the progress. 844 * 845 * See {@link #dexoptPackages} for how to customize the behavior. 846 */ 847 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) onBoot(@onNull @ootReason String bootReason, @Nullable @CallbackExecutor Executor progressCallbackExecutor, @Nullable Consumer<OperationProgress> progressCallback)848 public void onBoot(@NonNull @BootReason String bootReason, 849 @Nullable @CallbackExecutor Executor progressCallbackExecutor, 850 @Nullable Consumer<OperationProgress> progressCallback) { 851 try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) { 852 dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor, 853 progressCallback); 854 } 855 } 856 857 /** 858 * Dumps the dexopt state of all packages in text format for debugging purposes. 859 * 860 * There are no stability guarantees for the output format. 861 * 862 * @throws IllegalStateException if the operation encounters an error that should never happen 863 * (e.g., an internal logic error). 864 */ 865 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) dump( @onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot)866 public void dump( 867 @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) { 868 new DumpHelper(this).dump(pw, snapshot); 869 } 870 871 /** 872 * Dumps the dexopt state of the given package in text format for debugging purposes. 873 * 874 * There are no stability guarantees for the output format. 875 * 876 * @throws IllegalArgumentException if the package is not found 877 * @throws IllegalStateException if the operation encounters an error that should never happen 878 * (e.g., an internal logic error). 879 */ 880 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) dumpPackage(@onNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName)881 public void dumpPackage(@NonNull PrintWriter pw, 882 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) { 883 new DumpHelper(this).dumpPackage( 884 pw, snapshot, Utils.getPackageStateOrThrow(snapshot, packageName)); 885 } 886 887 /** 888 * Cleans up obsolete profiles and artifacts. 889 * 890 * This is done in a mark-and-sweep approach. 891 * 892 * @return The amount of the disk space freed by the cleanup, in bytes. 893 * @hide 894 */ 895 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) cleanup(@onNull PackageManagerLocal.FilteredSnapshot snapshot)896 public long cleanup(@NonNull PackageManagerLocal.FilteredSnapshot snapshot) { 897 mInjector.getDexUseManager().cleanup(); 898 899 try { 900 // For every primary dex container file or secondary dex container file of every app, if 901 // it has code, we keep the following types of files: 902 // - The reference profile and the current profiles, regardless of the hibernation state 903 // of the app. 904 // - The dexopt artifacts, if they are up-to-date and the app is not hibernating. 905 // - Only the VDEX part of the dexopt artifacts, if the dexopt artifacts are outdated 906 // but the VDEX part is still usable and the app is not hibernating. 907 List<ProfilePath> profilesToKeep = new ArrayList<>(); 908 List<ArtifactsPath> artifactsToKeep = new ArrayList<>(); 909 List<VdexPath> vdexFilesToKeep = new ArrayList<>(); 910 911 for (PackageState pkgState : snapshot.getPackageStates().values()) { 912 if (!Utils.canDexoptPackage(pkgState, null /* appHibernationManager */)) { 913 continue; 914 } 915 AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); 916 boolean isInDalvikCache = Utils.isInDalvikCache(pkgState, mInjector.getArtd()); 917 boolean keepArtifacts = !Utils.shouldSkipDexoptDueToHibernation( 918 pkgState, mInjector.getAppHibernationManager()); 919 for (DetailedPrimaryDexInfo dexInfo : 920 PrimaryDexUtils.getDetailedDexInfo(pkgState, pkg)) { 921 if (!dexInfo.hasCode()) { 922 continue; 923 } 924 profilesToKeep.add(PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo)); 925 profilesToKeep.addAll(PrimaryDexUtils.getCurProfiles( 926 mInjector.getUserManager(), pkgState, dexInfo)); 927 if (keepArtifacts) { 928 for (Abi abi : Utils.getAllAbis(pkgState)) { 929 maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, pkgState, dexInfo, 930 abi, isInDalvikCache); 931 } 932 } 933 } 934 for (DetailedSecondaryDexInfo dexInfo : 935 mInjector.getDexUseManager().getFilteredDetailedSecondaryDexInfo( 936 pkgState.getPackageName())) { 937 profilesToKeep.add( 938 AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath())); 939 profilesToKeep.add( 940 AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); 941 if (keepArtifacts) { 942 for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { 943 maybeKeepArtifacts(artifactsToKeep, vdexFilesToKeep, pkgState, dexInfo, 944 abi, false /* isInDalvikCache */); 945 } 946 } 947 } 948 } 949 return mInjector.getArtd().cleanup(profilesToKeep, artifactsToKeep, vdexFilesToKeep); 950 } catch (RemoteException e) { 951 Utils.logArtdException(e); 952 return 0; 953 } 954 } 955 956 /** 957 * Checks if the artifacts are up-to-date, and maybe adds them to {@code artifactsToKeep} or 958 * {@code vdexFilesToKeep} based on the result. 959 */ 960 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) maybeKeepArtifacts(@onNull List<ArtifactsPath> artifactsToKeep, @NonNull List<VdexPath> vdexFilesToKeep, @NonNull PackageState pkgState, @NonNull DetailedDexInfo dexInfo, @NonNull Abi abi, boolean isInDalvikCache)961 private void maybeKeepArtifacts(@NonNull List<ArtifactsPath> artifactsToKeep, 962 @NonNull List<VdexPath> vdexFilesToKeep, @NonNull PackageState pkgState, 963 @NonNull DetailedDexInfo dexInfo, @NonNull Abi abi, boolean isInDalvikCache) 964 throws RemoteException { 965 try { 966 GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus( 967 dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext()); 968 if (DexFile.isValidCompilerFilter(result.compilerFilter)) { 969 // TODO(b/263579377): This is a bit inaccurate. We may be keeping the artifacts in 970 // dalvik-cache while OatFileAssistant actually picks the ones not in dalvik-cache. 971 // However, this isn't a big problem because it is an edge case and it only causes 972 // us to delete less rather than deleting more. 973 ArtifactsPath artifacts = 974 AidlUtils.buildArtifactsPath(dexInfo.dexPath(), abi.isa(), isInDalvikCache); 975 if (result.compilationReason.equals(ArtConstants.REASON_VDEX)) { 976 // Only the VDEX file is usable. 977 vdexFilesToKeep.add(VdexPath.artifactsPath(artifacts)); 978 } else { 979 artifactsToKeep.add(artifacts); 980 } 981 } 982 } catch (ServiceSpecificException e) { 983 // Don't add the artifacts to the lists. They should be cleaned up. 984 Log.e(TAG, 985 String.format("Failed to get dexopt status [packageName = %s, dexPath = %s, " 986 + "isa = %s, classLoaderContext = %s]", 987 pkgState.getPackageName(), dexInfo.dexPath(), abi.isa(), 988 dexInfo.classLoaderContext()), 989 e); 990 } 991 } 992 993 /** 994 * Should be used by {@link BackgroundDexoptJobService} ONLY. 995 * 996 * @hide 997 */ 998 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 999 @NonNull getBackgroundDexoptJob()1000 BackgroundDexoptJob getBackgroundDexoptJob() { 1001 return mInjector.getBackgroundDexoptJob(); 1002 } 1003 1004 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) maybeDowngradePackages(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal, @NonNull Executor executor)1005 private void maybeDowngradePackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1006 @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal, 1007 @NonNull Executor executor) { 1008 if (shouldDowngrade()) { 1009 List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE) 1010 .stream() 1011 .filter(pkg -> !excludedPackages.contains(pkg)) 1012 .collect(Collectors.toList()); 1013 if (!packages.isEmpty()) { 1014 Log.i(TAG, "Storage is low. Downgrading inactive packages"); 1015 DexoptParams params = 1016 new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build(); 1017 mInjector.getDexoptHelper().dexopt(snapshot, packages, params, cancellationSignal, 1018 executor, null /* processCallbackExecutor */, null /* progressCallback */); 1019 } else { 1020 Log.i(TAG, 1021 "Storage is low, but downgrading is disabled or there's nothing to " 1022 + "downgrade"); 1023 } 1024 } 1025 } 1026 1027 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) shouldDowngrade()1028 private boolean shouldDowngrade() { 1029 try { 1030 return mInjector.getStorageManager().getAllocatableBytes(StorageManager.UUID_DEFAULT) 1031 < DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES; 1032 } catch (IOException e) { 1033 Log.e(TAG, "Failed to check storage. Assuming storage not low", e); 1034 return false; 1035 } 1036 } 1037 1038 /** Returns the list of packages to process for the given reason. */ 1039 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1040 @NonNull getDefaultPackages(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String reason)1041 private List<String> getDefaultPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1042 @NonNull /* @BatchDexoptReason|REASON_INACTIVE */ String reason) { 1043 var appHibernationManager = mInjector.getAppHibernationManager(); 1044 1045 // Filter out hibernating packages even if the reason is REASON_INACTIVE. This is because 1046 // artifacts for hibernating packages are already deleted. 1047 Stream<PackageState> packages = snapshot.getPackageStates().values().stream().filter( 1048 pkgState -> Utils.canDexoptPackage(pkgState, appHibernationManager)); 1049 1050 switch (reason) { 1051 case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE: 1052 packages = packages.filter(pkgState 1053 -> mInjector.isSystemUiPackage(pkgState.getPackageName()) 1054 || mInjector.isLauncherPackage(pkgState.getPackageName())); 1055 break; 1056 case ReasonMapping.REASON_INACTIVE: 1057 packages = filterAndSortByLastActiveTime( 1058 packages, false /* keepRecent */, false /* descending */); 1059 break; 1060 default: 1061 // Actually, the sorting is only needed for background dexopt, but we do it for all 1062 // cases for simplicity. 1063 packages = filterAndSortByLastActiveTime( 1064 packages, true /* keepRecent */, true /* descending */); 1065 } 1066 1067 return packages.map(PackageState::getPackageName).collect(Collectors.toList()); 1068 } 1069 1070 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1071 @NonNull filterAndSortByLastActiveTime( @onNull Stream<PackageState> packages, boolean keepRecent, boolean descending)1072 private Stream<PackageState> filterAndSortByLastActiveTime( 1073 @NonNull Stream<PackageState> packages, boolean keepRecent, boolean descending) { 1074 // "pm.dexopt.downgrade_after_inactive_days" is repurposed to also determine whether to 1075 // dexopt a package. 1076 long inactiveMs = TimeUnit.DAYS.toMillis(SystemProperties.getInt( 1077 "pm.dexopt.downgrade_after_inactive_days", Integer.MAX_VALUE /* def */)); 1078 long currentTimeMs = mInjector.getCurrentTimeMillis(); 1079 long thresholdTimeMs = currentTimeMs - inactiveMs; 1080 return packages 1081 .map(pkgState 1082 -> Pair.create(pkgState, 1083 Utils.getPackageLastActiveTime(pkgState, 1084 mInjector.getDexUseManager(), mInjector.getUserManager()))) 1085 .filter(keepRecent ? (pair -> pair.second > thresholdTimeMs) 1086 : (pair -> pair.second <= thresholdTimeMs)) 1087 .sorted(descending ? Comparator.comparingLong(pair -> - pair.second) 1088 : Comparator.comparingLong(pair -> pair.second)) 1089 .map(pair -> pair.first); 1090 } 1091 1092 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1093 @NonNull mergeProfilesAndGetFd(@onNull List<ProfilePath> profiles, @NonNull OutputProfile output, @NonNull List<String> dexPaths, @NonNull MergeProfileOptions options)1094 private ParcelFileDescriptor mergeProfilesAndGetFd(@NonNull List<ProfilePath> profiles, 1095 @NonNull OutputProfile output, @NonNull List<String> dexPaths, 1096 @NonNull MergeProfileOptions options) throws SnapshotProfileException { 1097 try { 1098 boolean hasContent = false; 1099 try { 1100 hasContent = mInjector.getArtd().mergeProfiles( 1101 profiles, null /* referenceProfile */, output, dexPaths, options); 1102 } catch (ServiceSpecificException e) { 1103 throw new SnapshotProfileException(e); 1104 } 1105 1106 String path; 1107 Path emptyFile = null; 1108 if (hasContent) { 1109 path = output.profilePath.tmpPath; 1110 } else { 1111 // We cannot use /dev/null because `ParcelFileDescriptor` have an API `getStatSize`, 1112 // which expects the file to be a regular file or a link, and apps may call that 1113 // API. 1114 emptyFile = 1115 Files.createTempFile(Paths.get(mInjector.getTempDir()), "empty", ".tmp"); 1116 path = emptyFile.toString(); 1117 } 1118 ParcelFileDescriptor fd; 1119 try { 1120 fd = ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY); 1121 } catch (FileNotFoundException e) { 1122 throw new IllegalStateException( 1123 String.format("Failed to open profile snapshot '%s'", path), e); 1124 } 1125 1126 // The deletion is done on the open file so that only the FD keeps a reference to the 1127 // file. 1128 if (hasContent) { 1129 mInjector.getArtd().deleteProfile(ProfilePath.tmpProfilePath(output.profilePath)); 1130 } else { 1131 Files.delete(emptyFile); 1132 } 1133 1134 return fd; 1135 } catch (IOException | RemoteException e) { 1136 throw new SnapshotProfileException(e); 1137 } 1138 } 1139 1140 /** @hide */ 1141 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1142 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1143 public interface BatchDexoptStartCallback { 1144 /** 1145 * Mutates {@code builder} to override the default params for {@link #dexoptPackages}. It 1146 * must ignore unknown reasons because more reasons may be added in the future. 1147 * 1148 * This is called before the start of any automatic package dexopt (i.e., not 1149 * including package dexopt initiated by the {@link #dexoptPackage} API call). 1150 * 1151 * If {@code builder.setPackages} is not called, {@code defaultPackages} will be used as the 1152 * list of packages to dexopt. 1153 * 1154 * If {@code builder.setDexoptParams} is not called, the default params built from {@code 1155 * new DexoptParams.Builder(reason)} will to used as the params for dexopting each 1156 * package. 1157 * 1158 * Additionally, {@code cancellationSignal.cancel()} can be called to cancel this operation. 1159 * If this operation is initiated by the job scheduler and the {@code reason} is {@link 1160 * ReasonMapping#REASON_BG_DEXOPT}, the job will be retried in the next <i>maintenance 1161 * window</i>. For information about <i>maintenance window</i>, see 1162 * https://developer.android.com/training/monitoring-device-state/doze-standby. 1163 * 1164 * Changing the reason is not allowed. Doing so will result in {@link IllegalStateException} 1165 * when {@link #dexoptPackages} is called. 1166 */ onBatchDexoptStart(@onNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages, @NonNull BatchDexoptParams.Builder builder, @NonNull CancellationSignal cancellationSignal)1167 void onBatchDexoptStart(@NonNull PackageManagerLocal.FilteredSnapshot snapshot, 1168 @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages, 1169 @NonNull BatchDexoptParams.Builder builder, 1170 @NonNull CancellationSignal cancellationSignal); 1171 } 1172 1173 /** @hide */ 1174 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1175 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1176 public interface ScheduleBackgroundDexoptJobCallback { 1177 /** 1178 * Mutates {@code builder} to override the configuration of the background dexopt job. 1179 * 1180 * The default configuration described in {@link 1181 * ArtManagerLocal#scheduleBackgroundDexoptJob()} is passed to the callback as the {@code 1182 * builder} argument. 1183 * 1184 * Setting {@link JobInfo.Builder#setRequiresStorageNotLow(boolean)} is not allowed. Doing 1185 * so will result in {@link IllegalStateException} when {@link 1186 * #scheduleBackgroundDexoptJob()} is called. ART Service has its own storage check, which 1187 * skips package dexopt when the storage is low. The storage check is enabled by 1188 * default for background dexopt jobs. {@link 1189 * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} can be used to disable 1190 * the storage check by clearing the {@link ArtFlags#FLAG_SKIP_IF_STORAGE_LOW} flag. 1191 */ onOverrideJobInfo(@onNull JobInfo.Builder builder)1192 void onOverrideJobInfo(@NonNull JobInfo.Builder builder); 1193 } 1194 1195 /** @hide */ 1196 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1197 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1198 public interface DexoptDoneCallback { onDexoptDone(@onNull DexoptResult result)1199 void onDexoptDone(@NonNull DexoptResult result); 1200 } 1201 1202 /** 1203 * Represents an error that happens when snapshotting profiles. 1204 * 1205 * @hide 1206 */ 1207 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) 1208 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1209 public static class SnapshotProfileException extends Exception { 1210 /** @hide */ SnapshotProfileException(@onNull Throwable cause)1211 public SnapshotProfileException(@NonNull Throwable cause) { 1212 super(cause); 1213 } 1214 1215 /** @hide */ SnapshotProfileException(@onNull String message)1216 public SnapshotProfileException(@NonNull String message) { 1217 super(message); 1218 } 1219 } 1220 1221 /** 1222 * Injector pattern for testing purpose. 1223 * 1224 * @hide 1225 */ 1226 @VisibleForTesting 1227 public static class Injector { 1228 @Nullable private final ArtManagerLocal mArtManagerLocal; 1229 @Nullable private final Context mContext; 1230 @Nullable private final PackageManagerLocal mPackageManagerLocal; 1231 @Nullable private final Config mConfig; 1232 @Nullable private BackgroundDexoptJob mBgDexoptJob = null; 1233 1234 // TODO(jiakaiz): Remove @SuppressLint and check `Build.VERSION.SDK_INT >= 1235 // Build.VERSION_CODES.UPSIDE_DOWN_CAKE` once the SDK is finalized. 1236 @SuppressLint("NewApi") Injector(@onNull ArtManagerLocal artManagerLocal, @Nullable Context context)1237 Injector(@NonNull ArtManagerLocal artManagerLocal, @Nullable Context context) { 1238 mArtManagerLocal = artManagerLocal; 1239 mContext = context; 1240 if (context != null) { 1241 // We only need them on Android U and above, where a context is passed. 1242 mPackageManagerLocal = Objects.requireNonNull( 1243 LocalManagerRegistry.getManager(PackageManagerLocal.class)); 1244 mConfig = new Config(); 1245 1246 // Call the getters for the dependencies that aren't optional, to ensure correct 1247 // initialization order. 1248 getDexoptHelper(); 1249 getUserManager(); 1250 getDexUseManager(); 1251 getStorageManager(); 1252 ArtModuleServiceInitializer.getArtModuleServiceManager(); 1253 } else { 1254 mPackageManagerLocal = null; 1255 mConfig = null; 1256 } 1257 } 1258 1259 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1260 @NonNull getContext()1261 public Context getContext() { 1262 return Objects.requireNonNull(mContext); 1263 } 1264 1265 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1266 @NonNull getPackageManagerLocal()1267 public PackageManagerLocal getPackageManagerLocal() { 1268 return Objects.requireNonNull(mPackageManagerLocal); 1269 } 1270 1271 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1272 @NonNull getArtd()1273 public IArtd getArtd() { 1274 return Utils.getArtd(); 1275 } 1276 1277 /** Returns a new {@link DexoptHelper} instance. */ 1278 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1279 @NonNull getDexoptHelper()1280 public DexoptHelper getDexoptHelper() { 1281 return new DexoptHelper(getContext(), getConfig()); 1282 } 1283 1284 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1285 @NonNull getConfig()1286 public Config getConfig() { 1287 return mConfig; 1288 } 1289 1290 /** Returns the registered {@link AppHibernationManager} instance. */ 1291 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1292 @NonNull getAppHibernationManager()1293 public AppHibernationManager getAppHibernationManager() { 1294 return Objects.requireNonNull(mContext.getSystemService(AppHibernationManager.class)); 1295 } 1296 1297 /** 1298 * Returns the {@link BackgroundDexoptJob} instance. 1299 * 1300 * @throws RuntimeException if called during boot before the job scheduler service has 1301 * started. 1302 */ 1303 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1304 @NonNull getBackgroundDexoptJob()1305 public synchronized BackgroundDexoptJob getBackgroundDexoptJob() { 1306 if (mBgDexoptJob == null) { 1307 mBgDexoptJob = new BackgroundDexoptJob(mContext, mArtManagerLocal, mConfig); 1308 } 1309 return mBgDexoptJob; 1310 } 1311 1312 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1313 @NonNull getUserManager()1314 public UserManager getUserManager() { 1315 return Objects.requireNonNull(mContext.getSystemService(UserManager.class)); 1316 } 1317 1318 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1319 @NonNull getDexUseManager()1320 public DexUseManagerLocal getDexUseManager() { 1321 return Objects.requireNonNull( 1322 LocalManagerRegistry.getManager(DexUseManagerLocal.class)); 1323 } 1324 1325 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) isSystemUiPackage(@onNull String packageName)1326 public boolean isSystemUiPackage(@NonNull String packageName) { 1327 return Utils.isSystemUiPackage(mContext, packageName); 1328 } 1329 1330 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) isLauncherPackage(@onNull String packageName)1331 public boolean isLauncherPackage(@NonNull String packageName) { 1332 return Utils.isLauncherPackage(mContext, packageName); 1333 } 1334 1335 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) getCurrentTimeMillis()1336 public long getCurrentTimeMillis() { 1337 return System.currentTimeMillis(); 1338 } 1339 1340 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1341 @NonNull getStorageManager()1342 public StorageManager getStorageManager() { 1343 return Objects.requireNonNull(mContext.getSystemService(StorageManager.class)); 1344 } 1345 1346 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1347 @NonNull getTempDir()1348 public String getTempDir() { 1349 // This is a path that system_server is known to have full access to. 1350 return "/data/system"; 1351 } 1352 } 1353 } 1354