CLI en DalvikVM falla en JNI lib

Necesito ejecutar una versión de línea de comando de la aplicación Java en Android (sí, sé que no es trivial).

Intento iniciarlo usando Dalvikvm, en realidad comienza, pero en algún momento después mi código falla porque comienza a usar android.util.log y arroja esta excepción.

java.lang.UnsatisfiedLinkError: println_native at android.util.Log.println_native(Native Method) at android.util.Log.i(Log.java:159) at org.slf4j.impl.AndroidLogger.info(AndroidLogger.java:151) at org.gihon.client.TunnelingClient.(TunnelingClient.java:62) at org.gihon.client.CLI.main(CLI.java:95) at dalvik.system.NativeStart.main(Native Method) 

Intenté establecer las variables de entorno, establecí las variables LD_LIBRARY_PATH y BOOTCLASSPATH. Incluso intenté precargar liblog con LD_PRELOAD pero nada lo solucionó. Parece que algo está mal / diferente con la forma en que dalvikvm establece el entorno.

¡Buena pregunta! Tuve que cavar un poco para resolver esto.

Hay una gran cantidad de métodos JNI en libandroid_runtime.so que no se enlazan de forma predeterminada, cuando está usando el comando dalvikvm. Desafortunadamente, no se puede simplemente hacer una System.loadLibrary (“android_runtime”), porque esto no vincula realmente todos los métodos nativos.

Sin embargo, después de algunas excavaciones, resulta que hay una clase interna, no pública, no garantizada para estar allí llamada com.android.internal.util.WithFramework, cuya finalidad es cargar libandroid_runtime.so y enlazar todos sus métodos JNI.

Para usarlo, solo tira com.android.internal.util.WithFramework delante de tu nombre de clase, en el comando dalvikvm, así:

 dalvikvm -cp /some/path/classes.dex com.android.internal.util.WithFramework my.example.cls "This is an argument" 

(Nota: Esto solo funciona en dispositivos pre-M, debido a que la clase WithFramework se eliminó en M – gracias por el aviso previo @JaredRummler)

Para Android M, encontré que este método funciona.
Cree una secuencia de comandos helloworld.sh para acompañar su archivo jar \ zip:

 #!/system/bin/sh # Copied by example from am command base=/system export CLASSPATH=/path/to/your/jar/HelloWorld.jar exec app_process $base/bin HelloWorldMainClass "$@" 

app_process parece iniciar su código java con todas las clases java y bibliotecas compartidas cargadas, por lo que puede usar tanto clases SDK como android.util.log.Log Y clases nativas “secretas”, p. ej. ActivityManagerNative, que se usan en otros comandos de adb shell, y no están presentes en el SDK.

Por cierto, para el comando java shell que creé, tuve que recurrir al uso de la reflexión para las clases anteriores, porque me parece que no hay forma de comstackr correctamente sin clonar y construir todo el AOSP …
Si alguien conoce una forma más fácil de, por ejemplo, usar ActivityManagerNative en código Java sin reflexión, agradecería la ayuda.