[FIXED] JNI getting error message after exiting conditinal loop when the method returns

Issue

I am new with c and i am trying to create this project in android studio that reads linux input with /dev/input/event* directory, the other part that read’s so is an executable program (run with root access), that writes a fifo pipe (named) that can be read with this program and send input_event struct back to java.

C code:

boolean READ_EVENT_TASK_LOOP = false;
pthread_t eventThread, eventTest;

void Java_com_yacine_1app_bixby_1map_event_CustomEvent_releaseListener(JNIEnv *env, jobject obj){
    READ_EVENT_TASK_LOOP = false;

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

    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onError", "(Ljava/lang/String;)V");

    char msg[] = "No error were found!";

    (*env)->CallVoidMethod(env, obj, onEventId, (*env)->NewStringUTF(env, msg));

}

void * eventCheckingThread(JNIEnv *env, jobject obj, jstring shared_path);

void Java_com_yacine_1app_bixby_1map_event_CustomEvent_listenTo(JNIEnv *env, jobject obj, jstring shared_path) {

    int stat = pthread_create(&eventThread, NULL, eventCheckingThread(env, obj, shared_path), "Event Thread");

    if(stat) return;

    pthread_join(eventThread, NULL);

}

void *eventCheckingThread(JNIEnv *env, jobject obj, jstring shared_path){

    READ_EVENT_TASK_LOOP = true;

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

    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onEvent", "(Lcom/yacine_app/bixby_map/event/InputEventStruct;)V");

    jclass inputEventClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/InputEventStruct");
    jobject inputEventObject = (*env)->GetObjectClass(env, inputEventClass);
    jmethodID inputEventConstructor = (*env)->GetMethodID(env, inputEventObject, "<init>", "()V");
    jobject inputEventInstance = (*env)->NewObject(env, inputEventClass, inputEventConstructor);
    jfieldID inputEventInstanceTimeval = (*env)->GetFieldID(env, inputEventClass, "timeval", "Lcom/yacine_app/bixby_map/event/Timeval;");
    jfieldID inputEventInstanceType = (*env)->GetFieldID(env, inputEventClass, "type", "S");
    jfieldID inputEventInstanceCode = (*env)->GetFieldID(env, inputEventClass, "code", "S");
    jfieldID inputEventInstanceValue = (*env)->GetFieldID(env, inputEventClass, "value", "I");

    jclass timevalClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/Timeval");
    jobject timevalObject = (*env)->GetObjectClass(env, timevalClass);
    jmethodID timevalInstanceId = (*env)->GetMethodID(env, timevalObject, "<init>", "()V");
    jobject timevalInstance = (*env)->NewObject(env, timevalClass, timevalInstanceId);
    jfieldID timevalInstanceTv_sec = (*env)->GetFieldID(env, timevalClass, "tv_sec", "I");
    jfieldID timevalInstanceTv_usec = (*env)->GetFieldID(env, timevalClass, "tv_usec", "I");

    const char *path = (*env)->GetStringUTFChars(env, shared_path, null);

    struct input_event in;
    struct pollfd fds[1];

    char *full_exist_hash = (char*) malloc(500 * sizeof(char));

    sprintf(full_exist_hash, "%s/%s", path, INPUT_CUSTOM_EVENT_END_HASH);

    mkfifo(full_exist_hash, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH | S_IWOTH);
    int end_hash_fd = open(full_exist_hash, O_NONBLOCK | O_WRONLY);

    if(end_hash_fd < 0){
        printf("Error with reading %s\n", full_exist_hash);
        _exit(-1);
    }

    char *full_path = (char*) malloc(500 * sizeof(char));

    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);

    mkfifo(full_path, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);

    fds[0].fd = open(full_path, O_RDONLY);

    if(fds[0].fd < 0){
        printf("Error with reading %s\n", full_path);
        _exit(-1);
    }

    fds[0].events = POLLIN;

    while (READ_EVENT_TASK_LOOP) {
        poll(fds, 1, 5000);
        if(fds[0].revents){
            read(fds[0].fd, &in, sizeof(in));
            if(in.code | in.type | in.value > 0) {
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_sec, in.time.tv_sec);
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_usec, in.time.tv_usec);
                (*env)->SetObjectField(env, inputEventInstance, inputEventInstanceTimeval, timevalInstance);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceType, in.type);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceCode, in.code);
                (*env)->SetIntField(env, inputEventInstance, inputEventInstanceValue, in.value);
                (*env)->CallVoidMethod(env, obj, onEventId, inputEventInstance);
            }
                //printf("time: %li, code: %hi, type: %hi, value: %d\n\r", in.time.tv_sec, in.code, in.type, in.value);
        }
    }

    write(end_hash_fd, INPUT_CUSTOM_EVENT_END_HASH, strlen(INPUT_CUSTOM_EVENT_END_HASH));
    close(end_hash_fd);
    close(fds[0].fd);

}

