Конфигурация службы ART

Прежде чем начать, ознакомьтесь с общим обзором службы ART .

Начиная с Android 14, компиляция AOT для приложений на устройстве (также известная как dexopt) выполняется службой ART. ART Service является частью модуля ART, и вы можете настроить его с помощью системных свойств и API.

Свойства системы

ART Service поддерживает все соответствующие опции dex2oat .

Кроме того, ART Service поддерживает следующие свойства системы:

pm.dexopt.<причина>

Это набор системных свойств, которые определяют фильтры компилятора по умолчанию для всех предопределенных причин компиляции , описанных в сценариях Dexopt .

Дополнительные сведения см. в разделе Фильтры компилятора .

Стандартные значения по умолчанию:

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 (по умолчанию: скорость)

Это резервный фильтр компилятора для приложений, используемых другими приложениями.

В принципе, ART Service выполняет компиляцию на основе профиля ( speed-profile ) для всех приложений, когда это возможно, обычно во время фоновой dexopt. Однако есть некоторые приложения, которые используются другими приложениями (либо через <uses-library> , либо загружаются динамически с помощью Context#createPackageContext с CONTEXT_INCLUDE_CODE ). Такие приложения не могут использовать локальные профили по соображениям конфиденциальности.

Для такого приложения, если запрашивается компиляция на основе профиля, ART Service сначала пытается использовать облачный профиль. Если облачный профиль не существует, ART Service возвращается к использованию фильтра компилятора, указанного pm.dexopt.shared .

Если запрошенная компиляция не ориентирована на профиль, это свойство не имеет никакого эффекта.

pm.dexopt.<причина>.concurrency (по умолчанию: 1)

Это количество вызовов dex2oat по определенным предопределенным причинам компиляции ( first-boot , boot-after-ota , boot-after-mainline-update и bg-dexopt ).

Обратите внимание, что эффект этого параметра сочетается с параметрами использования ресурсов dex2oat ( dalvik.vm.*dex2oat-threads , dalvik.vm.*dex2oat-cpu-set и профилями задач):

  • dalvik.vm.*dex2oat-threads управляет количеством потоков для каждого вызова dex2oat, а pm.dexopt.<reason>.concurrency контролирует количество вызовов dex2oat. То есть максимальное количество одновременных потоков является произведением двух системных свойств.
  • dalvik.vm.*dex2oat-cpu-set и профили задач всегда ограничивают использование ядра ЦП, независимо от максимального количества одновременных потоков (обсуждалось выше).

Один вызов dex2oat может не полностью задействовать все ядра ЦП, независимо от dalvik.vm.*dex2oat-threads . Таким образом, увеличение количества вызовов dex2oat ( pm.dexopt.<reason>.concurrency ) позволяет лучше использовать ядра ЦП и ускорить общий прогресс dexopt. Это особенно полезно во время загрузки.

Однако слишком большое количество вызовов dex2oat может привести к нехватке памяти на устройстве, хотя эту проблему можно смягчить, установив для dalvik.vm.dex2oat-swap значение true , чтобы разрешить использование файла подкачки. Слишком большое количество вызовов также может привести к ненужному переключению контекста. Поэтому это число следует тщательно корректировать для каждого продукта.

pm.dexopt.downgrade_after_inactive_days (по умолчанию: не установлено)

Если этот параметр установлен, ART Service сканирует только приложения, использованные в течение последнего заданного количества дней.

Кроме того, если объем памяти почти исчерпан, во время фоновой обработки ART Service понижает уровень фильтра компилятора приложений, которые не использовались в течение последнего заданного количества дней, чтобы освободить место. Причина этого — inactive компилятора, а фильтр компилятора определяется pm.dexopt.inactive . Пороговое значение пространства для запуска этой функции — это пороговое значение малого пространства диспетчера хранилища (настраиваемое с помощью глобальных параметров sys_storage_threshold_percentage и sys_storage_threshold_max_bytes , по умолчанию: 500 МБ) плюс 500 МБ.

Если вы настраиваете список пакетов с помощью ArtManagerLocal#setBatchDexoptStartCallback , пакеты в списке, предоставленном BatchDexoptStartCallback для bg-dexopt никогда не будут понижены.

pm.dexopt.disable_bg_dexopt (по умолчанию: false)

Это только для тестирования. Это не позволяет службе ART планировать фоновое задание dexopt.

Если фоновое задание dexopt уже запланировано, но еще не запущено, этот параметр не действует. То есть задание все равно будет выполняться.

Рекомендуемая последовательность команд для предотвращения запуска фонового задания dexopt:

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

Первая строка предотвращает планирование фонового задания dexopt, если оно еще не запланировано. Вторая строка отменяет планирование фонового задания dexopt, если оно уже запланировано, и немедленно отменяет фоновое задание dexopt, если оно выполняется.

API-интерфейсы службы ART

ART Service предоставляет API-интерфейсы Java для настройки. API определены в ArtManagerLocal . См. Javadoc в art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java для использования ( исходный код Android 14 , неизданный исходный код разработки ).

ArtManagerLocal — это синглтон, хранящийся в LocalManagerRegistry . Вспомогательная функция com.android.server.pm.DexOptHelper#getArtManagerLocal поможет вам получить его.

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

Большинству API требуется экземпляр PackageManagerLocal.FilteredSnapshot , который содержит информацию обо всех приложениях. Вы можете получить его, вызвав PackageManagerLocal#withFilteredSnapshot , где PackageManagerLocal также является синглтоном, хранящимся в LocalManagerRegistry , и его можно получить из com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal .

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

Ниже приведены некоторые типичные случаи использования API.

Запустить dexopt для приложения

Вы можете запустить dexopt для любого приложения в любое время, вызвав ArtManagerLocal#dexoptPackage .

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

Вы также можете указать свою собственную причину dexopt. Если вы это сделаете, класс приоритета и фильтр компилятора должны быть установлены явно.

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());
}

