ARTE TI

En Android 8.0 y versiones posteriores, la interfaz de herramientas ART (ART TI) expone ciertas funciones internas del tiempo de ejecución y permite que los generadores de perfiles y los depuradores influyan en el comportamiento del tiempo de ejecución de las aplicaciones. Esto se puede usar para implementar herramientas de rendimiento de última generación que se proporcionan para implementar agentes nativos en otras plataformas.

Los componentes internos del tiempo de ejecución están expuestos a los agentes que se han cargado en el proceso del tiempo de ejecución. Estos se comunican con la ART a través de llamadas directas y callbacks. El tiempo de ejecución admite múltiples agentes para que se puedan separar diferentes preocupaciones de perfilado ortogonal. Los agentes pueden suministrarse al inicio del tiempo de ejecución (cuando se dalvikvm o app_process ) o adjuntarse a un proceso que ya se está ejecutando.

Debido a que la capacidad de instrumentar y modificar el comportamiento de la aplicación y el tiempo de ejecución es muy poderosa, se han integrado dos medidas de seguridad en el ART TI:

  • Primero, el código que expone la interfaz del agente, JVMTI, se implementa como un complemento de tiempo de ejecución, no como un componente central del tiempo de ejecución. La carga de complementos puede estar restringida, de modo que los agentes no puedan encontrar ninguno de los puntos de interfaz.
  • En segundo lugar, tanto la clase ActivityManager como el proceso de tiempo de ejecución solo permiten que los agentes se conecten a aplicaciones depurables. Las aplicaciones depurables han sido aprobadas por sus desarrolladores para ser analizadas e instrumentadas, y no se distribuyen a los usuarios finales. La tienda Google Play no permite la distribución de aplicaciones depurables. Esto garantiza que las aplicaciones normales (incluidos los componentes principales) no puedan instrumentarse ni manipularse.

El diseño

En la Figura 1 se muestra el flujo general y la interconexión en una aplicación instrumentada.

Flow and interconnection in an instrumented app
Figura 1. Flujo e interconexión de una app instrumentada

El complemento ART libopenjdkjvmti expone el ART TI, que está diseñado para adaptarse a las necesidades y limitaciones de la plataforma:

  • La redefinición de clase se basa en archivos Dex , que contienen solo una definición de clase única, en lugar de archivos de clase.
  • Las API en lenguaje Java para instrumentación y redefinición no están expuestas.

ART TI también es compatible con los generadores de perfiles de Android Studio.

Cargar o adjuntar un agente

Para adjuntar un agente en el inicio del tiempo de ejecución, use este comando para cargar tanto el complemento JVMTI como el agente dado:

dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …

No existen medidas de seguridad cuando se carga un agente en el inicio del tiempo de ejecución, así que tenga en cuenta que un tiempo de ejecución iniciado manualmente permite la modificación completa sin medidas de seguridad. (Esto permite la prueba ART).

Nota: Esto no se aplica a las aplicaciones normales (incluido el servidor del sistema) en un dispositivo. Las aplicaciones se bifurcan a partir de un cigoto que ya se está ejecutando y un proceso de cigoto no puede cargar agentes.

Para adjuntar un agente a una aplicación que ya se está ejecutando, use este comando:

adb shell cmd activity attach-agent [process]
/path/to/agent/libagent.so[=agent-options]

Si el complemento JVMTI aún no se ha cargado, adjuntar un agente carga tanto el complemento como la biblioteca del agente.

Un agente solo se puede adjuntar a una aplicación en ejecución que esté marcada como depurable (parte del manifiesto de la aplicación, con el atributo android:debuggable establecido en true en el nodo de la aplicación). Tanto la clase ActivityManager como la ART realizan comprobaciones antes de permitir que se adjunte un agente. La clase ActivityManager verifica la información de la aplicación actual (derivada de los datos de la clase PackageManager ) para el estado depurable, y el tiempo de ejecución verifica su estado actual, que se estableció cuando se inició la aplicación.

Ubicaciones de agentes

El tiempo de ejecución necesita cargar agentes en el proceso actual, de modo que el agente pueda vincularse directamente y comunicarse con él. El ART en sí mismo es agnóstico con respecto a la ubicación específica de donde proviene el agente. La cadena se usa para una llamada dlopen . Los permisos del sistema de archivos y las políticas de SELinux restringen la carga real.

Para entregar agentes que puedan ejecutarse mediante una aplicación depurable, haga lo siguiente:

  • Incruste el agente en el directorio de la biblioteca del APK de la aplicación.
  • Utilice run-as para copiar el agente en el directorio de datos de la aplicación.

API

El siguiente método se agregó a android.os.Debug .

/**
     * Attach a library as a jvmti agent to the current runtime, with the given classloader
     * determining the library search path.
     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
     * throw a SecurityException.
     *
     * @param library the library containing the agent.
     * @param options the options passed to the agent.
     * @param classLoader the classloader determining the library search path.
     *
     * @throws IOException if the agent could not be attached.
     * @throws a SecurityException if the app is not debuggable.
     */
    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
            @Nullable ClassLoader classLoader) throws IOException {

Otras API de Android

El comando de adjunto-agente es visible públicamente. Este comando adjunta un agente JVMTI a un proceso en ejecución:

adb shell 'am attach-agent com.example.android.displayingbitmaps
\'/data/data/com.example.android.displayingbitmaps/code_cache/libfieldnulls.so=Ljava/lang/Class;.name:Ljava/lang/String;\''

Los comandos am start -P y am start-profiler/stop-profiler son similares al comando added-agent.

JVMTI

Esta función expone la API de JVMTI a los agentes (código nativo). Las capacidades importantes incluyen:

  • Redefiniendo una clase.
  • Seguimiento de asignación de objetos y recolección de basura.
  • Iterando sobre todos los objetos en un montón, siguiendo el árbol de referencia de objetos.
  • Inspección de pilas de llamadas de Java.
  • Suspender (y reanudar) todos los hilos.

Diferentes capacidades pueden estar disponibles en diferentes versiones de Android.

Compatibilidad

Esta función necesita compatibilidad con el tiempo de ejecución principal que solo está disponible en Android 8.0 y versiones posteriores. Los fabricantes de dispositivos no necesitan hacer ningún cambio para implementar esta característica. Es parte de AOSP.

Validación

CTS prueba lo siguiente en Android 8 y superior:

  • Prueba que los agentes se conectan a aplicaciones depurables y no se conectan a aplicaciones no depurables.
  • Prueba todas las API JVMTI implementadas
  • Comprueba que la interfaz binaria para agentes es estable

Se agregaron pruebas adicionales a Android 9 y versiones posteriores, y se incluyen en las pruebas CTS para esas versiones.