Configuration du service ART

Avant de commencer, consultez une présentation générale du service ART .

À partir d’Android 14, la compilation AOT sur l’appareil pour les applications (alias dexopt) est gérée par ART Service. ART Service fait partie du module ART et vous pouvez le personnaliser via les propriétés du système et les API.

Propriétés du système

ART Service prend en charge toutes les options dex2oat pertinentes.

De plus, le service ART prend en charge les propriétés système suivantes :

pm.dexopt.<raison>

Il s'agit d'un ensemble de propriétés système qui déterminent les filtres par défaut du compilateur pour toutes les raisons de compilation prédéfinies décrites dans les scénarios Dexopt .

Pour plus d’informations, consultez Filtres du compilateur .

Les valeurs standard par défaut sont :

pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify

pm.dexopt.shared (par défaut : vitesse)

Il s'agit du filtre du compilateur de secours pour les applications utilisées par d'autres applications.

En principe, ART Service effectue une compilation guidée par profil ( speed-profile ) pour toutes les applications lorsque cela est possible, généralement pendant la dexopt en arrière-plan. Cependant, certaines applications sont utilisées par d'autres applications (soit via <uses-library> soit chargées dynamiquement à l'aide de Context#createPackageContext avec CONTEXT_INCLUDE_CODE ). Ces applications ne peuvent pas utiliser de profils locaux pour des raisons de confidentialité.

Pour une telle application, si une compilation guidée par profil est demandée, ART Service essaie d'abord d'utiliser un profil cloud. Si un profil cloud n'existe pas, ART Service utilise le filtre du compilateur spécifié par pm.dexopt.shared .

Si la compilation demandée n'est pas guidée par profil, cette propriété n'a aucun effet.

pm.dexopt.<raison>.concurrency (par défaut : 1)

Il s'agit du nombre d'appels dex2oat pour certaines raisons de compilation prédéfinies ( first-boot , boot-after-ota , boot-after-mainline-update et bg-dexopt ).

Notez que l'effet de cette option est combiné avec les options d'utilisation des ressources dex2oat ( dalvik.vm.*dex2oat-threads , dalvik.vm.*dex2oat-cpu-set et les profils de tâches) :

  • dalvik.vm.*dex2oat-threads contrôle le nombre de threads pour chaque invocation dex2oat, tandis que pm.dexopt.<reason>.concurrency contrôle le nombre d'invocations dex2oat. Autrement dit, le nombre maximum de threads simultanés est le produit des deux propriétés système.
  • dalvik.vm.*dex2oat-cpu-set et les profils de tâches limitent toujours l'utilisation du cœur du processeur, quel que soit le nombre maximum de threads simultanés (discuté ci-dessus).

Un seul appel dex2oat peut ne pas utiliser pleinement tous les cœurs du processeur, quels que soient dalvik.vm.*dex2oat-threads . Par conséquent, l'augmentation du nombre d'invocations dex2oat ( pm.dexopt.<reason>.concurrency ) permet de mieux utiliser les cœurs de processeur, pour accélérer la progression globale de dexopt. Ceci est particulièrement utile lors du démarrage.

Cependant, un trop grand nombre d'appels dex2oat peut entraîner un manque de mémoire sur le périphérique, même si cela peut être atténué en définissant dalvik.vm.dex2oat-swap sur true pour autoriser l'utilisation d'un fichier d'échange. Un trop grand nombre d'appels peut également entraîner un changement de contexte inutile. Par conséquent, ce chiffre doit être soigneusement ajusté produit par produit.

pm.dexopt.downgrade_after_inactive_days (par défaut : non défini)

Si cette option est définie, ART Service déxopte uniquement les applications utilisées au cours du dernier nombre de jours donné.

De plus, si le stockage est presque faible, lors du dexopt en arrière-plan, ART Service rétrograde le filtre du compilateur des applications qui n'ont pas été utilisées au cours du dernier nombre de jours donné, pour libérer de l'espace. La raison du compilateur est inactive et le filtre du compilateur est déterminé par pm.dexopt.inactive . Le seuil d'espace pour déclencher cette fonctionnalité est le seuil d'espace faible du Storage Manager (configurable via les paramètres globaux sys_storage_threshold_percentage et sys_storage_threshold_max_bytes , par défaut : 500 Mo) plus 500 Mo.

Si vous personnalisez la liste des packages via ArtManagerLocal#setBatchDexoptStartCallback , les packages de la liste fournie par BatchDexoptStartCallback pour bg-dexopt ne sont jamais rétrogradés.

pm.dexopt.disable_bg_dexopt (par défaut : false)

Ceci est uniquement destiné aux tests. Cela empêche ART Service de planifier le travail dexopt en arrière-plan.

Si le travail dexopt en arrière-plan est déjà planifié mais n'a pas encore été exécuté, cette option n'a aucun effet. Autrement dit, le travail continuera à s'exécuter.

Une séquence de commandes recommandée pour empêcher l’exécution du travail dexopt en arrière-plan est :

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