Отменить дексопт

Если операция инициируется вызовом dexoptPackage , вы можете передать сигнал отмены, который позволит вам отменить операцию в определенный момент. Это может быть полезно при асинхронном запуске dexopt.

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();

Вы также можете отменить фоновую dexopt, которая инициируется службой ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Получить результаты дексопта

Если операция инициируется вызовом dexoptPackage , вы можете получить результат из возвращаемого значения.

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

// Process the result here.
...

ART Service также инициирует операции dexopt во многих сценариях, например, в фоновом режиме. Чтобы прослушать все результаты dexopt, независимо от того, инициирована ли операция вызовом dexoptPackage или службой ART, используйте ArtManagerLocal#addDexoptDoneCallback .

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

Первый аргумент определяет, включать ли в результат только обновления. Если вы хотите прослушивать только пакеты, обновляемые dexopt, установите для этого параметра значение true.

Второй аргумент — исполнитель обратного вызова. Чтобы выполнить обратный вызов в том же потоке, который выполняет dexopt, используйте Runnable::run . Если вы не хотите, чтобы обратный вызов блокировал dexopt, используйте асинхронный исполнитель.

Вы можете добавить несколько обратных вызовов, и ART Service выполнит их все последовательно. Все обратные вызовы останутся активными для всех будущих вызовов, если вы их не удалите.

Если вы хотите удалить обратный вызов, сохраните ссылку на обратный вызов при его добавлении и используйте 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);

Настройте список пакетов и параметры dexopt

ART Service инициирует операции dexopt самостоятельно во время загрузки и фонового dexopt. Чтобы настроить список пакетов или параметры dexopt для этих операций, используйте 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.
      }
    });

Вы можете добавлять элементы в список пакетов, удалять элементы из него, сортировать его или даже использовать совершенно другой список.

Ваш обратный вызов должен игнорировать неизвестные причины, поскольку в будущем могут быть добавлены дополнительные причины.

Вы можете установить не более одного BatchDexoptStartCallback . Обратный вызов останется активным для всех будущих вызовов, если вы его не очистите.

Если вы хотите очистить обратный вызов, используйте ArtManagerLocal#clearBatchDexoptStartCallback .

getArtManagerLocal().clearBatchDexoptStartCallback();

Настройте параметры фонового задания dexopt

