En Android 8.0 y versiones posteriores, ART Tooling Interface (ART TI) expone ciertos aspectos internos del tiempo de ejecución y permite que los perfiladores y depuradores influyan en el comportamiento del tiempo de ejecución de las aplicaciones. Esto se puede utilizar para implementar herramientas de rendimiento de última generación que se proporcionan para implementar agentes nativos en otras plataformas.
Los elementos internos del tiempo de ejecución están expuestos a agentes que se han cargado en el proceso de tiempo de ejecución. Estos se comunican con la ART mediante llamadas directas y devoluciones de llamada. El tiempo de ejecución admite múltiples agentes para que se puedan separar diferentes preocupaciones de perfiles ortogonales. Los agentes pueden suministrarse al inicio del tiempo de ejecución (cuando se invocan 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 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 se pueda impedir que los agentes encuentren cualquiera de los puntos de interfaz.
- En segundo lugar, tanto la clase
ActivityManager
como el proceso 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.
Diseño
El flujo general y la interconexión en una aplicación instrumentada se muestran en la Figura 1 .
El complemento ART libopenjdkjvmti
expone ART TI, que está diseñado para adaptarse a las necesidades y limitaciones de la plataforma:
- La redefinición de clases se basa en archivos
Dex
, que contienen solo una única definición de clase, 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 perfiladores de Android Studio.
Cargar o adjuntar un agente
Para adjuntar un agente al 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 durante el inicio del tiempo de ejecución, así que tenga en cuenta que un tiempo de ejecución iniciado manualmente permite una 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, al conectar un agente se cargan tanto el complemento como la biblioteca del agente.
Un agente solo puede conectarse 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 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 conocer el estado depurable, y el tiempo de ejecución verifica su estado actual, que se configuró 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 propio ART es agnóstico respecto del lugar específico de donde procede el agente. La cadena se utiliza 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 adjuntar 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 adjunto-agente.
JVMTI
Esta característica expone la API JVMTI a los agentes (código nativo). Las capacidades importantes incluyen:
- Redefiniendo una clase.
- Seguimiento de la asignación de objetos y la recolección de basura.
- Iterando sobre todos los objetos en un montón, siguiendo el árbol de referencia de objetos.
- Inspeccionando pilas de llamadas de Java.
- Suspender (y reanudar) todos los hilos.
Es posible que haya diferentes capacidades disponibles en diferentes versiones de Android.
Compatibilidad
Esta característica necesita soporte de tiempo de ejecución central que solo está disponible en Android 8.0 y versiones posteriores. Los fabricantes de dispositivos no necesitan realizar ningún cambio para implementar esta función. 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
- Prueba 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.