I am trying something a little unconventional so I am not surprised that I am encountering difficulties.
I want to use an Ubuntu chroot
on an Android tablet as a UNIX CLI IDE for various kinds of software development. Since I am working on an Android device, I was curious if I could build Android code against the SDK, dx
(Dalvik eXchange compiler) the compiled bytecode, copy out to the sdcard, and execute with dalvikvm
.
A 'hello world' test (with no Android API) worked fine but the handful of Android API calls I've tried failed with java.lang.UnsatisfiedLinkError
. The calls into the API that I have tried seem to call down to (presumably JNI) native methods that Dalvik cannot find at runtime:
java.lang.UnsatisfiedLinkError: No implementation found for boolean android.os.SystemProperties.native_get_boolean(java.lang.String, boolean) (tried Java_android_os_SystemProperties_native_1get_1boolean and Java_android_os_SystemProperties_native_1get_1boolean__Ljava_lang_String_2Z)
at android.os.SystemProperties.native_get_boolean(Native Method)
at android.os.SystemProperties.getBoolean(SystemProperties.java:114)
at android.text.TextUtils.<clinit>(TextUtils.java:1919)
at android.media.AudioAttributes$Builder.build(AudioAttributes.java:337)
at android.media.AudioRecord.<init>(AudioRecord.java:233)
at AltTest.main(alttest.java:34)
For a concrete example, I tried the following code as, say, test.java
:
import android.media.AudioRecord;
class APITest {
public static void main(String[] args) {
System.out.println("Hello, world!");
// compiles, 'dx'ifies, and runs up this this point
int apiMinimumBufferSize = AudioRecord.getMinBufferSize(44100,16,2);
System.out.println("Your audio sample buffer must be at least " + apiMinimumBufferSize + " bytes.");
}
}
Compiled to APITest.class
with the following:
root@localhost:~/projects/test# CLASSPATH=/root/android-sdk-linux/platforms/android-21/android.jar javac test.java
dx
ified:
root@localhost:~/projects/test# /root/android-sdk-linux/build-tools/25.0.0/dx --dex --output=test.dex APITest.class
Note that we would not expect APITest.class to execute in the chroot
java environment due to the android.jar
dependency:
root@localhost:~/projects/test# java -cp ".:/root/android-sdk-linux/platforms/android-21/android.jar" APITest
Hello, world!
Exception in thread "main" java.lang.RuntimeException: Stub!
at android.media.AudioRecord.getMinBufferSize(AudioRecord.java:21)
at APITest.main(test.java:9)
But, if we place a copy of the dx
ified build where we can see it from the non-chroot
ed android environment:
root@localhost:~/projects/test# cp test.dex /mnt/android_root/sdcard
Then we can attempt to execute the code in dalvikvm
on a running Android machine:
[non-root-user]@[product_ID]:/ # su
root@[product_ID]:/ # cd sdcard
root@[product_ID]:/sdcard # dalvikvm -cp test.dex APITest
Hello, world!
java.lang.UnsatisfiedLinkError: No implementation found for int android.media.AudioRecord.native_get_min_buff_size(int, int, int) (tried Java_android_media_AudioRecord_native_1get_1min_1buff_1size and Java_android_media_AudioRecord_native_1get_1min_1buff_1size__III)
at android.media.AudioRecord.native_get_min_buff_size(Native Method)
at android.media.AudioRecord.getMinBufferSize(AudioRecord.java:592)
at APITest.main(test.java:9)
I have scanned all the lib*.so
files in the native android filesystem (e.g. '/system/lib' and '/vendor/lib') for dynamic symbols that look like the method names that dalvikvm
was looking for and other possible "DLL entry points" based on my vague knowledge of JNI. I don't know exactly what I am looking for. So far, I have only found a random scattering of potentially related methods.
I have tried running dalvikvm
under strace
(which I carefully liberated from my chroot
so I could run it against shell commands in the native Android environment). My read of the system-call traffic was that, other than reading many of the libs in /system/vendor
, dalvikvm
gave little hint regarding where it would have expected those native implementations to be.
One idea I had was to figure out how to make dalvikvm
tell me where it found a native implementation. Using Ruboto IRB
, one may call Android API methods and many of these call through to native implementations. If there were available introspection in Dalvik and I knew how to call on it, or if I knew how to attach debugging equipment to a running process that has been activated by the Android GUI, or if I knew how to invoke Android GUI apps from the CLI presented by the Terminal Emulator
app, I could imagine trying something like that.
I have also imagined disassembling a working app to study the interrelationships between the parts and attempt to back-out build or run-time requirements for practical Dalvik code in the Android ecosystem.
Most of my ideas are a bit "blue sky", heading off into an open research question from a position of ignorance. I'm sure I would learn a lot but it might take me a long time to find a way to move forward.
And, so, I'm hoping that someone with relevant experience might have the grace to tell me what they know about the mistakes I am making and how to fix them.
So, to get some Android code running from the terminal:
What am I missing from the ad-hoc build process detailed above?
Or, what am I missing from the execution context at run-time?
Thanks!