Skip to content

Commit

Permalink
fix(android.c, CmdEntryPoint.java): fix non-critical EADDRINUSE error
Browse files Browse the repository at this point in the history
  • Loading branch information
twaik committed Dec 11, 2024
1 parent 473a650 commit 0d448f7
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 34 deletions.
56 changes: 56 additions & 0 deletions app/src/main/cpp/lorie/android.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma ide diagnostic ignored "bugprone-reserved-identifier"
#pragma ide diagnostic ignored "OCUnusedMacroInspection"
#pragma ide diagnostic ignored "EndlessLoop"
#define __USE_GNU
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
Expand All @@ -20,6 +21,8 @@
#include <wchar.h>
#include <inpututils.h>
#include <randrstr.h>
#include <linux/in.h>
#include <arpa/inet.h>
#include "renderer.h"
#include "lorie.h"

Expand Down Expand Up @@ -518,6 +521,56 @@ Java_com_termux_x11_CmdEntryPoint_connected(__unused JNIEnv *env, __unused jclas
return conn_fd != -1;
}

JNIEXPORT void JNICALL
Java_com_termux_x11_CmdEntryPoint_listenForConnections(JNIEnv *env, jobject thiz, jint port, jbyteArray jbytes) {
int server_fd, client, count;
struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr = { .s_addr = INADDR_ANY }, .sin_port = htons(port) };
int addrlen = sizeof(address);
jmethodID sendBroadcast = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, thiz), "sendBroadcast", "()V");
jbyte *bytes = (jbyte *)(*env)->GetByteArrayElements(env, jbytes, NULL);
size_t size = (*env)->GetArrayLength(env, jbytes);
uint8_t buffer[512] = {0};

// Even in the case if it will fail for some reason everything will work fine
// But connection will be delayed a bit

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
log(ERROR, "Socket creation failed: %s", strerror(errno));
return;
}

setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int));

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
log(ERROR, "Socket bind failed: %s", strerror(errno));
close(server_fd);
return;
}

if (listen(server_fd, 5) < 0) {
log(ERROR, "Socket listen failed: %s", strerror(errno));
close(server_fd);
return;
}

while(1) {
if ((client = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
log(ERROR, "Socket accept failed: %s", strerror(errno));
continue;
}

if ((count = read(client, buffer, sizeof(buffer))) > 0) {
if (!memcmp(buffer, bytes, min(count, size))) {
log(DEBUG, "New client connection!\n");
(*env)->CallVoidMethod(env, thiz, sendBroadcast);
}
}
close(client);
client = -1;
}
}

static inline void checkConnection(JNIEnv* env) {
int retval, b = 0;

Expand Down Expand Up @@ -712,6 +765,9 @@ Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, __unused jobject thiz,
jbyte *str = (*env)->GetByteArrayElements(env, text, NULL);
char *p = (char*) str;
mbstate_t state = { 0 };
if (!length)
return;

log(DEBUG, "Parsing text: %.*s", length, str);

while (*p) {
Expand Down
36 changes: 2 additions & 34 deletions app/src/main/java/com/termux/x11/CmdEntryPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,11 @@

import androidx.annotation.Keep;

import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;

@Keep @SuppressLint({"StaticFieldLeak", "UnsafeDynamicallyLoadedCode"})
public class CmdEntryPoint extends ICmdEntryInterface.Stub {
Expand Down Expand Up @@ -132,36 +128,7 @@ private void sendBroadcastDelayed() {
}

void spawnListeningThread() {
new Thread(() -> { // New thread is needed to avoid android.os.NetworkOnMainThreadException
/*
The purpose of this function is simple. If the application has not been launched
before running termux-x11, the initial sendBroadcast had no effect because no one
received the intent. To allow the application to reconnect freely, we will listen on
port `PORT` and when receiving a magic phrase, we will send another intent.
*/
Log.e("CmdEntryPoint", "Listening port " + PORT);
try (ServerSocket listeningSocket =
new ServerSocket(PORT, 0, InetAddress.getByName("127.0.0.1"))) {
listeningSocket.setReuseAddress(true);
while(true) {
try (Socket client = listeningSocket.accept()) {
Log.e("CmdEntryPoint", "Somebody connected!");
// We should ensure that it is some
byte[] b = new byte[MAGIC.length];
DataInputStream reader = new DataInputStream(client.getInputStream());
reader.readFully(b);
if (Arrays.equals(MAGIC, b)) {
Log.e("CmdEntryPoint", "New client connection!");
sendBroadcast();
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
}).start();
new Thread(() -> listenForConnections(PORT, MAGIC)).start();
}

public static void requestConnection() {
Expand Down Expand Up @@ -215,6 +182,7 @@ public static Context createContext() {
public native ParcelFileDescriptor getXConnection();
public native ParcelFileDescriptor getLogcatOutput();
private static native boolean connected();
private native void listenForConnections(int port, byte[] bytes);

static {
try {
Expand Down

0 comments on commit 0d448f7

Please sign in to comment.