2

My objective is to send a signal from kernel to a Android service running in the userspace.On receiving this signal, the service should make an IOCTL call back to the kernel. After it gets data from the kernel through this ioctl call,it has to display to the user. For this, i call a native method from my Java Service, which registers the sigaction structure, which includes the handler function for this signal. This handler function will make the IOCTL call and call a Java Function to pass the string to the Java service.

Here is the signal.java class

public class signal {
static{
    System.loadLibrary("signal");
}
public native String hello();

public String messageMe(String s)
{
    if(null != MainActivity.mUiHandler)
    {
        Message msgToActivity = new Message();
        msgToActivity.what = 0;

            msgToActivity.obj  = s; // you can put extra message here

        MainActivity.mUiHandler.sendMessage(msgToActivity);
    }

    System.out.println(s);
        return s;
}

}

I need to call this "messageMe" function from the signal handler. Here is the native hello() function which registers the sigaction structure.

JNIEXPORT jstring JNICALL Java_com_example_service_signal_hello
(JNIEnv * env, jobject obj) {

 int configfd;
char buf[10];
/* setup the signal handler for SIG_TEST
 * SA_SIGINFO -> we want the signal handler function with 3 arguments
 */
struct sigaction sig;
sig.sa_sigaction = receiveData;
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig,NULL);
}

The receiveData is the handler function. Here it goes.

void receiveData(int n, siginfo_t *info,void* unused) {
    char buf[200];
    char *msg = "hello world";
    int fd = -1;
    if ((fd = open("/dev/my_device", O_RDWR)) < 0) {
            perror("on");
            return;
    }
    if(ioctl(fd, READ_IOCTL, buf) < 0)
            perror("second ioctl");

     jstring jstr = (*env)->NewStringUTF(env, buf);
     jclass *clazz = (*env)->GetObjectClass(env, obj);
     jmethodID messageMe = (*env)->GetMethodID(env,clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
     jobject result = (*env)->CallObjectMethod(env,obj, messageMe, jstr);

    printf("message: %s\n", buf);

} This handler function should call the Java function "messageMe" and pass the string it read from the kernel as an argument. But it does not have Environment variables JNIEnv * and jobject obj. When I maintained a global pointers to these variables in the hello() function, and used them here, it was giving NoClassDefFoundError. How do I now send the string from this signal handler to the Java function?

user1505986
  • 257
  • 4
  • 10
  • While your question is **not a duplicate** of this, there's an example implementation referenced in the top answer to http://stackoverflow.com/questions/1083154/how-can-i-catch-sigsegv-segmentation-fault-and-get-a-stack-trace-under-jni-on – Chris Stratton May 18 '13 at 15:56
  • I tried caching the JavaVM pointer and obtained the JNIEnv variable in the handler. But the getMethodID function is giving a pending exception NoClassDefFoundError and the VM is aborting. When i do this inside the original native function, no exception is thrown. why is that? – user1505986 May 19 '13 at 12:06

2 Answers2

1

I have the following global variables. I cached these objects for future use.

JavaVM* vm;
static jclass cl;
static jobject ob;

In the hello function, i made global references for the above variables.

JNIEXPORT jstring JNICALL Java_com_example_service_signal_hello
(JNIEnv * env, jobject obj) {
(*env)->GetJavaVM(env,&vm);
ob = (*env)->NewGlobalRef(env,obj);
jclass clazz = (*env)->FindClass(env,"com/example/service/signal");
cl = (*env)->NewGlobalRef(env,clazz);
}

Now I used these in the handler function

void receiveData(int n, siginfo_t *info,void* unused) {
//other code
JNIEnv* env = NULL;
    if(vm==NULL)
        return;
    (*vm)->AttachCurrentThread(vm,&env,0);
 jmethodID messageMe = (*env)->GetStaticMethodID(env,cl, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
jobject result = (*env)->CallStaticObjectMethod(env,cl, messageMe, jstr);
}

One change I made was the use of static method instead of a normal one. So i didn't have to use a specific instance of the class. If the method is not static, the global variable "ob" can be used in the CallObjectMethod.

user1505986
  • 257
  • 4
  • 10
  • It's generally a good idea to do as little as possible inside a signal handler, since you don't know what state the current thread is in when the signal arrives. The Dalvik VM deals with this for the signals it wants to catch by blocking certain signals with `sigprocmask` and having a dedicated signal-catcher thread that sits in `sigwait`. (See Init.cpp and SignalCatcher.cpp in https://android.googlesource.com/platform/dalvik/+/master/vm/.) If you can target a specific thread with the signal you don't need to block it on all other threads. – fadden Jun 13 '13 at 16:53
0

As far as i know, the GetObjectClass method returns a jclass, not a jclass *.

In your receiveData function, you should replace

jclass *clazz = (*env)->GetObjectClass(env, obj);

by

jclass clazz = (*env)->GetObjectClass(env, obj);
mbrenon
  • 4,851
  • 23
  • 25
  • Yeah, I realised that later. But that didn't solve the problem. I finally figured out the problem. I'll post an answer. – user1505986 May 20 '13 at 10:15
  • I have same behaviour when I trying to call java non static method from C++. Did you find a solution? :-) – dimarik Jan 22 '23 at 21:38