From 72941a9e8814d7a497e321e2fc9faea59e70b668 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 6 Nov 2017 11:17:03 +0100 Subject: [PATCH] Use manage thread for native code --- .../eu/faircode/netguard/ServiceSinkhole.java | 57 +++++++++++--- app/src/main/jni/netguard/netguard.c | 77 ++++++++----------- app/src/main/jni/netguard/session.c | 40 +++------- 3 files changed, 86 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java index a6e8f66c..06d7e263 100644 --- a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java +++ b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java @@ -135,6 +135,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS private int last_blocked = -1; private int last_hosts = -1; + private Thread tunnelThread = null; private ServiceSinkhole.Builder last_builder = null; private ParcelFileDescriptor vpn = null; private boolean temporarilyStopped = false; @@ -195,9 +196,13 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS private native void jni_init(int sdk); - private native void jni_start(int tun, boolean fwd53, int rcode, int loglevel); + private native void jni_start(int loglevel); - private native void jni_stop(int tun, boolean clr); + private native void jni_run(int tun, boolean fwd53, int rcode); + + private native void jni_stop(); + + private native void jni_clear(); private native int jni_get_mtu(); @@ -1369,7 +1374,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS return builder; } - private void startNative(ParcelFileDescriptor vpn, List listAllowed, List listRule) { + private void startNative(final ParcelFileDescriptor vpn, List listAllowed, List listRule) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this); boolean log = prefs.getBoolean("log", false); boolean log_app = prefs.getBoolean("log_app", false); @@ -1403,7 +1408,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS if (log || log_app || filter) { int prio = Integer.parseInt(prefs.getString("loglevel", Integer.toString(Log.WARN))); - int rcode = Integer.parseInt(prefs.getString("rcode", "3")); + final int rcode = Integer.parseInt(prefs.getString("rcode", "3")); if (prefs.getBoolean("socks5_enabled", false)) jni_socks5( prefs.getString("socks5_addr", ""), @@ -1412,18 +1417,48 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS prefs.getString("socks5_password", "")); else jni_socks5("", 0, "", ""); - jni_start(vpn.getFd(), mapForward.containsKey(53), rcode, prio); + + if (tunnelThread == null) { + Log.i(TAG, "Starting tunnel thread"); + jni_start(prio); + + tunnelThread = new Thread(new Runnable() { + @Override + public void run() { + Log.i(TAG, "Running tunnel"); + jni_run(vpn.getFd(), mapForward.containsKey(53), rcode); + Log.i(TAG, "Tunnel exited"); + tunnelThread = null; + } + }); + tunnelThread.setPriority(Thread.MAX_PRIORITY); + tunnelThread.start(); + + Log.i(TAG, "Started tunnel thread"); + } } } private void stopNative(ParcelFileDescriptor vpn, boolean clear) { Log.i(TAG, "Stop native clear=" + clear); - try { - jni_stop(vpn.getFd(), clear); - } catch (Throwable ex) { - // File descriptor might be closed - Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex)); - jni_stop(-1, clear); + + if (tunnelThread != null) { + Log.i(TAG, "Stopping tunnel thread"); + + jni_stop(); + + while (true) + try { + tunnelThread.join(); + break; + } catch (InterruptedException ignored) { + } + tunnelThread = null; + + if (clear) + jni_clear(); + + Log.i(TAG, "Stopped tunnel thread"); } } diff --git a/app/src/main/jni/netguard/netguard.c b/app/src/main/jni/netguard/netguard.c index 1d2d4a46..f59506df 100644 --- a/app/src/main/jni/netguard/netguard.c +++ b/app/src/main/jni/netguard/netguard.c @@ -24,9 +24,8 @@ // Global variables -JavaVM *jvm = NULL; int pipefds[2]; -pthread_t thread_id = 0; +int stopping = 0; pthread_mutex_t lock; char socks5_addr[INET6_ADDRSTRLEN + 1]; int socks5_port = 0; @@ -133,13 +132,21 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1init(JNIEnv *env, jobject instanc JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1start( - JNIEnv *env, jobject instance, jint tun, jboolean fwd53, jint rcode, jint loglevel_) { + JNIEnv *env, jobject instance, jint loglevel_) { loglevel = loglevel_; max_tun_msg = 0; - log_android(ANDROID_LOG_WARN, - "Starting tun %d fwd53 %d level %d thread %x", - tun, fwd53, loglevel, thread_id); + stopping = 0; + + log_android(ANDROID_LOG_WARN, "Starting level %d", loglevel); + +} + +JNIEXPORT void JNICALL +Java_eu_faircode_netguard_ServiceSinkhole_jni_1run( + JNIEnv *env, jobject instance, jint tun, jboolean fwd53, jint rcode) { + + log_android(ANDROID_LOG_WARN, "Running tun %d fwd53 %d level %d", tun, fwd53, loglevel); // Set blocking int flags = fcntl(tun, F_GETFL, 0); @@ -147,52 +154,30 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1start( log_android(ANDROID_LOG_ERROR, "fcntl tun ~O_NONBLOCK error %d: %s", errno, strerror(errno)); - if (thread_id && pthread_kill(thread_id, 0) == 0) - log_android(ANDROID_LOG_ERROR, "Already running thread %x", thread_id); - else { - jint rs = (*env)->GetJavaVM(env, &jvm); - if (rs != JNI_OK) - log_android(ANDROID_LOG_ERROR, "GetJavaVM failed"); - - // Get arguments - struct arguments *args = malloc(sizeof(struct arguments)); - // args->env = will be set in thread - args->instance = (*env)->NewGlobalRef(env, instance); - args->tun = tun; - args->fwd53 = fwd53; - args->rcode = rcode; - - // Start native thread - int err = pthread_create(&thread_id, NULL, handle_events, (void *) args); - if (err == 0) - log_android(ANDROID_LOG_WARN, "Started thread %x", thread_id); - else - log_android(ANDROID_LOG_ERROR, "pthread_create error %d: %s", err, strerror(err)); - } + // Get arguments + struct arguments *args = malloc(sizeof(struct arguments)); + args->env = env; + args->instance = instance; + args->tun = tun; + args->fwd53 = fwd53; + args->rcode = rcode; + handle_events(args); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1stop( - JNIEnv *env, jobject instance, jint tun, jboolean clr) { - pthread_t t = thread_id; - log_android(ANDROID_LOG_WARN, "Stop tun %d thread %x", tun, t); - if (t && pthread_kill(t, 0) == 0) { - log_android(ANDROID_LOG_WARN, "Write pipe thread %x", t); - if (write(pipefds[1], "x", 1) < 0) - log_android(ANDROID_LOG_WARN, "Write pipe error %d: %s", errno, strerror(errno)); - else { - log_android(ANDROID_LOG_WARN, "Join thread %x", t); - int err = pthread_join(t, NULL); - if (err != 0) - log_android(ANDROID_LOG_WARN, "pthread_join error %d: %s", err, strerror(err)); - } + JNIEnv *env, jobject instance) { + stopping = 1; - if (clr) - clear(); + log_android(ANDROID_LOG_WARN, "Write pipe wakeup"); + if (write(pipefds[1], "w", 1) < 0) + log_android(ANDROID_LOG_WARN, "Write pipe error %d: %s", errno, strerror(errno)); +} - log_android(ANDROID_LOG_WARN, "Stopped thread %x", t); - } else - log_android(ANDROID_LOG_WARN, "Not running thread %x", t); +JNIEXPORT void JNICALL +Java_eu_faircode_netguard_ServiceSinkhole_jni_1clear( + JNIEnv *env, jobject instance) { + clear(); } JNIEXPORT jint JNICALL diff --git a/app/src/main/jni/netguard/session.c b/app/src/main/jni/netguard/session.c index 3f90d5c5..3b4825ef 100644 --- a/app/src/main/jni/netguard/session.c +++ b/app/src/main/jni/netguard/session.c @@ -19,9 +19,8 @@ #include "netguard.h" -extern JavaVM *jvm; extern int pipefds[2]; -extern pthread_t thread_id; +extern int stopping; extern pthread_mutex_t lock; struct ng_session *ng_session = NULL; @@ -47,16 +46,7 @@ void clear() { void *handle_events(void *a) { struct arguments *args = (struct arguments *) a; - log_android(ANDROID_LOG_WARN, "Start events tun=%d thread %x", args->tun, thread_id); - - // Attach to Java - JNIEnv *env; - jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL); - if (rs != JNI_OK) { - log_android(ANDROID_LOG_ERROR, "AttachCurrentThread failed"); - return NULL; - } - args->env = env; + log_android(ANDROID_LOG_WARN, "Start events tun=%d", args->tun); // Get max number of sessions int maxsessions = SESSION_MAX; @@ -74,8 +64,6 @@ void *handle_events(void *a) { // Terminate existing sessions not allowed anymore check_allowed(args); - int stopping = 0; - // Open epoll file int epoll_fd = epoll_create(1); if (epoll_fd < 0) { @@ -109,7 +97,7 @@ void *handle_events(void *a) { // Loop long long last_check = 0; while (!stopping) { - log_android(ANDROID_LOG_DEBUG, "Loop thread %x", thread_id); + log_android(ANDROID_LOG_DEBUG, "Loop"); int recheck = 0; int timeout = EPOLL_TIMEOUT; @@ -204,15 +192,14 @@ void *handle_events(void *a) { if (ready < 0) { if (errno == EINTR) { - log_android(ANDROID_LOG_DEBUG, - "epoll interrupted tun %d thread %x", args->tun, thread_id); + log_android(ANDROID_LOG_DEBUG, "epoll interrupted tun %d", args->tun); continue; } else { log_android(ANDROID_LOG_ERROR, - "epoll tun %d thread %x error %d: %s", - args->tun, thread_id, errno, strerror(errno)); - report_exit(args, "epoll tun %d thread %x error %d: %s", - args->tun, thread_id, errno, strerror(errno)); + "epoll tun %d error %d: %s", + args->tun, errno, strerror(errno)); + report_exit(args, "epoll tun %d error %d: %s", + args->tun, errno, strerror(errno)); break; } } @@ -229,7 +216,6 @@ void *handle_events(void *a) { for (int i = 0; i < ready; i++) { if (ev[i].data.ptr == &ev_pipe) { // Check pipe - stopping = 1; uint8_t buffer[1]; if (read(pipefds[0], buffer, 1) < 0) log_android(ANDROID_LOG_WARN, "Read pipe error %d: %s", @@ -292,18 +278,10 @@ void *handle_events(void *a) { log_android(ANDROID_LOG_ERROR, "epoll close error %d: %s", errno, strerror(errno)); - (*env)->DeleteGlobalRef(env, args->instance); - - // Detach from Java - rs = (*jvm)->DetachCurrentThread(jvm); - if (rs != JNI_OK) - log_android(ANDROID_LOG_ERROR, "DetachCurrentThread failed"); - // Cleanup free(args); - log_android(ANDROID_LOG_WARN, "Stopped events tun=%d thread %x", args->tun, thread_id); - thread_id = 0; + log_android(ANDROID_LOG_WARN, "Stopped events tun=%d", args->tun); return NULL; }