I need to run a command line version of java application on Android(Yeah I know it's not trivial).
I'm trying to start it using Dalvikvm, it actually starts but somewhere later my code fails because it starts using android.util.log and throws this exception.
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.<init>(TunnelingClient.java:62)
at org.gihon.client.CLI.main(CLI.java:95)
at dalvik.system.NativeStart.main(Native Method)
I tried setting the environment variables, I set the LD_LIBRARY_PATH and the BOOTCLASSPATH variables. I even tried preloading liblog with LD_PRELOAD but nothing fixed that. It seems that something is wrong/different with the way dalvikvm sets the environment.
Good question! I had to dig a bit to figure this out.
There are a slew of JNI methods in libandroid_runtime.so that don't get bound by default, when you're using the dalvikvm command. Unfortunately, you can't just do a System.loadLibrary("android_runtime"), because this doesn't actually bind all the native methods.
However, after some digging, it turns out there is an internal, non-public, not guaranteed to be there class called com.android.internal.util.WithFramework, whose purpose is to load libandroid_runtime.so and bind all its JNI methods.
To use it, just throw com.android.internal.util.WithFramework
in front of your class name, on the dalvikvm command, like so:
dalvikvm -cp /some/path/classes.dex com.android.internal.util.WithFramework my.example.cls "This is an argument"
(Note: This only works on pre-M devices, due to the WithFramework class being removed in M - thanks for the heads up @JaredRummler)
For android M, I found this method to work.
Create a helloworld.sh script to accompany your jar\zip file:
#!/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 seems to start your java code with all java classes and shared libs loaded, so you can use both SDK classes like android.util.log.Log AND "secret" native classes, e.g. ActivityManagerNative, that are used in other adb shell commands, and are not present in the SDK.
BTW, for the java shell command I created, I had to resort to using reflection for the above classes, because it seems to me there is no way to compile properly without cloning and building the entire AOSP...
If someone knows an easier way to e.g. use ActivityManagerNative in java code without reflection, I'd appreciate the help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With