La première ligne empêche la planification du travail dexopt en arrière-plan, s'il n'est pas encore planifié. La deuxième ligne déplanifie le travail dexopt en arrière-plan, s'il est déjà planifié, et annule immédiatement le travail dexopt en arrière-plan, s'il est en cours d'exécution.

API du service ART

ART Service expose les API Java pour la personnalisation. Les API sont définies dans ArtManagerLocal . Voir la Javadoc dans art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java pour les utilisations ( source Android 14 , source de développement inédite ).

ArtManagerLocal est un singleton détenu par LocalManagerRegistry . Une fonction d'assistance com.android.server.pm.DexOptHelper#getArtManagerLocal vous aide à l'obtenir.

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

La plupart des API nécessitent une instance de PackageManagerLocal.FilteredSnapshot , qui contient les informations de toutes les applications. Vous pouvez l'obtenir en appelant PackageManagerLocal#withFilteredSnapshot , où PackageManagerLocal est également un singleton détenu par LocalManagerRegistry et peut être obtenu à partir de com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal .

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

Voici quelques cas d’utilisation typiques des API.

Déclencher Dexopt pour une application

Vous pouvez déclencher dexopt pour n'importe quelle application à tout moment en appelant ArtManagerLocal#dexoptPackage .

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

Vous pouvez également transmettre votre propre raison dexopt. Si vous faites cela, la classe de priorité et le filtre du compilateur doivent être explicitement définis.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder("my-reason")
          .setCompilerFilter("speed-profile")
          .setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
          .build());
}

Annuler l'exclusion

Si une opération est initiée par un appel dexoptPackage , vous pouvez transmettre un signal d'annulation, qui vous permet d'annuler l'opération à un moment donné. Cela peut être utile lorsque vous exécutez dexopt de manière asynchrone.

Executor executor = ...;  // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
  try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
    getArtManagerLocal().dexoptPackage(
        snapshot,
        "com.google.android.calculator",
        new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
        cancellationSignal);
  }
});

// When you want to cancel the operation.
cancellationSignal.cancel();

Vous pouvez également annuler le dexopt en arrière-plan, initié par ART Service.

getArtManagerLocal().cancelBackgroundDexoptJob();

Obtenez des résultats Dexopt

Si une opération est initiée par un appel dexoptPackage , vous pouvez obtenir le résultat à partir de la valeur de retour.

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

ART Service lance également lui-même les opérations dexopt dans de nombreux scénarios, tels que le dexopt en arrière-plan. Pour écouter tous les résultats dexopt, que l'opération soit initiée par un appel dexoptPackage ou par ART Service, utilisez ArtManagerLocal#addDexoptDoneCallback .

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

Le premier argument détermine s'il faut inclure uniquement les mises à jour dans le résultat. Si vous souhaitez uniquement écouter les packages mis à jour par dexopt, définissez-le sur true.

Le deuxième argument est l'exécuteur du rappel. Pour exécuter le rappel sur le même thread qui exécute dexopt, utilisez Runnable::run . Si vous ne souhaitez pas que le rappel bloque dexopt, utilisez un exécuteur asynchrone.

Vous pouvez ajouter plusieurs rappels et ART Service les exécutera tous séquentiellement. Tous les rappels resteront actifs pour tous les appels futurs, sauf si vous les supprimez.

Si vous souhaitez supprimer un rappel, conservez la référence du rappel lorsque vous l'ajoutez et utilisez ArtManagerLocal#removeDexoptDoneCallback .

DexoptDoneCallback callback = (result) -> {
  // Process the result here.
  ...
};

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */, Runnable::run, callback);

// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);

Personnaliser la liste des packages et les paramètres dexopt

Le service ART lance lui-même les opérations dexopt au démarrage et en arrière-plan. Pour personnaliser la liste des packages ou les paramètres dexopt pour ces opérations, utilisez ArtManagerLocal#setBatchDexoptStartCallback .

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      switch (reason) {
        case ReasonMapping.REASON_BG_DEXOPT:
          var myPackages = new ArrayList<String>(defaultPackages);
          myPackages.add(...);
          myPackages.remove(...);
          myPackages.sort(...);
          builder.setPackages(myPackages);
          break;
        default:
          // Ignore unknown reasons.
      }
    });

Vous pouvez ajouter des éléments à la liste des packages, en supprimer des éléments, la trier ou même utiliser une liste complètement différente.

Votre rappel doit ignorer les raisons inconnues, car d'autres raisons pourraient être ajoutées à l'avenir.

Vous pouvez définir au plus un BatchDexoptStartCallback . Le rappel restera actif pour tous les appels futurs, sauf si vous l'effacez.

Si vous souhaitez effacer le rappel, utilisez ArtManagerLocal#clearBatchDexoptStartCallback .

getArtManagerLocal().clearBatchDexoptStartCallback();

Personnaliser les paramètres du travail dexopt en arrière-plan

Par défaut, la tâche dexopt en arrière-plan s'exécute une fois par jour lorsque l'appareil est inactif et en charge. Cela peut être modifié en utilisant ArtManagerLocal#setScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

