From 496b3a43003c4636597eb8204884f091910ba60c Mon Sep 17 00:00:00 2001 From: Xtr126 Date: Mon, 17 Oct 2022 12:44:47 +0530 Subject: [PATCH] Implement multi-threading in server (mouse_read.c) and move getevent from app to server-side to reduce su calls --- app/src/main/cpp/mouse_read.c | 173 ++++++++++++------ .../main/java/com/xtr/keymapper/Input.java | 1 + .../java/com/xtr/keymapper/TouchPointer.java | 55 ++++-- .../xtr/keymapper/activity/MainActivity.java | 1 - app/src/main/res/values/strings.xml | 2 +- 5 files changed, 155 insertions(+), 77 deletions(-) diff --git a/app/src/main/cpp/mouse_read.c b/app/src/main/cpp/mouse_read.c index 1429a1a2..f9b02d7f 100644 --- a/app/src/main/cpp/mouse_read.c +++ b/app/src/main/cpp/mouse_read.c @@ -17,23 +17,30 @@ #include #include +#include +#include + char str[16]; +typedef struct socket_context { + JavaVM *javaVM; + int client_fd; + int server_fd; + struct sockaddr_in address; + int addrlen; +} socketContext; +socketContext s_ctx; + typedef struct mouse_context { - JavaVM *javaVM; + JavaVM *javaVM; pthread_mutex_t lock; - int done; - const char* dev; - int port; + int done; } mouseContext; mouseContext g_ctx; -struct Socket { - int client_fd; - int server_fd; -} s_cts; -int fd; +int fd; int port; +const char* dev; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; @@ -49,41 +56,35 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { } -void create_socket(struct Socket *Socket, int PORT){ - struct sockaddr_in address; +void create_socket(void* context){ + socketContext *sock = (socketContext*) context; + int PORT = port; int opt = 1; - int addrlen = sizeof(address); + sock->addrlen = sizeof(sock->address); // Creating socket file descriptor - if ((Socket->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if ((sock->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("mouse_read: Socket creation error \n"); } // Forcefully attaching socket to the port - if (setsockopt(Socket->server_fd, SOL_SOCKET, + if (setsockopt(sock->server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { printf("mouse_read: setsockopt error \n"); } - address.sin_family = AF_INET; - address.sin_port = htons(PORT); - address.sin_addr.s_addr = INADDR_ANY; + sock->address.sin_family = AF_INET; + sock->address.sin_port = htons(PORT); + sock->address.sin_addr.s_addr = INADDR_ANY; - if (bind(Socket->server_fd, (struct sockaddr*)&address, - sizeof(address)) < 0) { + if (bind(sock->server_fd, (struct sockaddr*)&(sock->address), + sizeof(sock->address)) < 0) { printf("mouse_read: socket bind error \n"); } - printf("Waiting for overlay...\n"); - if (listen(Socket->server_fd, 3) < 0) { + if (listen(sock->server_fd, 2) < 0) { printf("mouse_read: socket listen failed \n"); } - - if ((Socket->client_fd - = accept(Socket->server_fd, (struct sockaddr*)&address, - (socklen_t*)&addrlen)) < 0) { - printf("mouse_read: connection failed \n"); - } } void send_data(struct input_event *ie, int sock) @@ -108,8 +109,65 @@ void send_data(struct input_event *ie, int sock) } } -void * UpdateMouse(void* context) { +void* send_mouse_events(void* context) { + socketContext *sock = (socketContext*) context; + JavaVM *javaVM = sock->javaVM; + JNIEnv *env; + jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6); + if (res != JNI_OK) { + res = (*javaVM)->AttachCurrentThread(javaVM, &env, NULL); + if (JNI_OK != res) { + return NULL; + } + } + struct input_event ie; + if ((fd = open(dev, O_RDONLY)) == -1) { + perror("opening device"); + exit(EXIT_FAILURE); + } + while (read(fd, &ie, sizeof(struct input_event))) { + send_data(&ie, sock->client_fd); + } + close(fd); + close(sock->client_fd); + (*javaVM)->DetachCurrentThread(javaVM); + return context; +} + +void* send_getevent(void *context) { + socketContext *sock = (socketContext*) context; + JavaVM *javaVM = sock->javaVM; + JNIEnv *env; + jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6); + if (res != JNI_OK) { + res = (*javaVM)->AttachCurrentThread(javaVM, &env, NULL); + if (JNI_OK != res) { + return NULL; + } + } + + pid_t cpid = fork(); + if (cpid < 0) exit(1); /* exit if fork() fails */ + if ( cpid ) { + /* In the parent process: */ + close(sock->client_fd ); /* new_socket is not needed in the parent after the fork */ + waitpid( cpid, NULL, 0 ); /* wait for and reap child process */ + } else { + /* In the child process: */ + dup2( sock->client_fd, STDOUT_FILENO ); /* duplicate socket on stdout */ + dup2( sock->client_fd, STDERR_FILENO ); /* duplicate socket on stderr too */ + close( sock->client_fd ); /* can close the original after it's duplicated */ + char *argp[] = {"sh", "-c", + "$LD_LIBRARY_PATH/libgetevent.so -ql", NULL}; + execve(_PATH_BSHELL, argp, environ); + } + (*javaVM)->DetachCurrentThread(javaVM); + return context; +} + +void* init(void* context) { mouseContext *pctx = (mouseContext*) context; + s_ctx.javaVM = pctx->javaVM; JavaVM *javaVM = pctx->javaVM; JNIEnv *env; jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6); @@ -119,6 +177,8 @@ void * UpdateMouse(void* context) { return NULL; } } + create_socket(&s_ctx); + while(true) { pthread_mutex_lock(&pctx->lock); int done = pctx->done; @@ -129,45 +189,47 @@ void * UpdateMouse(void* context) { if (done) { break; } - - if ((fd = open(pctx->dev, O_RDONLY)) == -1) { - perror("opening device"); - exit(EXIT_FAILURE); + printf("Waiting for overlay...\n"); + struct sockaddr_in address = s_ctx.address; + int addrlen = s_ctx.addrlen; + if ((s_ctx.client_fd + = accept(s_ctx.server_fd, (struct sockaddr*)&address, + (socklen_t*)&addrlen)) < 0) { + printf("mouse_read: connection failed \n"); } - - create_socket(&s_cts, pctx->port); - char buffer[12] = { 0 }; - read(s_cts.client_fd, buffer, 12); - printf("%s", buffer); + read(s_ctx.client_fd, buffer, 12); if (strcmp(buffer, "mouse_read\n") == 0) { - struct input_event ie; - while (read(fd, &ie, sizeof(struct input_event))) { - send_data(&ie, s_cts.client_fd); - } - close(fd); - } - else if (strcmp(buffer, "getevent") == 0) { + pthread_t threadInfo_; + pthread_attr_t threadAttr_; + pthread_attr_init(&threadAttr_); + pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED); + pthread_create(&threadInfo_, &threadAttr_, send_mouse_events, &s_ctx); + pthread_attr_destroy(&threadAttr_); + } + else if (strcmp(buffer, "getevent\n") == 0) { + pthread_t threadInfo_; + pthread_attr_t threadAttr_; + + pthread_attr_init(&threadAttr_); + pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED); + pthread_create(&threadInfo_, &threadAttr_, send_getevent, &s_ctx); + pthread_attr_destroy(&threadAttr_); } - - close(s_cts.client_fd); // closing the connected socket - close(s_cts.server_fd); // closing the listening socket - return 0; } - + close(s_ctx.server_fd); // closing the listening socket (*javaVM)->DetachCurrentThread(javaVM); return context; } - - JNIEXPORT void JNICALL - Java_com_xtr_keymapper_Input_startMouse(JNIEnv *env, jobject instance, jstring device, jint port) { +JNIEXPORT void JNICALL + Java_com_xtr_keymapper_Input_startMouse(JNIEnv *env, jobject instance, jstring device, jint default_port) { setlinebuf(stdout); - g_ctx.dev = (*env)->GetStringUTFChars(env, device, 0); - g_ctx.port = port; + dev = (*env)->GetStringUTFChars(env, device, 0); + port = default_port; pthread_t threadInfo_; pthread_attr_t threadAttr_; @@ -179,8 +241,7 @@ void * UpdateMouse(void* context) { jclass clz = (*env)->GetObjectClass(env, instance); (*env)->NewGlobalRef(env, clz); (*env)->NewGlobalRef(env, instance); - - int result = pthread_create(&threadInfo_, &threadAttr_, UpdateMouse, &g_ctx); + int result = pthread_create(&threadInfo_, &threadAttr_, init, &g_ctx); assert(result == 0); pthread_attr_destroy(&threadAttr_); (void) result; diff --git a/app/src/main/java/com/xtr/keymapper/Input.java b/app/src/main/java/com/xtr/keymapper/Input.java index eea420c2..319a59dc 100644 --- a/app/src/main/java/com/xtr/keymapper/Input.java +++ b/app/src/main/java/com/xtr/keymapper/Input.java @@ -152,6 +152,7 @@ public void start(Socket socket) { break; } case "exit": { + Runtime.getRuntime().exec("pkill -f libgetevent.so"); System.exit(1); break; } diff --git a/app/src/main/java/com/xtr/keymapper/TouchPointer.java b/app/src/main/java/com/xtr/keymapper/TouchPointer.java index 59f151d0..4684bfe2 100644 --- a/app/src/main/java/com/xtr/keymapper/TouchPointer.java +++ b/app/src/main/java/com/xtr/keymapper/TouchPointer.java @@ -8,7 +8,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; -import android.os.SystemClock; import android.util.Log; import android.view.Display; import android.view.Gravity; @@ -102,7 +101,7 @@ public void open() { loadKeymap(); startHandlers(); } catch (IOException e) { - e.printStackTrace(); + updateCmdView("error: keymap not set"); } } @@ -118,7 +117,16 @@ public void hideCursor() { ((ViewGroup) cursorView.getParent()).removeAllViews(); // the above steps are necessary when you are adding and removing // the view simultaneously, it might give some exceptions - handlerThread.quit(); + connectionHandler.post(() -> { + try { + keyEventHandler.stop(); + mouseEventHandler.stop(); + handlerThread.quit(); + } catch (IOException e) { + updateCmdView(e.toString()); + } + }); + } catch (Exception e) { Log.e("Error2",e.toString()); } @@ -153,6 +161,7 @@ private void updateCmdView(String s) { private void startHandlers() { Server server = ((MainActivity)context).server; + server.c1 = new StringBuilder(); server.c1.append("connecting to server.."); connectionHandler.post(new Runnable() { int counter = 5; @@ -160,20 +169,20 @@ private void startHandlers() { public void run() { server.c1.append("."); try { - mouseEventHandler.connect(); keyEventHandler.connect(); + mouseEventHandler.connect(); } catch (IOException ignored) { } if (connected) { - new Thread(mouseEventHandler::start).start(); new Thread(keyEventHandler::start).start(); + new Thread(mouseEventHandler::start).start(); } else { if (counter > 0) { connectionHandler.postDelayed(this, 1000); counter--; } else { mHandler.post(() -> hideCursor()); - server.c1.append("\n timeout: exiting after 5 tries \n"); + server.c1.append("\n connection timeout\n Please retry activation \n"); } } } @@ -181,29 +190,37 @@ public void run() { } private class KeyEventHandler { - Socket socket; - DataOutputStream xOut; - String event; - BufferedReader getevent; + private Socket evSocket; + private DataOutputStream xOut; + private BufferedReader getevent; + private PrintWriter pOut; private void connect() throws IOException { - socket = new Socket("127.0.0.1", MainActivity.DEFAULT_PORT); + evSocket = new Socket("127.0.0.1", MainActivity.DEFAULT_PORT_2); + pOut = new PrintWriter(evSocket.getOutputStream()); + pOut.println("getevent"); pOut.flush(); + + Socket socket = new Socket("127.0.0.1", MainActivity.DEFAULT_PORT); xOut = new DataOutputStream(socket.getOutputStream()); - if (dpad1Handler != null) dpad1Handler.setOutputStream(xOut); - if (dpad2Handler != null) dpad2Handler.setOutputStream(xOut); } private void stop() throws IOException { - getevent.close(); + pOut.close(); xOut.close(); + getevent.close(); } private void start() { try { - getevent = Utils.geteventStream(context); + if (dpad1Handler != null) dpad1Handler.setOutputStream(xOut); + if (dpad2Handler != null) dpad2Handler.setOutputStream(xOut); + + getevent = new BufferedReader(new InputStreamReader(evSocket.getInputStream())); + String event; while ((event = getevent.readLine()) != null) { //read events - String[] input_event = event.split("\\s+"); // Keyboard input be like: /dev/input/event3 EV_KEY KEY_X DOWN - TouchPointer.this.updateCmdView(event); // Mouse input be like: /dev/input/event2 EV_REL REL_X ffffffff + String[] input_event = event.split("\\s+"); // Keyboard input: /dev/input/event3 EV_KEY KEY_X DOWN + if (input_event.length < 3) break; // Avoid ArrayIndexOutOfBoundsException + TouchPointer.this.updateCmdView(event); if (input_event[3].equals("DOWN") || input_event[3].equals("UP")) { int i = Utils.obtainIndex(input_event[2]); // Strips off KEY_ from KEY_X and return the index of X in alphabet if (i >= 0 && i <= 35) { // A-Z and 0-9 only in this range @@ -238,7 +255,7 @@ private class MouseEventHandler { private void connect() throws IOException { mouseSocket = new Socket("127.0.0.1", MainActivity.DEFAULT_PORT_2); out = new PrintWriter(mouseSocket.getOutputStream()); - in = new BufferedReader(new InputStreamReader(mouseSocket.getInputStream())); + out.println("mouse_read"); out.flush(); xOutSocket = new Socket("127.0.0.1", MainActivity.DEFAULT_PORT); xOut = new DataOutputStream(xOutSocket.getOutputStream()); @@ -246,7 +263,6 @@ private void connect() throws IOException { } private void start() { - out.println("mouse_read"); out.flush(); getDimensions(); try { pointerGrab(); @@ -286,6 +302,7 @@ private void movePointer() { } private void handleEvents() throws IOException { + in = new BufferedReader(new InputStreamReader(mouseSocket.getInputStream())); while ((line = in.readLine()) != null) { updateCmdView3("socket: " + line); input_event = line.split("\\s+"); diff --git a/app/src/main/java/com/xtr/keymapper/activity/MainActivity.java b/app/src/main/java/com/xtr/keymapper/activity/MainActivity.java index e1304964..444b0261 100644 --- a/app/src/main/java/com/xtr/keymapper/activity/MainActivity.java +++ b/app/src/main/java/com/xtr/keymapper/activity/MainActivity.java @@ -13,7 +13,6 @@ import com.xtr.keymapper.Server; import com.xtr.keymapper.TouchPointer; - public class MainActivity extends AppCompatActivity { public static final int DEFAULT_PORT = 6234; public static final int DEFAULT_PORT_2 = 6345; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b2dabf8d..bdcf165c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ XtMapper Overlay - Activate + Activate with su Activate with adb Select Input device Edit mode