Wednesday, April 1, 2009

JNI hands on

In the demo setup at the seminar we hijacked the SMS application to send a LINX signal using JNI. In this post I'll describe more in detail how we implemented it.

First you need to create a Java-class that loads the C-library and declares the method (send(...))
public class NativeLib {
static {
try {
Log.i("JNI", "Trying to load libNativeLib.so");
System.loadLibrary("NativeLib");
}
catch (UnsatisfiedLinkError ule) {
Log.e("JNI", "WARNING: Could not load libNativeLib.so");
}
}
public static native long send(String number, String message);
}

Call the method somewhere appropriate from your activity:
long sum = NativeLib.send(...);

Next, you need to generate C-headers from the NativeLib Java-class:
cd <path to eclipse project dir>/bin
javah <class name>

Create a directory nativelib in the <android root>/external directory and move the header file to here.

Now, implement the C-file (and remember to include your recently generated header-file)
#include "com_android_mms_transaction_NativeSms.h"

JNIEXPORT jlong JNICALL Java_com_android_mms_transaction_NativeSms_send
(JNIEnv *env, jclass class, jstring number, jstring message)
{
//Get the native string from javaString
const char *numString;
const char *messageString;

numString = (*env)->GetStringUTFChars(env, number, 0);
messageString = (*env)->GetStringUTFChars(env, message, 0);

/*code to handle the sms info in native context goes here*/

/*release resources when done*/
(*env)->ReleaseStringUTFChars(env, number, numString); (*env)->ReleaseStringUTFChars(env, message, messageString);

return 0;
}

Next, we will construct a make-file: name it Android.mk and put it in <android root>/external/nativelib. Fill it up with the following:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := %lt;name of C-file%gt;

LOCAL_SHARED_LIBRARIES := libcutils

LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)

LOCAL_MODULE := libNativeLib

include $(BUILD_SHARED_LIBRARY)

Open file prelink-linux-arm.map in <android root>/build/core and add the row:

libNativeLib.so 0x9F400000

under comment:

# libraries for specific apps or temporary libraries

This will add your library to the prelink map. Make sure that the address is not used by anything else.
Now type make in Android root dir to build. Hopefully everything goes well and voila! You have implemented your native C-library

/Sebastian

2 comments:

  1. Has this worked for you? cause i implemented this solution exactly but the Java application fails to launch and the LogCat says that it failed to find the implementation for send.. Some others describe the same process with registering functions defined with androidjniregistermethods? have you omitted that part? or is it not necessary and i am making another mistake somewhere else?

    ReplyDelete
  2. Hi,
    Sorry for a late reply. Yes, it worked fine for us. As I mentioned in the post, we used it in a live demo at our seminar.
    Have you checked that 'libNativeLib.so' loads correctly at start? Ie. that the application can find the file? You might want to double-check that library-file exists in the filesystem somehwere accessible for the application.
    Let me know how it works out for you!

    ReplyDelete