Java code:

import android.util.Log;

public class CustomEvent {

    public static CustomEvent newInstance(){
        return new CustomEvent();
    }

    private String path;
    private OnEventListener onEventListener;
    private OnReleasedListener onReleasedListener;
    private OnStartedListener onStartedListener;

    public void setPath(String path) {
        this.path = path;
    }

    public void setOnEventListener(OnEventListener onEventListener) { this.onEventListener = onEventListener; }

    public void setOnReleasedListener(OnReleasedListener onReleasedListener) { this.onReleasedListener = onReleasedListener; }

    public void setOnStartedListener(OnStartedListener onStartedListener) { this.onStartedListener = onStartedListener; }

    private CustomEvent(){ }

    static {
        System.loadLibrary("LinuxEventReader");
    }

    private void onEvent(InputEventStruct inputEvent){
        if(this.onEventListener != null) this.onEventListener.onEvent(inputEvent);
    }

    private void onError(String err){
        Log.e("ERROR", err);
    }

    private native void listenTo(String path);
    private native void releaseListener();

    public void startListening(){
        new Thread(()->{
            if(this.onStartedListener != null) this.onStartedListener.onStarted();
            listenTo(path);
        }).start();
    }

    public void release(){
        new Thread(()->{
            releaseListener();
            if(this.onReleasedListener != null) this.onReleasedListener.onReleased();
        }).start();
    }

}

The problem is, while this program is executing it works fine but when i try to exit the loop it crashes before it returns to java code

A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x74e8995000 in tid 14435 (Thread-7), pid 13933 (e_app.bixby_map)

that’s the error i get. any help?

Solution

I had like to thank you both @Michael and @Alex Cohn for their helpful comment.

After what @Michael said, i re-read the JNI Documentation from oracle and JavaVM and JNIEnv and Threads i noticed my mistake that i didn’t read them carefully before, after so I used AttachCurrentThread to achieve what i want as described, but then again after what @Alex Cohn said, i decided to make the threading made inside the java code, it’s much easier to handle and debug, and here is the final code:

C code:

static boolean READ_EVENT_TASK_LOOP = false;
static char * path = NULL;
static struct pollfd fds[1];

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_releaseListener(__unused JNIEnv *env, __unused jobject obj){

    READ_EVENT_TASK_LOOP = false;
    char *full_path = (char*) malloc(500 * sizeof(char));
    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);
    remove(full_path);
    close(fds[0].fd);
    path = NULL;
    //free(fds);
    //_exit(0);
    return NULL;
}

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_listenTo(JNIEnv *env, jobject obj) {

    if(!READ_EVENT_TASK_LOOP) return NULL;

    struct input_event in;
    char *full_path = (char*) malloc(500 * sizeof(char));

    sprintf(full_path, "%s/%s", path, INPUT_CUSTOM_EVENT_FIFIO_FILE);
    mkfifo(full_path, O_CREAT | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);

    jclass ceClass = (*env)->GetObjectClass(env, obj);
    jmethodID onEventId = (*env)->GetMethodID(env, ceClass, "onEvent", "(Lcom/yacine_app/bixby_map/event/InputEventStruct;)V");

    jclass inputEventClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/InputEventStruct");
    jobject inputEventObject = (*env)->GetObjectClass(env, inputEventClass);
    jmethodID inputEventConstructor = (*env)->GetMethodID(env, inputEventObject, "<init>", "()V");
    jobject inputEventInstance = (*env)->NewObject(env, inputEventClass, inputEventConstructor);
    jfieldID inputEventInstanceTimeval = (*env)->GetFieldID(env, inputEventClass, "timeval", "Lcom/yacine_app/bixby_map/event/Timeval;");
    jfieldID inputEventInstanceType = (*env)->GetFieldID(env, inputEventClass, "type", "S");
    jfieldID inputEventInstanceCode = (*env)->GetFieldID(env, inputEventClass, "code", "S");
    jfieldID inputEventInstanceValue = (*env)->GetFieldID(env, inputEventClass, "value", "I");

    jclass timevalClass = (*env)->FindClass(env, "com/yacine_app/bixby_map/event/Timeval");
    jobject timevalObject = (*env)->GetObjectClass(env, timevalClass);
    jmethodID timevalInstanceId = (*env)->GetMethodID(env, timevalObject, "<init>", "()V");
    jobject timevalInstance = (*env)->NewObject(env, timevalClass, timevalInstanceId);
    jfieldID timevalInstanceTv_sec = (*env)->GetFieldID(env, timevalClass, "tv_sec", "I");
    jfieldID timevalInstanceTv_usec = (*env)->GetFieldID(env, timevalClass, "tv_usec", "I");

    fds[0].fd = open(full_path, O_RDONLY);
    if(fds[0].fd < 0){
        printf("Error with reading %s\n", full_path);
        return NULL;
    }

    fds[0].events = POLLIN;

    //pthread_cleanup_push(cleanup, end_hash_fd)

    while (READ_EVENT_TASK_LOOP) {
        poll(fds, 1, 500);
        if(fds[0].revents){
            read(fds[0].fd, &in, sizeof(in));
            if(in.code | in.type | in.value > 0) {
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_sec, in.time.tv_sec);
                (*env)->SetIntField(env, timevalInstance, timevalInstanceTv_usec, in.time.tv_usec);
                (*env)->SetObjectField(env, inputEventInstance, inputEventInstanceTimeval, timevalInstance);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceType, in.type);
                (*env)->SetShortField(env, inputEventInstance, inputEventInstanceCode, in.code);
                (*env)->SetIntField(env, inputEventInstance, inputEventInstanceValue, in.value);
                (*env)->CallVoidMethod(env, obj, onEventId, inputEventInstance);
            }
            //printf("time: %li, code: %hi, type: %hi, value: %d\n\r", in.time.tv_sec, in.code, in.type, in.value);
        }
    }

    //close(fds[0].fd);
    return NULL;
}

