Configuración del servicio ART

Antes de comenzar, consulte una descripción general de alto nivel del Servicio ART .

A partir de Android 14, ART Service se encarga de la compilación AOT en el dispositivo para aplicaciones (también conocida como dexopt). El servicio ART es parte del módulo ART y puede personalizarlo a través de las propiedades del sistema y las API.

Propiedades del sistema

ART Service admite todas las opciones relevantes de dex2oat .

Además, ART Service admite las siguientes propiedades del sistema:

pm.dexopt.<motivo>

Este es un conjunto de propiedades del sistema que determinan los filtros predeterminados del compilador para todos los motivos de compilación predefinidos descritos en escenarios de Dexopt .

Para obtener más información, consulte Filtros del compilador .

Los valores predeterminados estándar son:

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 (predeterminado: velocidad)

Este es el filtro del compilador alternativo para aplicaciones utilizadas por otras aplicaciones.

En principio, ART Service realiza una compilación guiada por perfiles ( speed-profile ) para todas las aplicaciones cuando es posible, normalmente durante el dexopt en segundo plano. Sin embargo, hay algunas aplicaciones que son utilizadas por otras aplicaciones (ya sea a través de <uses-library> o cargadas dinámicamente usando Context#createPackageContext con CONTEXT_INCLUDE_CODE ). Estas aplicaciones no pueden utilizar perfiles locales por motivos de privacidad.

Para una aplicación de este tipo, si se solicita una compilación guiada por perfiles, ART Service primero intenta utilizar un perfil en la nube. Si no existe un perfil de nube, ART Service recurre para utilizar el filtro del compilador especificado por pm.dexopt.shared .

Si la compilación solicitada no está guiada por perfiles, esta propiedad no tiene ningún efecto.

pm.dexopt.<motivo>.concurrencia (predeterminado: 1)

Este es el número de invocaciones dex2oat por ciertos motivos de compilación predefinidos ( first-boot , boot-after-ota , boot-after-mainline-update y bg-dexopt ).

Tenga en cuenta que el efecto de esta opción se combina con las opciones de uso de recursos dex2oat ( dalvik.vm.*dex2oat-threads , dalvik.vm.*dex2oat-cpu-set y los perfiles de tarea):

  • dalvik.vm.*dex2oat-threads controla el número de subprocesos para cada invocación de dex2oat, mientras que pm.dexopt.<reason>.concurrency controla el número de invocaciones de dex2oat. Es decir, el número máximo de subprocesos simultáneos es el producto de las dos propiedades del sistema.
  • dalvik.vm.*dex2oat-cpu-set y los perfiles de tareas siempre limitan el uso del núcleo de la CPU, independientemente del número máximo de subprocesos simultáneos (discutido anteriormente).

Es posible que una única invocación dex2oat no utilice completamente todos los núcleos de la CPU, independientemente de dalvik.vm.*dex2oat-threads . Por lo tanto, aumentar el número de invocaciones de dex2oat ( pm.dexopt.<reason>.concurrency ) puede utilizar mejor los núcleos de CPU para acelerar el progreso general de dexopt. Esto es particularmente útil durante el arranque.

Sin embargo, tener demasiadas invocaciones dex2oat puede hacer que el dispositivo se quede sin memoria, aunque esto se puede mitigar configurando dalvik.vm.dex2oat-swap en true para permitir el uso de un archivo de intercambio. Demasiadas invocaciones también pueden provocar cambios de contexto innecesarios. Por lo tanto, esta cifra debe ajustarse cuidadosamente producto por producto.

pm.dexopt.downgrade_after_inactive_days (predeterminado: no configurado)

Si se configura esta opción, ART Service solo dexopta las aplicaciones utilizadas dentro del último número de días determinado.

Además, si el almacenamiento es casi bajo, durante la dexopt en segundo plano, ART Service degrada el filtro del compilador de las aplicaciones que no se utilizan en el último número de días determinado, para liberar espacio. El motivo del compilador para esto es inactive y el filtro del compilador está determinado por pm.dexopt.inactive . El umbral de espacio para activar esta función es el umbral de espacio bajo del Administrador de almacenamiento (configurable a través de las configuraciones globales sys_storage_threshold_percentage y sys_storage_threshold_max_bytes , predeterminado: 500 MB) más 500 MB.

Si personaliza la lista de paquetes a través de ArtManagerLocal#setBatchDexoptStartCallback , los paquetes en la lista proporcionada por BatchDexoptStartCallback para bg-dexopt nunca se degradan.

pm.dexopt.disable_bg_dexopt (predeterminado: falso)

Esto es sólo para pruebas. Impide que ART Service programe el trabajo dexopt en segundo plano.

Si el trabajo dexopt en segundo plano ya está programado pero aún no se ha ejecutado, esta opción no tiene ningún efecto. Es decir, el trabajo seguirá ejecutándose.

Una secuencia recomendada de comandos para evitar que se ejecute el trabajo dexopt en segundo plano es:

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

La primera línea evita que se programe el trabajo dexopt en segundo plano, si aún no lo está. La segunda línea desprograma el trabajo dexopt en segundo plano, si ya está programado, y cancela el trabajo dexopt en segundo plano inmediatamente, si se está ejecutando.

API de servicio ART

ART Service expone las API de Java para su personalización. Las API están definidas en ArtManagerLocal . Consulte el Javadoc en art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java para conocer los usos ( fuente de Android 14 , fuente de desarrollo inédita ).

ArtManagerLocal es un singleton mantenido por LocalManagerRegistry . Una función auxiliar com.android.server.pm.DexOptHelper#getArtManagerLocal le ayuda a obtenerlo.

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

La mayoría de las API requieren una instancia de PackageManagerLocal.FilteredSnapshot , que contiene la información de todas las aplicaciones. Puede obtenerlo llamando PackageManagerLocal#withFilteredSnapshot , donde PackageManagerLocal también es un singleton mantenido por LocalManagerRegistry y se puede obtener en com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal .

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

Los siguientes son algunos casos de uso típicos de las API.

Activar dexopt para una aplicación

Puede activar dexopt para cualquier aplicación en cualquier momento llamando a ArtManagerLocal#dexoptPackage .

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

También puede pasar su propio motivo dexopt. Si hace eso, la clase de prioridad y el filtro del compilador deben establecerse explícitamente.

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

Cancelar dexopt

Si una operación se inicia mediante una llamada dexoptPackage , puede pasar una señal de cancelación, que le permite cancelar la operación en algún momento. Esto puede resultar útil cuando ejecuta dexopt de forma asincrónica.

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

También puede cancelar el dexopt en segundo plano, que inicia el Servicio ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Obtenga resultados dexopt

Si una operación se inicia mediante una llamada dexoptPackage , puede obtener el resultado del valor de retorno.

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

// Process the result here.
...

ART Service también inicia operaciones dexopt por sí mismo en muchos escenarios, como dexopt en segundo plano. Para escuchar todos los resultados de dexopt, ya sea que la operación se inicie mediante una llamada dexoptPackage o mediante el servicio ART, use ArtManagerLocal#addDexoptDoneCallback .

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

El primer argumento determina si solo se incluyen actualizaciones en el resultado. Si solo desea escuchar los paquetes actualizados por dexopt, configúrelo en verdadero.

El segundo argumento es el ejecutor de la devolución de llamada. Para ejecutar la devolución de llamada en el mismo hilo que realiza dexopt, use Runnable::run . Si no desea que la devolución de llamada bloquee dexopt, utilice un ejecutor asincrónico.

Puede agregar varias devoluciones de llamada y ART Service las ejecutará todas de forma secuencial. Todas las devoluciones de llamada permanecerán activas para todas las llamadas futuras a menos que las elimine.

Si desea eliminar una devolución de llamada, mantenga la referencia de la devolución de llamada cuando la agregue y use 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);

