Search code examples
candroid-studiocmakeandroid-ndkjava-native-interface

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


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 :)