По умолчанию фоновое задание dexopt запускается один раз в день, когда устройство находится в режиме ожидания и заряжается. Это можно изменить с помощью ArtManagerLocal#setScheduleBackgroundDexoptJobCallback .

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

Вы можете установить не более одного ScheduleBackgroundDexoptJobCallback . Обратный вызов останется активным для всех будущих вызовов, если вы его не очистите.

Если вы хотите очистить обратный вызов, используйте ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Временно отключить dexopt

Любая операция dexopt, инициированная службой ART, запускает BatchDexoptStartCallback . Вы можете продолжать отменять операции, чтобы эффективно отключить dexopt.

Если отменяемая операция является фоновой dexopt, она следует политике повтора по умолчанию (30 секунд, экспоненциально, с ограничением в 5 часов).

// 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);

У вас может быть не более одного BatchDexoptStartCallback . Если вы также хотите использовать BatchDexoptStartCallback для настройки списка пакетов или параметров dexopt, вам необходимо объединить код в один обратный вызов.

// Bad example.

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

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

Операция dexopt, выполняемая при установке приложения, не инициируется службой ART. Вместо этого он инициируется менеджером пакетов посредством вызова dexoptPackage . Поэтому он не запускает BatchDexoptStartCallback . Чтобы отключить dexopt при установке приложения, запретите менеджеру пакетов вызывать dexoptPackage .

Переопределить фильтр компилятора для определенных пакетов (Android 15 (экспериментальная версия AOSP)+)

Вы можете переопределить фильтр компилятора для определенных пакетов, зарегистрировав обратный вызов через setAdjustCompilerFilterCallback . Обратный вызов вызывается всякий раз, когда пакет будет дексоптирован, независимо от того, инициируется ли дексопт службой ART во время загрузки и фоновой дексопт или вызовом API dexoptPackage .

Если пакет не нуждается в настройке, обратный вызов должен вернуть originalCompilerFilter .

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

Вы можете установить только один AdjustCompilerFilterCallback . Если вы хотите использовать AdjustCompilerFilterCallback для переопределения фильтра компилятора для нескольких пакетов, вам необходимо объединить код в один обратный вызов. Обратный вызов остается активным для всех будущих вызовов, если вы его не очистите.

Если вы хотите очистить обратный вызов, используйте ArtManagerLocal#clearAdjustCompilerFilterCallback .

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Другие настройки

ART Service также поддерживает некоторые другие настройки.

Установите температурный порог для фоновой dexopt

Температурный контроль фонового задания dexopt выполняется планировщиком заданий. Задание немедленно отменяется, когда температура достигает THERMAL_STATUS_MODERATE . Порог THERMAL_STATUS_MODERATE настраивается.

Определите, работает ли dexopt в фоновом режиме

Фоновое задание dexopt управляется планировщиком заданий, его идентификатор задания — 27873780 . Чтобы определить, выполняется ли задание, используйте API планировщика заданий.

// 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.
  ...
}

Предоставить профиль для dexopt

Чтобы использовать профиль для управления dexopt, поместите файл .prof или .dm рядом с APK.

Файл .prof должен быть файлом профиля в двоичном формате, а имя файла должно совпадать с именем APK + .prof . Например,

base.apk.prof

Имя файла .dm должно совпадать с именем файла APK с расширением, замененным на .dm . Например,

base.dm

Чтобы убедиться, что профиль используется для dexopt, запустите dexopt со speed-profile и проверьте результат.

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

Первая строка очищает все профили, созданные средой выполнения (т. е. те, которые находятся в /data/misc/profiles ), если таковые имеются, чтобы убедиться, что профиль рядом с APK является единственным профилем, который может использовать служба ART. Вторая строка запускает dexopt с speed-profile и передает -v для вывода подробного результата.

Если профиль используется, в результате вы увидите actualCompilerFilter=speed-profile . В противном случае вы увидите actualCompilerFilter=verify . Например,

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}

Типичные причины, по которым АРТ Сервис не использует профиль, включают следующее:

  • В профиле указано неправильное имя файла или его нет рядом с APK.
  • Профиль имеет неправильный формат.
  • Профиль не соответствует APK. (Контрольные суммы в профиле не совпадают с контрольными суммами файлов .dex в APK.)