Personalice la lista de paquetes y los parámetros dexopt

El servicio ART inicia las operaciones dexopt durante el arranque y el dexopt en segundo plano. Para personalizar la lista de paquetes o los parámetros dexopt para esas operaciones, use 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.
      }
    });

Puede agregar elementos a la lista de paquetes, eliminar elementos, ordenarlos o incluso usar una lista completamente diferente.

Su devolución de llamada debe ignorar motivos desconocidos porque es posible que se agreguen más motivos en el futuro.

Puede configurar como máximo un BatchDexoptStartCallback . La devolución de llamada permanecerá activa para todas las llamadas futuras a menos que la borre.

Si desea borrar la devolución de llamada, use ArtManagerLocal#clearBatchDexoptStartCallback .

getArtManagerLocal().clearBatchDexoptStartCallback();

Personaliza los parámetros del trabajo dexopt en segundo plano.

De forma predeterminada, el trabajo dexopt en segundo plano se ejecuta una vez al día cuando el dispositivo está inactivo y cargándose. Esto se puede cambiar usando ArtManagerLocal#setScheduleBackgroundDexoptJobCallback .

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

Puede configurar como máximo un ScheduleBackgroundDexoptJobCallback . La devolución de llamada permanecerá activa para todas las llamadas futuras a menos que la borre.