Vous pouvez définir au plus un ScheduleBackgroundDexoptJobCallback . Le rappel restera actif pour tous les appels futurs, sauf si vous l'effacez.

Si vous souhaitez effacer le rappel, utilisez ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Désactiver temporairement Dexopt

Toute opération dexopt initiée par le service ART déclenche un BatchDexoptStartCallback . Vous pouvez continuer à annuler les opérations pour désactiver efficacement dexopt.

Si l'opération que vous annulez est une opération dexopt en arrière-plan, elle suit la politique de nouvelle tentative par défaut (30 secondes, exponentielle, plafonnée à 5 heures).

// Good example.

var shouldDisableDexopt = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (shouldDisableDexopt.get()) {
        cancellationSignal.cancel();
      }
    });

// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();

// Re-enable dexopt.
shouldDisableDexopt.set(false);

Vous pouvez avoir au plus un BatchDexoptStartCallback . Si vous souhaitez également utiliser BatchDexoptStartCallback pour personnaliser la liste des packages ou les paramètres dexopt, vous devez combiner le code en un seul rappel.

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

L'opération dexopt effectuée lors de l'installation de l'application n'est pas initiée par ART Service. Au lieu de cela, il est initié par le gestionnaire de packages via un appel dexoptPackage . Par conséquent, cela ne déclenche pas BatchDexoptStartCallback . Pour désactiver dexopt lors de l'installation de l'application, empêchez le gestionnaire de packages d'appeler dexoptPackage .

Remplacer le filtre du compilateur pour certains packages (Android 15 (AOSP expérimental)+)

Vous pouvez remplacer le filtre du compilateur pour certains packages en enregistrant un rappel via setAdjustCompilerFilterCallback . Le rappel est appelé chaque fois qu'un package va être déxopté, peu importe que le dexopt soit initié par le service ART lors du démarrage et du dexopt en arrière-plan ou par un appel d'API dexoptPackage .

Si un package n’a pas besoin d’ajustement, le rappel doit renvoyer originalCompilerFilter .

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

Vous ne pouvez définir qu’un seul AdjustCompilerFilterCallback . Si vous souhaitez utiliser AdjustCompilerFilterCallback pour remplacer le filtre du compilateur pour plusieurs packages, vous devez combiner le code en un seul rappel. Le rappel reste actif pour tous les appels futurs, sauf si vous l'effacez.

Si vous souhaitez effacer le rappel, utilisez ArtManagerLocal#clearAdjustCompilerFilterCallback .

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Autres personnalisations

Art Service prend également en charge d'autres personnalisations.

Réglez le seuil thermique pour l'arrière-plan Dexopt

Le contrôle thermique du travail Dexopt d'arrière-plan est effectué par le planificateur de travaux. Le travail est annulé immédiatement lorsque la température atteint THERMAL_STATUS_MODERATE . Le seuil de THERMAL_STATUS_MODERATE est accordable.

Déterminez si Dexopt est en cours d'exécution

Le travail de Dexopt d'arrière-plan est géré par le planificateur d'emploi et son ID de travail est 27873780 . Pour déterminer si le travail est en cours d'exécution, utilisez des API du planificateur de travail.

// Good example.

var jobScheduler =
    Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);

if (reason == PENDING_JOB_REASON_EXECUTING) {
  // Do something when the job is running.
  ...
}
// Bad example.

var backgroundDexoptRunning = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(true);
      }
    });

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(false);
      }
    });

if (backgroundDexoptRunning.get()) {
  // Do something when the job is running.
  ...
}

Fournir un profil pour Dexopt

Pour utiliser un profil pour guider Dexopt, mettez un fichier .prof ou un fichier .dm à côté de l'APK.

Le fichier .prof doit être un fichier de profil de format binaire, et le nom de fichier doit être le nom de fichier de l'APK + .prof . Par exemple,

base.apk.prof

Le nom de fichier du .dm doit être le nom de fichier de l'APK avec l'extension remplacée par .dm . Par exemple,

base.dm

Pour vérifier que le profil est utilisé pour Dexopt, exécutez Dexopt avec speed-profile et vérifiez le résultat.

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

La première ligne efface tous les profils produits par l'exécution (c'est-à-dire ceux dans /data/misc/profiles ), le cas échéant, pour s'assurer que le profil à côté de l'APK est le seul profil que le service d'art peut éventuellement utiliser. La deuxième ligne exécute Dexopt avec speed-profile , et il passe -v pour imprimer le résultat verbeux.

Si le profil est utilisé, vous voyez actualCompilerFilter=speed-profile dans le résultat. Sinon, vous voyez actualCompilerFilter=verify . Par exemple,

DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}

Les raisons typiques pour lesquelles Art Service n'utilise pas le profil incluent les éléments suivants:

  • Le profil a un mauvais nom de fichier ou il n'est pas à côté de l'APK.
  • Le profil est dans le mauvais format.
  • Le profil ne correspond pas à l'APK. (Les sommes de contrôle du profil ne correspondent pas aux sommes de contrôle des fichiers .dex dans l'APK.)