void *Java_com_yacine_1app_bixby_1map_event_CustomEvent_initialize(JNIEnv *env, __unused jobject obj, jstring shared_path) {
    READ_EVENT_TASK_LOOP = true;
    path = (char*) (*env)->GetStringUTFChars(env, shared_path, NULL);
    return NULL;
}

Java code:

public class CustomEvent {

    public static CustomEvent newInstance(){ return new CustomEvent(); }

    private boolean initialized = false;
    private String path;
    private OnEventListener onEventListener;
    private OnReleasedListener onReleasedListener;
    private OnStartedListener onStartedListener;
    private RootManager rm;
    private Thread startThread, stopThread;
    private final Runnable start = ()->{
        try {
            this.rm.exec("./PsB -d 6 -o " + this.path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(this.onStartedListener != null) this.onStartedListener.onStarted();
        this.listenTo();
    }, stop = ()->{
        this.releaseListener();
        this.startThread = null;
        this.stopThread = null;
        try {
            this.rm.exit();
            this.rm = null;
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        this.initialized = false;
        if(this.onReleasedListener != null) this.onReleasedListener.onReleased();
    };

    static {
        System.loadLibrary("LinuxEventReader");
    }

    private CustomEvent(){ }

    public void init(String path){
        this.path = path;
        this.initialize(this.path);
        this.rm = new RootManager();
        if(this.rm.isRootAccess())return;
        try {
            this.rm.setDir(this.path + "/files/exec");
            this.rm.requestRoot();
            this.rm.exec("chmod +x PsB");
            this.startThread = new Thread(this.start);
            this.stopThread = new Thread(this.stop);
            this.initialized = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startListening(){
        if(!this.initialized) throw new IllegalStateException();
        this.startThread.start();
    }

    public void release(){
        if(!this.initialized) throw new IllegalStateException();
        this.stopThread.start();
    }

    public void setOnEventListener(OnEventListener onEventListener) { this.onEventListener = onEventListener; }
    public void setOnReleasedListener(OnReleasedListener onReleasedListener) { this.onReleasedListener = onReleasedListener; }
    public void setOnStartedListener(OnStartedListener onStartedListener) { this.onStartedListener = onStartedListener; }

    @SuppressWarnings("unused")
    private synchronized void onEvent(InputEventStruct inputEvent){
        if(this.onEventListener != null) this.onEventListener.onEvent(inputEvent);
    }
    @SuppressWarnings("unused")
    private void onError(String err){
        Log.e("ERROR", err);
    }

    private native void listenTo();
    private native void releaseListener();
    private native void initialize(String path);

}

If anything i made is wrong or not accurate i will appreciate more help 🙂

Answered By – yacine-app

Answer Checked By – David Marino (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published