Si desea borrar la devolución de llamada, use ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Desactivar temporalmente dexopt

Cualquier operación dexopt iniciada por el servicio ART desencadena un BatchDexoptStartCallback . Puede seguir cancelando las operaciones para desactivar efectivamente dexopt.

Si la operación que cancela es dexopt en segundo plano, sigue la política de reintento predeterminada (30 segundos, exponencial, con un límite de 5 horas).

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

Puede tener como máximo un BatchDexoptStartCallback . Si también desea utilizar BatchDexoptStartCallback para personalizar la lista de paquetes o los parámetros dexopt, debe combinar el código en una sola devolución de llamada.

// Bad example.

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

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

La operación dexopt realizada durante la instalación de la aplicación no la inicia el servicio ART. En cambio, lo inicia el administrador de paquetes a través de una llamada dexoptPackage . Por lo tanto, no activa BatchDexoptStartCallback . Para deshabilitar dexopt en la instalación de la aplicación, evite que el administrador de paquetes llame dexoptPackage .

Anule el filtro del compilador para ciertos paquetes (Android 15 (AOSP experimental)+)

Puede anular el filtro del compilador para ciertos paquetes registrando una devolución de llamada a través de setAdjustCompilerFilterCallback . La devolución de llamada se llama cada vez que se va a dexoptar un paquete, sin importar que el servicio ART inicie el dexopt durante el arranque y el dexopt en segundo plano o mediante una llamada a la API dexoptPackage .

Si un paquete no necesita ajustes, la devolución de llamada debe devolver originalCompilerFilter .

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

Solo puede configurar un AdjustCompilerFilterCallback . Si desea utilizar AdjustCompilerFilterCallback para anular el filtro del compilador para varios paquetes, debe combinar el código en una sola devolución de llamada. La devolución de llamada permanece activa para todas las llamadas futuras a menos que la borre.

Si desea borrar la devolución de llamada, use ArtManagerLocal#clearAdjustCompilerFilterCallback .

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Otras personalizaciones

ART Service también admite algunas otras personalizaciones.

Establecer el umbral térmico para dexopt en segundo plano

El control térmico del trabajo dexopt en segundo plano lo realiza Job Scheduler. El trabajo se cancela inmediatamente cuando la temperatura alcanza THERMAL_STATUS_MODERATE . El umbral de THERMAL_STATUS_MODERATE se puede ajustar.

Determinar si se está ejecutando dexopt en segundo plano

El trabajo dexopt en segundo plano lo administra Job Scheduler y su ID de trabajo es 27873780 . Para determinar si el trabajo se está ejecutando, utilice las API del Programador de trabajos.

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

Proporcionar un perfil para dexopt

Para utilizar un perfil para guiar dexopt, coloque un archivo .prof o un archivo .dm al lado del APK.

El archivo .prof debe ser un archivo de perfil en formato binario y el nombre del archivo debe ser el nombre del archivo APK + .prof . Por ejemplo,

base.apk.prof

El nombre del archivo .dm debe ser el nombre del archivo APK con la extensión reemplazada por .dm . Por ejemplo,

base.dm

Para verificar que el perfil se esté utilizando para dexopt, ejecute dexopt con speed-profile y verifique el resultado.

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

La primera línea borra todos los perfiles producidos por el tiempo de ejecución (es decir, aquellos en /data/misc/profiles ), si los hay, para asegurarse de que el perfil al lado del APK sea el único perfil que ART Service pueda usar. La segunda línea ejecuta dexopt con speed-profile y pasa -v para imprimir el resultado detallado.

Si se está utilizando el perfil, verá actualCompilerFilter=speed-profile en el resultado. De lo contrario, verá actualCompilerFilter=verify . Por ejemplo,

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}

Las razones típicas por las que ART Service no utiliza el perfil incluyen las siguientes:

  • El perfil tiene un nombre de archivo incorrecto o no está al lado del APK.
  • El perfil tiene el formato incorrecto.
  • El perfil no coincide con el APK. (Las sumas de verificación del perfil no coinciden con las sumas de verificación de los archivos .dex en el APK).