Initial: initial commit

This commit is contained in:
kr328
2021-05-15 00:51:08 +08:00
commit 07e8afa69a
483 changed files with 26328 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.0)
project(clash-bridge C)
set(GO_OUTPUT_BASE ${GO_OUTPUT}/${FLAVOR_NAME})
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(GO_OUTPUT_BASE "${GO_OUTPUT_BASE}Debug")
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
set(GO_OUTPUT_BASE "${GO_OUTPUT_BASE}Release")
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
set(GO_OUTPUT_BASE "${GO_OUTPUT_BASE}Release")
else ()
message(FATAL_ERROR "Unknown build type ${CMAKE_BUILD_TYPE}")
endif ()
include_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
include_directories("${GO_SOURCE}")
link_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
add_library(bridge SHARED main.c jni_helper.c bridge_helper.c)
target_link_libraries(bridge log clash)

View File

@@ -0,0 +1,12 @@
#include "bridge_helper.h"
uint64_t down_scale_traffic(uint64_t value) {
if (value > 1042 * 1024 * 1024)
return ((value * 100u / 1024u / 1024u / 1024u) & 0x3FFFFFFFu) | (3u << 30u);
if (value > 1024 * 1024)
return ((value * 100u / 1024u / 1024u) & 0x3FFFFFFFu) | (2u << 30u);
if (value > 1024)
return ((value * 100u / 1024u) & 0x3FFFFFFFu) | (1u << 30u);
return value & 0x3FFFFFFFu;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
uint64_t down_scale_traffic(uint64_t value);

View File

@@ -0,0 +1,75 @@
#include "jni_helper.h"
#include <malloc.h>
#include <string.h>
static JavaVM *global_vm;
static jclass c_string;
static jmethodID m_new_string;
static jmethodID m_get_bytes;
void initialize_jni(JavaVM *vm, JNIEnv *env) {
global_vm = vm;
c_string = (jclass) new_global(find_class("java/lang/String"));
m_new_string = find_method(c_string, "<init>", "([B)V");
m_get_bytes = find_method(c_string, "getBytes", "()[B");
}
JavaVM *global_java_vm() {
return global_vm;
}
char *jni_get_string(JNIEnv *env, jstring str) {
jbyteArray array = (*env)->CallObjectMethod(env, str, m_get_bytes);
int length = (*env)->GetArrayLength(env, array);
char *content = (char *) malloc(length + 1);
(*env)->GetByteArrayRegion(env, array, 0, length, (jbyte *) content);
content[length] = 0;
return content;
}
jstring jni_new_string(JNIEnv *env, const char *str) {
int length = strlen(str);
jbyteArray array = (*env)->NewByteArray(env, length);
(*env)->SetByteArrayRegion(env, array, 0, length, (const jbyte *) str);
return (jstring) (*env)->NewObject(env, c_string, m_new_string, array);
}
int jni_catch_exception(JNIEnv *env) {
int result = (*env)->ExceptionCheck(env);
if (result) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
return result;
}
void jni_attach_thread(JNIEnv **penv) {
JavaVM *vm = global_java_vm();
if ((*vm)->AttachCurrentThread(vm, penv, NULL) != JNI_OK) {
abort();
}
}
void jni_detach_thread(JNIEnv **env) {
(void) env;
JavaVM *vm = global_java_vm();
(*vm)->DetachCurrentThread(vm);
}
void release_string(char **str) {
free(*str);
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <jni.h>
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <android/log.h>
extern void initialize_jni(JavaVM *vm, JNIEnv *env);
extern jstring jni_new_string(JNIEnv *env, const char *str);
extern char *jni_get_string(JNIEnv *env, jstring str);
extern int jni_catch_exception(JNIEnv *env);
extern void jni_attach_thread(JNIEnv **penv);
extern void jni_detach_thread(JNIEnv **env);
extern void release_string(char **str);
#define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) JNIEnv *env = NULL; jni_attach_thread(&env)
#define scoped_string __attribute__((cleanup(release_string))) char*
#define find_class(name) (*env)->FindClass(env, name)
#define find_method(cls, name, signature) (*env)->GetMethodID(env, cls, name, signature)
#define new_global(obj) (*env)->NewGlobalRef(env, obj)
#define del_global(obj) (*env)->DeleteGlobalRef(env, obj)
#define get_string(jstr) jni_get_string(env, jstr)
#define new_string(cstr) jni_new_string(env, cstr)

528
core/src/main/cpp/main.c Normal file
View File

@@ -0,0 +1,528 @@
#include <jni.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "bridge_helper.h"
#include "libclash.h"
#include "jni_helper.h"
#include "trace.h"
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeInit(JNIEnv *env, jobject thiz,
jstring home,
jstring version_name, jint sdk_version) {
TRACE_METHOD();
scoped_string _home = get_string(home);
scoped_string _version_name = get_string(version_name);
coreInit(_home, _version_name, sdk_version);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeReset(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
reset();
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeForceGc(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
forceGc();
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeSuspend(JNIEnv *env, jobject thiz,
jboolean suspended) {
TRACE_METHOD();
suspend((int) suspended);
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTunnelState(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
scoped_string response = queryTunnelState();
return new_string(response);
}
JNIEXPORT jlong JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTrafficNow(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
uint64_t upload = 0l, download = 0l;
queryNow(&upload, &download);
return (jlong) (down_scale_traffic(upload) << 32u | down_scale_traffic(download));
}
JNIEXPORT jlong JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryTrafficTotal(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
uint64_t upload = 0l, download = 0l;
queryTotal(&upload, &download);
return (jlong) (down_scale_traffic(upload) << 32u | down_scale_traffic(download));
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyDnsChanged(JNIEnv *env, jobject thiz,
jstring dns_list) {
TRACE_METHOD();
scoped_string _dns_list = get_string(dns_list);
notifyDnsChanged(_dns_list);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(JNIEnv *env,
jobject thiz,
jstring uid_list) {
TRACE_METHOD();
scoped_string _uid_list = get_string(uid_list);
notifyInstalledAppsChanged(_uid_list);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz, jint fd,
jint mtu, jstring gateway,
jstring mirror, jstring dns,
jobject cb) {
TRACE_METHOD();
scoped_string _gateway = get_string(gateway);
scoped_string _mirror = get_string(mirror);
scoped_string _dns = get_string(dns);
jobject _interface = new_global(cb);
startTun(fd, mtu, _gateway, _mirror, _dns, _interface);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStopTun(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
stopTun();
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartHttp(JNIEnv *env, jobject thiz,
jstring listen_at) {
TRACE_METHOD();
scoped_string _listen_at = get_string(listen_at);
scoped_string listened = startHttp(_listen_at);
if (listened == NULL)
return NULL;
return new_string(listened);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStopHttp(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
stopHttp();
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryGroupNames(JNIEnv *env, jobject thiz,
jboolean exclude_not_selectable) {
TRACE_METHOD();
scoped_string response = queryGroupNames((int) exclude_not_selectable);
return new_string(response);
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryGroup(JNIEnv *env, jobject thiz,
jstring name, jstring mode) {
TRACE_METHOD();
scoped_string _name = get_string(name);
scoped_string _mode = get_string(mode);
scoped_string response = queryGroup(_name, _mode);
if (response == NULL)
return NULL;
return new_string(response);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeHealthCheck(JNIEnv *env, jobject thiz,
jobject completable,
jstring name) {
TRACE_METHOD();
jobject _completable = new_global(completable);
scoped_string _name = get_string(name);
healthCheck(_completable, _name);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeHealthCheckAll(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
healthCheckAll();
}
JNIEXPORT jboolean JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativePatchSelector(JNIEnv *env, jobject thiz,
jstring selector, jstring name) {
TRACE_METHOD();
scoped_string _selector = get_string(selector);
scoped_string _name = get_string(name);
return (jboolean) patchSelector(_selector, _name);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeLoad(JNIEnv *env, jobject thiz,
jobject completable, jstring path) {
TRACE_METHOD();
jobject _completable = new_global(completable);
scoped_string _path = get_string(path);
load(_completable, _path);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeFetchAndValid(JNIEnv *env, jobject thiz,
jobject callback,
jstring path,
jstring url, jboolean force) {
TRACE_METHOD();
jobject _completable = new_global(callback);
scoped_string _path = get_string(path);
scoped_string _url = get_string(url);
fetchAndValid(_completable, _path, _url, force);
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryProviders(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
scoped_string response = queryProviders();
return new_string(response);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeUpdateProvider(JNIEnv *env, jobject thiz,
jobject completable,
jstring type,
jstring name) {
TRACE_METHOD();
jobject _completable = new_global(completable);
scoped_string _type = get_string(type);
scoped_string _name = get_string(name);
updateProvider(_completable, _type, _name);
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeReadOverride(JNIEnv *env, jobject thiz,
jint slot) {
TRACE_METHOD();
scoped_string response = readOverride(slot);
return new_string(response);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeWriteOverride(JNIEnv *env, jobject thiz,
jint slot,
jstring content) {
TRACE_METHOD();
scoped_string _content = get_string(content);
writeOverride(slot, _content);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeClearOverride(JNIEnv *env, jobject thiz,
jint slot) {
TRACE_METHOD();
clearOverride(slot);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeInstallSideloadGeoip(JNIEnv *env, jobject thiz,
jbyteArray data) {
TRACE_METHOD();
if (data == NULL) {
installSideloadGeoip(NULL, 0);
return;
}
jbyte *bytes = (*env)->GetByteArrayElements(env, data, NULL);
int size = (*env)->GetArrayLength(env, data);
scoped_string err = installSideloadGeoip(bytes, size);
(*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
if (err != NULL) {
(*env)->ThrowNew(
env,
find_class("com/github/kr328/clash/core/bridge/ClashException"),
err
);
}
}
JNIEXPORT jstring JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeQueryConfiguration(JNIEnv *env, jobject thiz) {
TRACE_METHOD();
scoped_string response = queryConfiguration();
return new_string(response);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeSubscribeLogcat(JNIEnv *env, jobject thiz,
jobject callback) {
TRACE_METHOD();
jobject _callback = new_global(callback);
subscribeLogcat(_callback);
}
static jmethodID m_tun_interface_mark_socket;
static jmethodID m_tun_interface_query_socket_uid;
static jmethodID m_completable_complete;
static jmethodID m_completable_complete_exceptionally;
static jmethodID m_logcat_interface_received;
static jmethodID m_clash_exception;
static jmethodID m_fetch_callback_report;
static jmethodID m_fetch_callback_complete;
static jmethodID m_open;
static jmethodID m_get_message;
static jclass c_clash_exception;
static jclass c_content;
static jobject o_unit;
static void call_tun_interface_mark_socket_impl(void *tun_interface, int fd) {
TRACE_METHOD();
ATTACH_JNI();
(*env)->CallVoidMethod(env, (jobject) tun_interface,
(jmethodID) m_tun_interface_mark_socket,
(jint) fd);
}
static int call_tun_interface_query_socket_uid_impl(void *tun_interface, int protocol,
const char *source, const char *target) {
TRACE_METHOD();
ATTACH_JNI();
return (*env)->CallIntMethod(env, (jobject) tun_interface,
(jmethodID) m_tun_interface_query_socket_uid,
(jint) protocol,
(jstring) new_string(source),
(jstring) new_string(target));
}
static void call_completable_complete_impl(void *completable, const char *exception) {
TRACE_METHOD();
ATTACH_JNI();
if (exception == NULL) {
(*env)->CallBooleanMethod(env,
(jobject) completable,
(jmethodID) m_completable_complete,
(jobject) o_unit);
} else {
jthrowable _exception = (jthrowable)
(*env)->NewObject(env,
(jclass) c_clash_exception,
(jmethodID) m_clash_exception,
(jstring) new_string(exception)
);
(*env)->CallBooleanMethod(env,
(jobject) completable,
(jmethodID) m_completable_complete_exceptionally,
(jobject) _exception);
}
}
static void call_fetch_callback_report_impl(void *fetch_callback, const char *status_json) {
TRACE_METHOD();
ATTACH_JNI();
jstring _status_json = new_string(status_json);
(*env)->CallVoidMethod(env,
(jobject) fetch_callback,
(jmethodID) m_fetch_callback_report,
(jstring) _status_json);
}
static void call_fetch_callback_complete_impl(void *fetch_callback, const char *error) {
TRACE_METHOD();
ATTACH_JNI();
jstring _error = NULL;
if (error != NULL)
_error = new_string(error);
(*env)->CallVoidMethod(env,
(jobject) fetch_callback,
(jmethodID) m_fetch_callback_complete,
(jstring) _error);
}
static int call_logcat_interface_received_impl(void *callback, const char *payload) {
TRACE_METHOD();
ATTACH_JNI();
(*env)->CallVoidMethod(env,
(jobject) callback,
(jmethodID) m_logcat_interface_received,
(jstring) new_string(payload));
if (jni_catch_exception(env)) {
return 1;
}
return 0;
}
static int open_content_impl(const char *url, char *error, int error_length) {
TRACE_METHOD();
ATTACH_JNI();
int fd = (*env)->CallStaticIntMethod(env, c_content, m_open, new_string(url));
if ((*env)->ExceptionCheck(env)) {
jthrowable exception = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env);
jstring message = (jstring) (*env)->CallObjectMethod(
env,
(jthrowable) exception,
(jmethodID) m_get_message
);
if (message == NULL) {
strncpy(error, "unknown", error_length - 1);
} else {
scoped_string _message = get_string(message);
strncpy(error, _message, error_length - 1);
}
return -1;
}
return fd;
}
static void release_jni_object_impl(void *obj) {
TRACE_METHOD();
ATTACH_JNI();
del_global((jobject) obj);
}
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
TRACE_METHOD();
JNIEnv *env = NULL;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
initialize_jni(vm, env);
jclass c_tun_interface = find_class("com/github/kr328/clash/core/bridge/TunInterface");
jclass c_completable = find_class("kotlinx/coroutines/CompletableDeferred");
jclass c_fetch_callback = find_class("com/github/kr328/clash/core/bridge/FetchCallback");
jclass c_logcat_interface = find_class("com/github/kr328/clash/core/bridge/LogcatInterface");
jclass _c_clash_exception = find_class("com/github/kr328/clash/core/bridge/ClashException");
jclass _c_content = find_class("com/github/kr328/clash/core/bridge/Content");
jclass c_throwable = find_class("java/lang/Throwable");
jclass c_unit = find_class("kotlin/Unit");
m_tun_interface_mark_socket = find_method(c_tun_interface, "markSocket",
"(I)V");
m_tun_interface_query_socket_uid = find_method(c_tun_interface, "querySocketUid",
"(ILjava/lang/String;Ljava/lang/String;)I");
m_completable_complete = find_method(c_completable, "complete",
"(Ljava/lang/Object;)Z");
m_fetch_callback_report = find_method(c_fetch_callback, "report",
"(Ljava/lang/String;)V");
m_fetch_callback_complete = find_method(c_fetch_callback, "complete",
"(Ljava/lang/String;)V");
m_completable_complete_exceptionally = find_method(c_completable, "completeExceptionally",
"(Ljava/lang/Throwable;)Z");
m_logcat_interface_received = find_method(c_logcat_interface, "received",
"(Ljava/lang/String;)V");
m_clash_exception = find_method(_c_clash_exception, "<init>",
"(Ljava/lang/String;)V");
m_get_message = find_method(c_throwable, "getMessage",
"()Ljava/lang/String;");
m_open = (*env)->GetStaticMethodID(env, _c_content, "open",
"(Ljava/lang/String;)I");
o_unit = (*env)->GetStaticObjectField(env, c_unit,
(*env)->GetStaticFieldID(env, c_unit, "INSTANCE",
"Lkotlin/Unit;"));
c_clash_exception = (jclass) new_global(_c_clash_exception);
c_content = (jclass) new_global(_c_content);
o_unit = new_global(o_unit);
mark_socket_func = &call_tun_interface_mark_socket_impl;
query_socket_uid_func = &call_tun_interface_query_socket_uid_impl;
complete_func = &call_completable_complete_impl;
fetch_report_func = &call_fetch_callback_report_impl;
fetch_complete_func = &call_fetch_callback_complete_impl;
logcat_received_func = &call_logcat_interface_received_impl;
open_content_func = &open_content_impl;
release_object_func = &release_jni_object_impl;
return JNI_VERSION_1_6;
}