refactor clash bridge
This commit is contained in:
36 files changed
+1051
-561
No files matched your search
@@ -94,8 +94,7 @@ fun generateGolangBuildEnvironment(abi: String): Map<String, String> {
|
||||
"GOOS" to "android",
|
||||
"GOARCH" to golangArch,
|
||||
"CGO_ENABLED" to "1",
|
||||
"LDFLAGS" to "-lpthread",
|
||||
"CFLAGS" to "-O3"
|
||||
"CFLAGS" to "-O3 -Werror"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -128,7 +127,7 @@ fun String.exec(pwd: File = buildDir, env: Map<String, String> = System.getenv()
|
||||
task("compileClashCore") {
|
||||
onlyIf {
|
||||
val sourceModified = golangSource.walk()
|
||||
.filter { it.extension == "go" || it.name == "go.mod" }
|
||||
.filter { it.extension == "go" || it.extension == "c" || it.extension == "h" || it.name == "go.mod" }
|
||||
.map { it.lastModified() }
|
||||
.max() ?: Long.MAX_VALUE
|
||||
val targetModified = golangOutput.walk()
|
||||
|
||||
@@ -8,4 +8,4 @@ include_directories(${CLASH_OUTPUT}/${ANDROID_ABI} ${CLASH_SOURCE})
|
||||
link_directories(${CLASH_OUTPUT}/${CMAKE_ANDROID_ARCH_ABI})
|
||||
link_libraries(log clash)
|
||||
|
||||
add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp)
|
||||
add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp tun.cpp defer.cpp)
|
||||
@@ -0,0 +1,175 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
struct deferContext {
|
||||
jobject completableFuture;
|
||||
};
|
||||
|
||||
static void downloadContextSuccess(void *context, void *argument) {
|
||||
UNUSED(argument);
|
||||
|
||||
auto *ctx = reinterpret_cast<deferContext*>(context);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
context->completeCompletableFuture(ctx->completableFuture, nullptr);
|
||||
|
||||
context->removeGlobalReference(ctx->completableFuture);
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
static void downloadContextFailure(void *context, void *argument) {
|
||||
auto *ctx = reinterpret_cast<deferContext*>(context);
|
||||
auto *err = reinterpret_cast<char *>(argument);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
if (err != nullptr) {
|
||||
context->completeExceptionallyCompletableFuture(ctx->completableFuture, context->newClashException(err));
|
||||
} else {
|
||||
context->completeExceptionallyCompletableFuture(ctx->completableFuture, context->newClashException("Unknown"));
|
||||
}
|
||||
|
||||
context->removeGlobalReference(ctx->completableFuture);
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
free(err);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_downloadProfile__ILjava_lang_String_2Ljava_lang_String_2(
|
||||
JNIEnv *env, jclass clazz, jint fd, jstring base, jstring output) {
|
||||
UNUSED(clazz);
|
||||
|
||||
return Master::runWithContext<jobject>(env, [&](Master::Context *context) -> jobject {
|
||||
const char *b = context->getString(base);
|
||||
const char *o = context->getString(output);
|
||||
|
||||
auto *ctx = new deferContext();
|
||||
|
||||
ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture());
|
||||
|
||||
ccall_t onSuccess = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextSuccess
|
||||
};
|
||||
ccall_t onFailure = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextFailure
|
||||
};
|
||||
|
||||
downloadProfileFromFd(fd, b, o, onSuccess, onFailure);
|
||||
|
||||
context->releaseString(base, b);
|
||||
context->releaseString(output, o);
|
||||
|
||||
return ctx->completableFuture;
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_downloadProfile__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2(
|
||||
JNIEnv *env, jclass clazz, jstring url, jstring base, jstring output) {
|
||||
UNUSED(clazz);
|
||||
|
||||
return Master::runWithContext<jobject>(env, [&](Master::Context *context) -> jobject {
|
||||
const char *u = context->getString(url);
|
||||
const char *b = context->getString(base);
|
||||
const char *o = context->getString(output);
|
||||
|
||||
auto *ctx = new deferContext();
|
||||
|
||||
ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture());
|
||||
|
||||
ccall_t onSuccess = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextSuccess
|
||||
};
|
||||
ccall_t onFailure = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextFailure
|
||||
};
|
||||
|
||||
downloadProfileFromUrl(u, b, o, onSuccess, onFailure);
|
||||
|
||||
context->releaseString(url, u);
|
||||
context->releaseString(base, b);
|
||||
context->releaseString(output, o);
|
||||
|
||||
return ctx->completableFuture;
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_loadProfile(JNIEnv *env, jclass clazz, jstring path,
|
||||
jstring base) {
|
||||
UNUSED(clazz);
|
||||
|
||||
return Master::runWithContext<jobject>(env, [&](Master::Context *context) -> jobject {
|
||||
const char *p = context->getString(path);
|
||||
const char *b = context->getString(base);
|
||||
|
||||
auto *ctx = new deferContext();
|
||||
|
||||
ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture());
|
||||
|
||||
ccall_t onSuccess = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextSuccess
|
||||
};
|
||||
ccall_t onFailure = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextFailure
|
||||
};
|
||||
|
||||
loadProfile(p, b, onSuccess, onFailure);
|
||||
|
||||
context->releaseString(path, p);
|
||||
context->releaseString(base, b);
|
||||
|
||||
return ctx->completableFuture;
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_performHealthCheck(JNIEnv *env, jclass clazz,
|
||||
jstring group) {
|
||||
UNUSED(clazz);
|
||||
|
||||
return Master::runWithContext<jobject>(env, [&](Master::Context *context) -> jobject {
|
||||
const char *g = context->getString(group);
|
||||
|
||||
auto *ctx = new deferContext();
|
||||
|
||||
ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture());
|
||||
|
||||
ccall_t onSuccess = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextSuccess
|
||||
};
|
||||
ccall_t onFailure = {
|
||||
.context = ctx,
|
||||
.function = &downloadContextFailure
|
||||
};
|
||||
|
||||
performHealthCheck(g, onSuccess, onFailure);
|
||||
|
||||
context->releaseString(group, g);
|
||||
|
||||
return ctx->completableFuture;
|
||||
});
|
||||
}
|
||||
+133
-13
@@ -2,23 +2,45 @@
|
||||
|
||||
Master *Master::master = nullptr;
|
||||
|
||||
Master::Master(JNIEnv *env) {
|
||||
Master::Master(JavaVM *vm, JNIEnv *env): vm(vm) {
|
||||
master = this;
|
||||
|
||||
cClashException = env->FindClass("com/github/kr328/clash/core/bridge/ClashException");
|
||||
cTraffic = env->FindClass("com/github/kr328/clash/core/model/Traffic");
|
||||
cGeneral = env->FindClass("com/github/kr328/clash/core/model/General");
|
||||
cCompletableFuture = env->FindClass("java/util/concurrent/CompletableFuture");
|
||||
cProxyGroup = env->FindClass("com/github/kr328/clash/core/model/ProxyGroup");
|
||||
cProxy = env->FindClass("com/github/kr328/clash/core/model/Proxy");
|
||||
iTunCallback = env->FindClass("com/github/kr328/clash/core/bridge/TunCallback");
|
||||
cClashExceptionConstructor = env->GetMethodID(cClashException, "<init>",
|
||||
"(Ljava/lang/String;)V");
|
||||
cTrafficConstructor = env->GetMethodID(cTraffic, "<init>", "(JJ)V");
|
||||
cGeneralConstructor = env->GetMethodID(cGeneral, "<init>", "(Ljava/lang/String;IIII)V");
|
||||
cCompletableFutureConstructor = env->GetMethodID(cCompletableFuture, "<init>", "()V");
|
||||
cProxyGroupConstructor = env->GetMethodID(cProxyGroup, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Lcom/github/kr328/clash/core/model/Proxy;)V");
|
||||
cProxyConstructor = env->GetMethodID(cProxy, "<init>", "(Ljava/lang/String;Ljava/lang/String;J)V");
|
||||
mCompletableFutureComplete = env->GetMethodID(cCompletableFuture, "complete",
|
||||
"(Ljava/lang/Object;)Z");
|
||||
mCompletableFutureCompleteExceptionally = env->GetMethodID(cCompletableFuture,
|
||||
"completeExceptionally",
|
||||
"(Ljava/lang/Throwable;)Z");
|
||||
mTunCallbackOnNewSocket = env->GetMethodID(iTunCallback, "onNewSocket", "(I)V");
|
||||
mTunCallbackOnStop = env->GetMethodID(iTunCallback, "onStop", "()V");
|
||||
|
||||
sDirect = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Direct")));
|
||||
sReject = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Reject")));
|
||||
sShadowsocks = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Shadowsocks")));
|
||||
sSnell = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Snell")));
|
||||
sSocks5 = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Socks5")));
|
||||
sHttp = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Http")));
|
||||
sVmess = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Vmess")));
|
||||
sTrojan = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Trojan")));
|
||||
sRelay = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Relay")));
|
||||
sSelector = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Selector")));
|
||||
sFallback = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Fallback")));
|
||||
sURLTest = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("URLTest")));
|
||||
sLoadBalance = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("LoadBalance")));
|
||||
sUnknown = reinterpret_cast<jstring>(env->NewGlobalRef(env->NewStringUTF("Unknown")));
|
||||
}
|
||||
|
||||
Master::Context::Context(JNIEnv *env) {
|
||||
@@ -69,17 +91,6 @@ Master::Context::completeExceptionallyCompletableFuture(jobject completable, jth
|
||||
throwable);
|
||||
}
|
||||
|
||||
buffer_t Master::Context::createBufferFromByteArray(jbyteArray array) {
|
||||
return {
|
||||
.buffer = env->GetByteArrayElements(array, nullptr),
|
||||
.length = env->GetArrayLength(array)
|
||||
};
|
||||
}
|
||||
|
||||
void Master::Context::releaseBufferFromByteArray(jbyteArray array, buffer_t &buffer) {
|
||||
env->ReleaseByteArrayElements(array, reinterpret_cast<jbyte*>(buffer.buffer), JNI_COMMIT);
|
||||
}
|
||||
|
||||
const_buffer_t Master::Context::createConstBufferFromByteArray(jbyteArray array) {
|
||||
return {
|
||||
.buffer = env->GetByteArrayElements(array, nullptr),
|
||||
@@ -99,6 +110,115 @@ void Master::Context::releaseString(jstring str, const char *c) {
|
||||
env->ReleaseStringUTFChars(str, c);
|
||||
}
|
||||
|
||||
void Master::Context::tunCallbackNewSocket(jobject callback, int fd) {
|
||||
env->CallVoidMethod(callback, master->mTunCallbackOnNewSocket, fd);
|
||||
}
|
||||
|
||||
void Master::Context::tunCallbackStop(jobject callback) {
|
||||
env->CallVoidMethod(callback, master->mTunCallbackOnStop);
|
||||
}
|
||||
|
||||
jobjectArray Master::Context::createProxyGroupArray(int size, jobject elements[]) {
|
||||
jobjectArray result = env->NewObjectArray(size, master->cProxyGroup, nullptr);
|
||||
|
||||
for ( int i = 0 ; i < size ; i++ )
|
||||
env->SetObjectArrayElement(result, i, elements[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
jobjectArray Master::Context::createProxyArray(int size, jobject elements[]) {
|
||||
jobjectArray result = env->NewObjectArray(size, master->cProxy, nullptr);
|
||||
|
||||
for ( int i = 0 ; i < size ; i++ )
|
||||
env->SetObjectArrayElement(result, i, elements[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
jobject Master::Context::createProxy(std::string const &name, proxy_type_t type, long delay) {
|
||||
jstring ts = nullptr;
|
||||
|
||||
switch (type) {
|
||||
case Direct:
|
||||
ts = master->sDirect;
|
||||
break;
|
||||
case Reject:
|
||||
ts = master->sReject;
|
||||
break;
|
||||
case Socks5:
|
||||
ts = master->sSocks5;
|
||||
break;
|
||||
case Http:
|
||||
ts = master->sHttp;
|
||||
break;
|
||||
case Shadowsocks:
|
||||
ts = master->sShadowsocks;
|
||||
break;
|
||||
case Vmess:
|
||||
ts = master->sVmess;
|
||||
break;
|
||||
case Snell:
|
||||
ts = master->sSnell;
|
||||
break;
|
||||
case Trojan:
|
||||
ts = master->sTrojan;
|
||||
break;
|
||||
case Selector:
|
||||
ts = master->sSelector;
|
||||
break;
|
||||
case Fallback:
|
||||
ts = master->sFallback;
|
||||
break;
|
||||
case LoadBalance:
|
||||
ts = master->sLoadBalance;
|
||||
break;
|
||||
case URLTest:
|
||||
ts = master->sURLTest;
|
||||
break;
|
||||
case Relay:
|
||||
ts = master->sRelay;
|
||||
break;
|
||||
case Unknown:
|
||||
ts = master->sUnknown;
|
||||
break;
|
||||
default:
|
||||
ts = master->sUnknown;
|
||||
}
|
||||
|
||||
return env->NewObject(master->cProxy, master->cProxyConstructor, env->NewStringUTF(name.c_str()), ts, delay);
|
||||
}
|
||||
|
||||
jobject Master::Context::createProxyGroup(std::string const &name, proxy_type_t type,
|
||||
std::string const ¤t, jobjectArray proxies) {
|
||||
jstring ts = nullptr;
|
||||
|
||||
switch (type) {
|
||||
case Selector:
|
||||
ts = master->sSelector;
|
||||
break;
|
||||
case Fallback:
|
||||
ts = master->sFallback;
|
||||
break;
|
||||
case LoadBalance:
|
||||
ts = master->sLoadBalance;
|
||||
break;
|
||||
case URLTest:
|
||||
ts = master->sURLTest;
|
||||
break;
|
||||
case Relay:
|
||||
ts = master->sRelay;
|
||||
break;
|
||||
case Unknown:
|
||||
ts = master->sUnknown;
|
||||
break;
|
||||
default:
|
||||
ts = master->sUnknown;
|
||||
}
|
||||
|
||||
return env->NewObject(master->cProxyGroup, master->cProxyGroupConstructor, env->NewStringUTF(name.c_str()), ts, env->NewStringUTF(current.c_str()), proxies);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
|
||||
UNUSED(unused);
|
||||
|
||||
@@ -107,7 +227,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
||||
return -1;
|
||||
|
||||
new Master(env);
|
||||
new Master(vm, env);
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
@@ -11,23 +11,49 @@ public:
|
||||
class Context;
|
||||
|
||||
public:
|
||||
Master(JNIEnv *env);
|
||||
Master(JavaVM *vm, JNIEnv *env);
|
||||
|
||||
public:
|
||||
template <class R>
|
||||
static R runWithContext(JNIEnv *env, const std::function<R (Context *)>& func);
|
||||
template <class R> static R runWithContext(JNIEnv *env, const std::function<R (Context *)>& func);
|
||||
template <class R> static R runWithAttached(const std::function<R (JNIEnv *)> &func);
|
||||
|
||||
private:
|
||||
jclass cClashException;
|
||||
jclass cTraffic;
|
||||
jclass cGeneral;
|
||||
jclass cCompletableFuture;
|
||||
jclass cProxyGroup;
|
||||
jclass cProxy;
|
||||
jclass iTunCallback;
|
||||
jmethodID cClashExceptionConstructor;
|
||||
jmethodID cTrafficConstructor;
|
||||
jmethodID cGeneralConstructor;
|
||||
jmethodID cCompletableFutureConstructor;
|
||||
jmethodID cProxyGroupConstructor;
|
||||
jmethodID cProxyConstructor;
|
||||
jmethodID mCompletableFutureComplete;
|
||||
jmethodID mCompletableFutureCompleteExceptionally;
|
||||
jmethodID mTunCallbackOnNewSocket;
|
||||
jmethodID mTunCallbackOnStop;
|
||||
|
||||
private:
|
||||
jstring sDirect;
|
||||
jstring sReject;
|
||||
jstring sShadowsocks;
|
||||
jstring sSnell;
|
||||
jstring sSocks5;
|
||||
jstring sHttp;
|
||||
jstring sVmess;
|
||||
jstring sTrojan;
|
||||
jstring sRelay;
|
||||
jstring sSelector;
|
||||
jstring sFallback;
|
||||
jstring sURLTest;
|
||||
jstring sLoadBalance;
|
||||
jstring sUnknown;
|
||||
|
||||
private:
|
||||
JavaVM *vm;
|
||||
|
||||
private:
|
||||
static Master *master;
|
||||
@@ -37,6 +63,7 @@ private:
|
||||
};
|
||||
|
||||
class Master::Context {
|
||||
public:
|
||||
public:
|
||||
Context(JNIEnv *env);
|
||||
|
||||
@@ -56,10 +83,14 @@ public:
|
||||
public:
|
||||
bool completeCompletableFuture(jobject completable, jobject object);
|
||||
bool completeExceptionallyCompletableFuture(jobject completable, jthrowable throwable);
|
||||
void tunCallbackNewSocket(jobject callback, int fd);
|
||||
void tunCallbackStop(jobject callback);
|
||||
|
||||
public:
|
||||
buffer_t createBufferFromByteArray(jbyteArray array);
|
||||
void releaseBufferFromByteArray(jbyteArray array, buffer_t &buffer);
|
||||
jobject createProxy(std::string const &name, proxy_type_t type, long delay);
|
||||
jobject createProxyGroup(std::string const &name, proxy_type_t type, std::string const ¤t, jobjectArray proxies);
|
||||
jobjectArray createProxyArray(int size, jobject elements[]);
|
||||
jobjectArray createProxyGroupArray(int size, jobject elements[]);
|
||||
const_buffer_t createConstBufferFromByteArray(jbyteArray array);
|
||||
void releaseConstBufferFromByteArray(jbyteArray array, const_buffer_t &buffer);
|
||||
const char *getString(jstring str);
|
||||
@@ -74,4 +105,18 @@ R Master::runWithContext(JNIEnv *env, const std::function<R (Master::Context *)>
|
||||
Master::Context context(env);
|
||||
|
||||
return func(&context);
|
||||
}
|
||||
|
||||
template <class R> R Master::runWithAttached(const std::function<R (JNIEnv *)> &func) {
|
||||
Master *m = Master::master;
|
||||
|
||||
JNIEnv *env;
|
||||
|
||||
m->vm->AttachCurrentThread(&env, nullptr);
|
||||
|
||||
R result = func(env);
|
||||
|
||||
m->vm->DetachCurrentThread();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,2 +1,68 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_setProxyMode(JNIEnv *env, jclass clazz,
|
||||
jstring proxy_mode) {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
const char *m = context->getString(proxy_mode);
|
||||
int mode;
|
||||
|
||||
if ( strcmp(m, "Direct") == 0 )
|
||||
mode = MODE_DIRECT;
|
||||
else if ( strcmp(m, "Global") == 0 )
|
||||
mode = MODE_GLOBAL;
|
||||
else if ( strcmp(m, "Rule") == 0 )
|
||||
mode = MODE_RULE;
|
||||
else if ( strcmp(m, "Script") == 0 )
|
||||
mode = MODE_SCRIPT;
|
||||
else
|
||||
mode = MODE_UNKNOWN;
|
||||
|
||||
setProxyMode(mode);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_setDnsOverride(JNIEnv *env, jclass clazz,
|
||||
jboolean override_dns,
|
||||
jstring append_dns) {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
const char *appendDns = context->getString(append_dns);
|
||||
int override = 1;
|
||||
|
||||
if ( override_dns )
|
||||
override = 1;
|
||||
else
|
||||
override = 0;
|
||||
|
||||
dns_override_t dns = {
|
||||
.override_dns = override,
|
||||
.append_dns = appendDns
|
||||
};
|
||||
|
||||
setDnsOverride(&dns);
|
||||
|
||||
context->releaseString(append_dns, appendDns);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_setSelector(JNIEnv *env, jclass clazz, jstring group,
|
||||
jstring selected) {
|
||||
UNUSED(clazz);
|
||||
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
const char *g = context->getString(group);
|
||||
const char *s = context->getString(selected);
|
||||
|
||||
setSelector(g, s);
|
||||
|
||||
context->releaseString(group, g);
|
||||
context->releaseString(selected, s);
|
||||
});
|
||||
}
|
||||
@@ -59,4 +59,51 @@ Java_com_github_kr328_clash_core_bridge_Bridge_querySpeed(JNIEnv *env, jclass cl
|
||||
|
||||
return context->newTraffic(traffic.upload, traffic.download);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_queryProxyGroups(JNIEnv *env, jclass clazz) {
|
||||
UNUSED(clazz);
|
||||
|
||||
return Master::runWithContext<jobjectArray>(env, [&](Master::Context *context) -> jobjectArray {
|
||||
proxy_group_list_t *list = queryProxyGroups();
|
||||
auto *jgroups = new jobject[list->size];
|
||||
|
||||
for (int group_index = 0 ; group_index < list->size ; group_index++ ) {
|
||||
std::string now;
|
||||
proxy_group_t *group = list->groups[group_index];
|
||||
auto *jproxies = new jobject[group->proxies_size];
|
||||
|
||||
for ( int proxy_index = 0 ; proxy_index < group->proxies_size ; proxy_index++ ) {
|
||||
proxy_t *proxy = &group->proxies[proxy_index];
|
||||
|
||||
jproxies[proxy_index] = context->createProxy(proxy->name, proxy->proxy_type, proxy->delay);
|
||||
|
||||
if ( proxy_index == group->now )
|
||||
now = proxy->name;
|
||||
|
||||
free(proxy->name);
|
||||
}
|
||||
|
||||
jgroups[group_index] = context->createProxyGroup(
|
||||
group->base.name,
|
||||
group->base.proxy_type,
|
||||
now,
|
||||
context->createProxyArray(group->proxies_size, jproxies));
|
||||
|
||||
delete[] jproxies;
|
||||
|
||||
free(group->base.name);
|
||||
free(group);
|
||||
}
|
||||
|
||||
jobjectArray result = context->createProxyGroupArray(list->size, jgroups);
|
||||
|
||||
delete[] jgroups;
|
||||
|
||||
free(list);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "main.h"
|
||||
|
||||
struct tunContext {
|
||||
jobject callback;
|
||||
};
|
||||
|
||||
static void tunContextOnNewSocket(void *context, void *argument) {
|
||||
auto *ctx = reinterpret_cast<tunContext*>(context);
|
||||
auto fd = reinterpret_cast<long>(argument);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
context->tunCallbackNewSocket(ctx->callback, fd);
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
static void tunContextOnStop(void *context, void *argument) {
|
||||
UNUSED(argument);
|
||||
|
||||
auto *ctx = reinterpret_cast<tunContext*>(context);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
context->tunCallbackStop(ctx->callback);
|
||||
context->removeGlobalReference(ctx->callback);
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_startTunDevice(JNIEnv *env, jclass clazz, jint fd,
|
||||
jint mtu, jstring gateway,
|
||||
jstring mirror, jstring dns,
|
||||
jobject callback) {
|
||||
UNUSED(clazz);
|
||||
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
const char *gatewayString = context->getString(gateway);
|
||||
const char *mirrorString = context->getString(mirror);
|
||||
const char *dnsString = context->getString(dns);
|
||||
|
||||
jobject callbackGlobal = context->newGlobalReference(callback);
|
||||
auto *callbackContext = new tunContext();
|
||||
|
||||
callbackContext->callback = callbackGlobal;
|
||||
|
||||
ccall_t onNewSocket = {
|
||||
.function = &tunContextOnNewSocket,
|
||||
.context = callbackContext
|
||||
};
|
||||
ccall_t onStop = {
|
||||
.function = &tunContextOnStop,
|
||||
.context = callbackContext
|
||||
};
|
||||
|
||||
char *exception = startTunDevice(fd, mtu, gatewayString, mirrorString, dnsString, onNewSocket, onStop);
|
||||
|
||||
context->releaseString(gateway, gatewayString);
|
||||
context->releaseString(mirror, mirrorString);
|
||||
context->releaseString(dns, dnsString);
|
||||
|
||||
if ( exception != nullptr ) {
|
||||
context->throwThrowable(context->newClashException(exception));
|
||||
context->removeGlobalReference(callbackContext->callback);
|
||||
free(exception);
|
||||
delete callbackContext;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_stopTunDevice(JNIEnv *env, jclass clazz) {
|
||||
stopTunDevice();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package bridge
|
||||
|
||||
type DoneCallback interface {
|
||||
Done()
|
||||
DoneWithError(error)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/hub/executor"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type TunnelGeneral struct {
|
||||
Mode string
|
||||
HTTPPort int
|
||||
SocksPort int
|
||||
RedirectPort int
|
||||
}
|
||||
|
||||
func QueryGeneral() *TunnelGeneral {
|
||||
result := &TunnelGeneral{}
|
||||
|
||||
g := executor.GetGeneral()
|
||||
m := tunnel.Mode()
|
||||
|
||||
result.Mode = m.String()
|
||||
result.HTTPPort = g.Port
|
||||
result.SocksPort = g.SocksPort
|
||||
result.RedirectPort = g.RedirPort
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func SetProxyMode(mode string) {
|
||||
switch mode {
|
||||
case "Direct":
|
||||
tunnel.SetMode(tunnel.Direct)
|
||||
case "Global":
|
||||
tunnel.SetMode(tunnel.Global)
|
||||
case "Rule":
|
||||
tunnel.SetMode(tunnel.Rule)
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/component/mmdb"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/kr328/cfa/config"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
logCallback LogCallback
|
||||
logSubscribe sync.Once
|
||||
)
|
||||
|
||||
type LogCallback interface {
|
||||
OnLogEvent(level, payload string)
|
||||
}
|
||||
|
||||
func InitCore(geoipDatabase[] byte, homeDir string, version string) {
|
||||
dataClone := make([]byte, len(geoipDatabase))
|
||||
copy(dataClone, geoipDatabase)
|
||||
|
||||
mmdb.LoadFromBytes(dataClone)
|
||||
C.SetHomeDir(homeDir)
|
||||
config.ApplicationVersion = version
|
||||
|
||||
Reset()
|
||||
|
||||
log.Infoln("Initialed")
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
config.LoadDefault()
|
||||
tunnel.DefaultManager.ResetStatistic()
|
||||
}
|
||||
|
||||
func SetLogCallback(callback LogCallback) {
|
||||
logSubscribe.Do(func() {
|
||||
go func() {
|
||||
sub := log.Subscribe()
|
||||
defer log.UnSubscribe(sub)
|
||||
|
||||
for {
|
||||
elm := <-sub
|
||||
l := elm.(*log.Event)
|
||||
|
||||
if l.LogLevel < log.Level() {
|
||||
continue
|
||||
}
|
||||
|
||||
if cb := logCallback; cb != nil {
|
||||
cb.OnLogEvent(l.LogLevel.String(), l.Payload)
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
logCallback = callback
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kr328/cfa/config"
|
||||
)
|
||||
|
||||
func ResetDnsAppend(dns string) {
|
||||
if len(dns) == 0 {
|
||||
config.NameServersAppend = make([]string, 0)
|
||||
} else {
|
||||
config.NameServersAppend = strings.Split(dns, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func SetDnsOverrideEnabled(enabled bool) {
|
||||
if enabled {
|
||||
config.DnsPatch = config.OptionalDnsPatch
|
||||
} else {
|
||||
config.DnsPatch = nil
|
||||
}
|
||||
}
|
||||
|
||||
func LoadProfileFile(path, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(config.LoadFromFile(path, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
func DownloadProfileAndCheck(url, output, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(config.PullRemote(url, output, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
func ReadProfileAndCheck(fd int, output, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(config.PullLocal(fd, output, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
func call(err error, callback DoneCallback) {
|
||||
if err != nil {
|
||||
callback.DoneWithError(err)
|
||||
} else {
|
||||
callback.Done()
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type ProxyItem struct {
|
||||
Name string
|
||||
Type string
|
||||
Delay int
|
||||
}
|
||||
|
||||
type ProxyGroupItem struct {
|
||||
Name string
|
||||
Type string
|
||||
Current string
|
||||
Delay int
|
||||
|
||||
providers []provider.ProxyProvider
|
||||
}
|
||||
|
||||
type ProxyGroupCollection interface {
|
||||
Add(proxy *ProxyGroupItem) bool
|
||||
}
|
||||
|
||||
type ProxyCollection interface {
|
||||
Add(proxy *ProxyItem) bool
|
||||
}
|
||||
|
||||
func (p *ProxyGroupItem) QueryAllProxies(collection ProxyCollection) {
|
||||
for _, v := range p.providers {
|
||||
for _, p := range v.Proxies() {
|
||||
collection.Add(
|
||||
&ProxyItem{
|
||||
Name: p.Name(),
|
||||
Type: p.Type().String(),
|
||||
Delay: int(p.LastDelay()),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StartUrlTest(group string, callback DoneCallback) {
|
||||
go func() {
|
||||
defer callback.Done()
|
||||
|
||||
p := tunnel.Proxies()[group]
|
||||
|
||||
pi, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
group, ok := pi.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
providers := group.GetProxyProviders()
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(providers))
|
||||
|
||||
for _, v := range providers {
|
||||
go func(p provider.ProxyProvider) {
|
||||
p.HealthCheck()
|
||||
wg.Done()
|
||||
}(v)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func QueryAllProxyGroups(collection ProxyGroupCollection) {
|
||||
ps := tunnel.Proxies()
|
||||
|
||||
for _, p := range ps {
|
||||
pi, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
group, ok := pi.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
collection.Add(&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: group.Now(),
|
||||
Delay: 0,
|
||||
providers: group.GetProxyProviders(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func SetSelectedProxy(name, proxy string) bool {
|
||||
p := tunnel.Proxies()[name]
|
||||
if p == nil {
|
||||
log.Infoln("Set %s: Not such proxy group", name)
|
||||
return false
|
||||
}
|
||||
|
||||
pb, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
log.Infoln("Set %s: Not a proxy object", name)
|
||||
return false
|
||||
}
|
||||
|
||||
selector, ok := pb.ProxyAdapter.(*outboundgroup.Selector)
|
||||
if !ok {
|
||||
log.Infoln("Set %s: Not a selector group", name)
|
||||
return false
|
||||
}
|
||||
|
||||
selected := selector.Now()
|
||||
if selected == proxy {
|
||||
log.Infoln("Set " + name + " -> " + proxy)
|
||||
return true
|
||||
}
|
||||
|
||||
if err := selector.Set(proxy); err != nil {
|
||||
log.Infoln("Set %s: %s", name, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
for _, conn := range tunnel.DefaultManager.Snapshot().Connections {
|
||||
for _, p := range conn.Chain() {
|
||||
if p == name {
|
||||
_ = conn.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Infoln("Set " + name + " -> " + proxy)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type EventPoll struct {
|
||||
stop sync.Once
|
||||
|
||||
onStop func()
|
||||
}
|
||||
|
||||
func (e *EventPoll) Stop() {
|
||||
e.stop.Do(func() {
|
||||
e.onStop()
|
||||
})
|
||||
}
|
||||
|
||||
type Traffic struct {
|
||||
Download int64
|
||||
Upload int64
|
||||
}
|
||||
|
||||
type Logs interface {
|
||||
OnEvent(level, payload string)
|
||||
}
|
||||
|
||||
func QueryBandwidth() *Traffic {
|
||||
upload := tunnel.DefaultManager.UploadTotal()
|
||||
download := tunnel.DefaultManager.DownloadTotal()
|
||||
|
||||
return &Traffic{
|
||||
Upload: upload,
|
||||
Download: download,
|
||||
}
|
||||
}
|
||||
|
||||
func QueryTraffic() *Traffic {
|
||||
up, down := tunnel.DefaultManager.Now()
|
||||
|
||||
return &Traffic{
|
||||
Upload: up,
|
||||
Download: down,
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/kr328/cfa/tun"
|
||||
)
|
||||
|
||||
type TunCallback interface {
|
||||
OnCreateSocket(fd int)
|
||||
OnStop()
|
||||
}
|
||||
|
||||
var callback TunCallback
|
||||
|
||||
func init() {
|
||||
dialer.DialerHook = onNewDialer
|
||||
dialer.ListenConfigHook = onNewListenConfig
|
||||
}
|
||||
|
||||
func onNewDialer(dialer *net.Dialer) error {
|
||||
dialer.Control = onNewSocket
|
||||
return nil
|
||||
}
|
||||
|
||||
func onNewListenConfig(listen *net.ListenConfig) error {
|
||||
listen.Control = onNewSocket
|
||||
return nil
|
||||
}
|
||||
|
||||
func onNewSocket(_, _ string, c syscall.RawConn) error {
|
||||
if cb := callback; cb != nil {
|
||||
_ = c.Control(func(fd uintptr) {
|
||||
cb.OnCreateSocket(int(fd))
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartTunDevice(fd, mtu int, gateway, mirror, dns string, cb TunCallback) error {
|
||||
callback = cb
|
||||
|
||||
return tun.StartTunDevice(fd, mtu, gateway, mirror, dns)
|
||||
}
|
||||
|
||||
func StopTunDevice() {
|
||||
tun.StopTunDevice()
|
||||
|
||||
if c := callback; c != nil {
|
||||
c.OnStop()
|
||||
}
|
||||
|
||||
callback = nil
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void nextCcall(ccall_t *call, void **argument);
|
||||
extern void nextCcall(ccall_t *call, void **argument, uint64_t *index);
|
||||
extern void finishCcall();
|
||||
|
||||
static void *run(void *arg) {
|
||||
@@ -12,20 +13,23 @@ static void *run(void *arg) {
|
||||
while ( 1 ) {
|
||||
ccall_t call;
|
||||
void *argument;
|
||||
uint64_t index;
|
||||
|
||||
nextCcall(&call, &argument);
|
||||
nextCcall(&call, &argument, &index);
|
||||
|
||||
call.function(call.context, argument);
|
||||
|
||||
finishCcall();
|
||||
finishCcall(index);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void initialize_ccall() {
|
||||
pthread_t tid;
|
||||
void initialize_ccall(int pool_size) {
|
||||
for ( int i = 0 ; i < pool_size ; i++ ) {
|
||||
pthread_t tid = 0;
|
||||
|
||||
if ( pthread_create(&tid, NULL, &run, NULL) < 0 )
|
||||
abort();
|
||||
if ( pthread_create(&tid, NULL, &run, NULL) < 0 )
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,15 @@ package main
|
||||
/*
|
||||
#include "ccall.h"
|
||||
|
||||
extern void initialize_ccall();
|
||||
extern void initialize_ccall(int pool_size);
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type nativeCcall C.ccall_t
|
||||
|
||||
@@ -17,10 +22,11 @@ type ccallContext struct {
|
||||
}
|
||||
|
||||
var ccallQueue chan *ccallContext
|
||||
var current *ccallContext
|
||||
var ccallMap sync.Map
|
||||
var currentIndex uint64
|
||||
|
||||
func init() {
|
||||
C.initialize_ccall()
|
||||
C.initialize_ccall(C.int(runtime.NumCPU()))
|
||||
}
|
||||
|
||||
func callCcall(call nativeCcall, argument unsafe.Pointer) {
|
||||
@@ -36,22 +42,22 @@ func callCcall(call nativeCcall, argument unsafe.Pointer) {
|
||||
}
|
||||
|
||||
//export nextCcall
|
||||
func nextCcall(ccall *nativeCcall, argument *unsafe.Pointer) {
|
||||
func nextCcall(ccall *nativeCcall, argument *unsafe.Pointer, index *uint64) {
|
||||
ctx := <- ccallQueue
|
||||
|
||||
current = ctx
|
||||
|
||||
*index = atomic.AddUint64(¤tIndex, 1)
|
||||
*ccall = ctx.call
|
||||
*argument = ctx.argument
|
||||
|
||||
ccallMap.Store(*index, ctx)
|
||||
}
|
||||
|
||||
//export finishCcall
|
||||
func finishCcall() {
|
||||
ctx := current
|
||||
|
||||
current = nil
|
||||
|
||||
if ctx != nil {
|
||||
ctx.result <- struct{}{}
|
||||
func finishCcall(index uint64) {
|
||||
ctx, ok := ccallMap.Load(index)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.(*ccallContext).result <- struct{}{}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
//#include "config.h"
|
||||
//#include "buffer.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
@@ -10,18 +11,18 @@ import (
|
||||
|
||||
//export setDnsOverride
|
||||
func setDnsOverride(override *C.dns_override_t) {
|
||||
dnsOverride := override.override_dns != 0
|
||||
appendNameservers := C.GoString(override.append_nameservers)
|
||||
overrideDns := override.override_dns != 0
|
||||
appendDns := C.GoString(override.append_dns)
|
||||
|
||||
if dnsOverride {
|
||||
if overrideDns {
|
||||
config.DnsPatch = config.OptionalDnsPatch
|
||||
} else {
|
||||
config.DnsPatch = nil
|
||||
}
|
||||
|
||||
if len(appendNameservers) == 0 {
|
||||
if len(appendDns) == 0 {
|
||||
config.NameServersAppend = make([]string, 0)
|
||||
} else {
|
||||
config.NameServersAppend = strings.Split(appendNameservers, ",")
|
||||
config.NameServersAppend = strings.Split(appendDns, ",")
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ extern "C" {
|
||||
|
||||
typedef struct dns_override_t {
|
||||
int override_dns;
|
||||
const char *append_nameservers;
|
||||
const char *append_dns;
|
||||
} dns_override_t;
|
||||
|
||||
#if __cplusplus
|
||||
|
||||
@@ -78,7 +78,7 @@ func fetchLocal(fd int) ([]byte, error) {
|
||||
return ioutil.ReadAll(file)
|
||||
}
|
||||
|
||||
func PullRemote(url, output, baseDir string) error {
|
||||
func DownloadUrl(url, output, baseDir string) error {
|
||||
data, err := fetchRemote(url)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -87,7 +87,7 @@ func PullRemote(url, output, baseDir string) error {
|
||||
return save(data, output, baseDir)
|
||||
}
|
||||
|
||||
func PullLocal(fd int, output, baseDir string) error {
|
||||
func DownloadFd(fd int, output, baseDir string) error {
|
||||
data, err := fetchLocal(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/kr328/cfa/config"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//#include "buffer.h"
|
||||
import "C"
|
||||
|
||||
//export downloadProfileFromFd
|
||||
func downloadProfileFromFd(fd int, base C.const_string_t, output C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) {
|
||||
b := C.GoString(base)
|
||||
o := C.GoString(output)
|
||||
|
||||
go func() {
|
||||
err := config.DownloadFd(fd, o, b)
|
||||
if err != nil {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString(err.Error())))
|
||||
} else {
|
||||
callCcall(onSuccess, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//export downloadProfileFromUrl
|
||||
func downloadProfileFromUrl(url C.const_string_t, base C.const_string_t, output C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) {
|
||||
u := C.GoString(url)
|
||||
b := C.GoString(base)
|
||||
o := C.GoString(output)
|
||||
|
||||
go func() {
|
||||
err := config.DownloadUrl(u, o, b)
|
||||
if err != nil {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString(err.Error())))
|
||||
} else {
|
||||
callCcall(onSuccess, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//export loadProfile
|
||||
func loadProfile(path C.const_string_t, base C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) {
|
||||
p := C.GoString(path)
|
||||
b := C.GoString(base)
|
||||
|
||||
go func() {
|
||||
err := config.LoadFromFile(p, b)
|
||||
if err != nil {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString(err.Error())))
|
||||
} else {
|
||||
callCcall(onSuccess, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//export performHealthCheck
|
||||
func performHealthCheck(group C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) {
|
||||
g := C.GoString(group)
|
||||
|
||||
go func() {
|
||||
p := tunnel.Proxies()[g]
|
||||
if p == nil {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString("No such proxy group")))
|
||||
|
||||
log.Warnln("Perform health check failure: %s not found", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
pw, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString("Invalid group")))
|
||||
|
||||
log.Warnln("Perform health check failure: %s not valid group", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
adapter, ok := pw.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
callCcall(onFailure, unsafe.Pointer(C.CString("Invalid group")))
|
||||
|
||||
log.Warnln("Perform health check failure: %s not valid group", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
providers := adapter.GetProxyProviders()
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
wg.Add(len(providers))
|
||||
|
||||
for _, p := range providers {
|
||||
go func(p provider.ProxyProvider) {
|
||||
p.HealthCheck()
|
||||
|
||||
wg.Done()
|
||||
}(p)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
callCcall(onSuccess, nil)
|
||||
}()
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static const int MODE_UNKNOWN = -1;
|
||||
static const int MODE_DIRECT = 0;
|
||||
static const int MODE_GLOBAL = 1;
|
||||
static const int MODE_RULE = 2;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <android/log.h>
|
||||
|
||||
#define TAG "ClashForAndroid"
|
||||
|
||||
void log_info(const char *msg) {
|
||||
__android_log_write(ANDROID_LOG_INFO, TAG, msg);
|
||||
}
|
||||
|
||||
void log_error(const char *msg) {
|
||||
__android_log_write(ANDROID_LOG_ERROR, TAG, msg);
|
||||
}
|
||||
|
||||
void log_warn(const char *msg) {
|
||||
__android_log_write(ANDROID_LOG_WARN, TAG, msg);
|
||||
}
|
||||
|
||||
void log_debug(const char *msg) {
|
||||
__android_log_write(ANDROID_LOG_DEBUG, TAG, msg);
|
||||
}
|
||||
|
||||
void log_verbose(const char *msg) {
|
||||
__android_log_write(ANDROID_LOG_VERBOSE, TAG, msg);
|
||||
}
|
||||
|
||||
@@ -3,15 +3,66 @@ package main
|
||||
import (
|
||||
"github.com/Dreamacro/clash/component/mmdb"
|
||||
"github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/kr328/cfa/config"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//#include "buffer.h"
|
||||
/*
|
||||
#cgo CFLAGS: -O3
|
||||
#cgo LDFLAGS: -llog
|
||||
#include "buffer.h"
|
||||
#include "malloc.h"
|
||||
|
||||
extern void log_info(const char *msg);
|
||||
extern void log_error(const char *msg);
|
||||
extern void log_warn(const char *msg);
|
||||
extern void log_debug(const char *msg);
|
||||
extern void log_verbose(const char *msg);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func main() {}
|
||||
|
||||
func init() {
|
||||
r := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
sub := log.Subscribe()
|
||||
defer log.UnSubscribe(sub)
|
||||
|
||||
for item := range sub {
|
||||
msg := item.(*log.Event)
|
||||
|
||||
if msg.LogLevel < log.Level() {
|
||||
continue
|
||||
}
|
||||
|
||||
cPayload := C.CString(msg.Payload)
|
||||
|
||||
switch msg.LogLevel {
|
||||
case log.INFO:
|
||||
C.log_info(cPayload)
|
||||
case log.ERROR:
|
||||
C.log_error(cPayload)
|
||||
case log.WARNING:
|
||||
C.log_warn(cPayload)
|
||||
case log.DEBUG:
|
||||
C.log_debug(cPayload)
|
||||
case log.SILENT:
|
||||
C.log_verbose(cPayload)
|
||||
}
|
||||
|
||||
C.free(unsafe.Pointer(cPayload))
|
||||
}
|
||||
|
||||
r <- struct{}{}
|
||||
}()
|
||||
|
||||
<- r
|
||||
}
|
||||
|
||||
//export initialize
|
||||
func initialize(database *C.const_buffer_t, home, version C.const_string_t) {
|
||||
databaseData := C.GoBytes(database.buffer, database.length)
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -Werror
|
||||
#include "buffer.h"
|
||||
#include "proxies.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static proxy_group_list_t *new_proxy_group_list_t(int size) {
|
||||
proxy_group_list_t *result = malloc(sizeof(proxy_group_list_t) + size * sizeof(proxy_group_t*));
|
||||
|
||||
result->size = size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static proxy_group_t *new_proxy_group_t(int proxies_size) {
|
||||
proxy_group_t *result = malloc(sizeof(proxy_group_t) + proxies_size * sizeof(proxy_t));
|
||||
|
||||
result->proxies_size = proxies_size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static proxy_t *proxy_group_get_proxy(proxy_group_t *group, int index) {
|
||||
return &group->proxies[index];
|
||||
}
|
||||
|
||||
static void proxy_group_list_set(proxy_group_list_t *list, int index, proxy_group_t *group) {
|
||||
list->groups[index] = group;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
//export setSelector
|
||||
func setSelector(group C.const_string_t, selected C.const_string_t) {
|
||||
g := C.GoString(group)
|
||||
s := C.GoString(selected)
|
||||
|
||||
p := tunnel.Proxies()[g]
|
||||
if p == nil {
|
||||
log.Warnln("Set selector failure: %s not found", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
pw, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
log.Warnln("Set selector failure: %s not valid group", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
adapter, ok := pw.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
log.Warnln("Set selector failure: %s not valid group", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
selector, ok := adapter.(*outboundgroup.Selector)
|
||||
if !ok {
|
||||
log.Warnln("Set selector failure: %s not selector", g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := selector.Set(s); err != nil {
|
||||
log.Warnln("Set selector failure: %s not in %s", s, g)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Infoln("Set %s -> %s", g, s)
|
||||
}
|
||||
|
||||
//export queryProxyGroups
|
||||
func queryProxyGroups() *C.proxy_group_list_t {
|
||||
proxies := tunnel.Proxies()
|
||||
groups := make([]outboundgroup.ProxyGroup, 0, len(proxies))
|
||||
|
||||
for _, p := range proxies {
|
||||
pw, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
adapter, ok := pw.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
groups = append(groups, adapter)
|
||||
}
|
||||
|
||||
result := C.new_proxy_group_list_t(C.int(len(groups)))
|
||||
groupIndex := 0
|
||||
for _, group := range groups {
|
||||
ps := make([]constant.Proxy, 0)
|
||||
for _, provider := range group.GetProxyProviders() {
|
||||
ps = append(ps, provider.Proxies()...)
|
||||
}
|
||||
|
||||
g := C.new_proxy_group_t(C.int(len(proxies)))
|
||||
|
||||
g.base.name = C.CString(group.Name())
|
||||
g.base.proxy_type = typeToProxyTypeC(group.Type())
|
||||
g.base.delay = 0
|
||||
|
||||
proxyIndex := 0
|
||||
for _, proxy := range proxies {
|
||||
p := C.proxy_group_get_proxy(g, C.int(proxyIndex))
|
||||
|
||||
p.name = C.CString(proxy.Name())
|
||||
p.proxy_type = typeToProxyTypeC(proxy.Type())
|
||||
p.delay = C.long(proxy.LastDelay())
|
||||
|
||||
if proxy.Name() == group.Now() {
|
||||
g.now = C.int(proxyIndex)
|
||||
}
|
||||
|
||||
proxyIndex++
|
||||
}
|
||||
|
||||
C.proxy_group_list_set(result, C.int(groupIndex), g)
|
||||
|
||||
groupIndex++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func typeToProxyTypeC(t constant.AdapterType) C.proxy_type_t {
|
||||
switch t {
|
||||
case constant.Direct:
|
||||
return C.Direct
|
||||
case constant.Reject:
|
||||
return C.Reject
|
||||
|
||||
case constant.Shadowsocks:
|
||||
return C.Shadowsocks
|
||||
case constant.Snell:
|
||||
return C.Snell
|
||||
case constant.Socks5:
|
||||
return C.Socks5
|
||||
case constant.Http:
|
||||
return C.Http
|
||||
case constant.Vmess:
|
||||
return C.Vmess
|
||||
case constant.Trojan:
|
||||
return C.Trojan
|
||||
|
||||
case constant.Relay:
|
||||
return C.Relay
|
||||
case constant.Selector:
|
||||
return C.Selector
|
||||
case constant.Fallback:
|
||||
return C.Fallback
|
||||
case constant.URLTest:
|
||||
return C.URLTest
|
||||
case constant.LoadBalance:
|
||||
return C.LoadBalance
|
||||
|
||||
default:
|
||||
return C.Unknown
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum proxy_type_t {
|
||||
Direct, Reject, Socks5, Http, Shadowsocks, Vmess, Snell, Trojan, Selector, Fallback, LoadBalance, URLTest, Relay, Unknown
|
||||
} proxy_type_t;
|
||||
|
||||
typedef struct proxy_t {
|
||||
char *name;
|
||||
proxy_type_t proxy_type;
|
||||
long delay;
|
||||
} proxy_t;
|
||||
|
||||
typedef struct proxy_group_t {
|
||||
proxy_t base;
|
||||
int now;
|
||||
int proxies_size;
|
||||
proxy_t proxies[];
|
||||
} proxy_group_t;
|
||||
|
||||
typedef struct proxy_group_list_t {
|
||||
int size;
|
||||
proxy_group_t *groups[];
|
||||
} proxy_group_list_t;
|
||||
|
||||
#if __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -2,22 +2,15 @@ package com.github.kr328.clash.core
|
||||
|
||||
import android.os.ParcelFileDescriptor
|
||||
import com.github.kr328.clash.common.Global
|
||||
import com.github.kr328.clash.common.utils.Log
|
||||
import com.github.kr328.clash.core.bridge.Bridge
|
||||
import com.github.kr328.clash.core.bridge.TunCallback
|
||||
import com.github.kr328.clash.core.event.LogEvent
|
||||
import com.github.kr328.clash.core.model.General
|
||||
import com.github.kr328.clash.core.model.Proxy
|
||||
import com.github.kr328.clash.core.model.ProxyGroup
|
||||
import com.github.kr328.clash.core.model.Traffic
|
||||
import com.github.kr328.clash.core.transact.DoneCallbackImpl
|
||||
import com.github.kr328.clash.core.transact.ProxyCollectionImpl
|
||||
import com.github.kr328.clash.core.transact.ProxyGroupCollectionImpl
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import kotlin.concurrent.thread
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
object Clash {
|
||||
private val logReceivers = mutableMapOf<String, (LogEvent) -> Unit>()
|
||||
@@ -49,31 +42,14 @@ object Clash {
|
||||
onNewSocket: (Int) -> Boolean,
|
||||
onTunStop: () -> Unit
|
||||
) {
|
||||
val sockets = ParcelFileDescriptor.createSocketPair()
|
||||
|
||||
thread {
|
||||
val input = DataInputStream(ParcelFileDescriptor.AutoCloseInputStream(sockets[0]))
|
||||
val output = DataOutputStream(ParcelFileDescriptor.AutoCloseOutputStream(sockets[0]))
|
||||
|
||||
runCatching {
|
||||
while (true) {
|
||||
val s = input.readInt()
|
||||
|
||||
onNewSocket(s)
|
||||
|
||||
output.writeInt(s)
|
||||
}
|
||||
Bridge.startTunDevice(fd, mtu, gateway, mirror, dns, object: TunCallback {
|
||||
override fun onNewSocket(socket: Int) {
|
||||
onNewSocket(socket)
|
||||
}
|
||||
|
||||
onTunStop()
|
||||
|
||||
Log.i("Tun Closed")
|
||||
|
||||
sockets[0].close()
|
||||
sockets[1].close()
|
||||
}
|
||||
|
||||
Bridge.startTunDevice(fd, mtu, gateway, mirror, dns, sockets[1].detachFd())
|
||||
override fun onStop() {
|
||||
onTunStop()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun stopTunDevice() {
|
||||
@@ -84,48 +60,28 @@ object Clash {
|
||||
Bridge.setDnsOverride(dnsOverride, appendNameservers.joinToString(","))
|
||||
}
|
||||
|
||||
fun loadProfile(path: File, baseDir: File): CompletableDeferred<Unit> {
|
||||
return DoneCallbackImpl().apply {
|
||||
Bridge.loadProfileFile(path.absolutePath, baseDir.absolutePath, this)
|
||||
}
|
||||
fun loadProfile(path: File, baseDir: File): CompletableFuture<Unit> {
|
||||
return Bridge.loadProfile(path.absolutePath, baseDir.absolutePath).thenApply { Unit }
|
||||
}
|
||||
|
||||
fun downloadProfile(url: String, output: File, baseDir: File): CompletableDeferred<Unit> {
|
||||
return DoneCallbackImpl().apply {
|
||||
Bridge.downloadProfileAndCheck(url, output.absolutePath, baseDir.absolutePath, this)
|
||||
}
|
||||
fun downloadProfile(url: String, output: File, baseDir: File): CompletableFuture<Unit> {
|
||||
return Bridge.downloadProfile(url, baseDir.absolutePath, output.absolutePath).thenApply { Unit }
|
||||
}
|
||||
|
||||
fun downloadProfile(fd: Int, output: File, baseDir: File): CompletableDeferred<Unit> {
|
||||
return DoneCallbackImpl().apply {
|
||||
Bridge.readProfileAndCheck(fd.toLong(), output.absolutePath, baseDir.absolutePath, this)
|
||||
}
|
||||
fun downloadProfile(fd: ParcelFileDescriptor, output: File, baseDir: File): CompletableFuture<Unit> {
|
||||
return Bridge.downloadProfile(fd.detachFd(), baseDir.absolutePath, output.absolutePath).thenApply { Unit }
|
||||
}
|
||||
|
||||
fun queryProxyGroups(): List<ProxyGroup> {
|
||||
return ProxyGroupCollectionImpl().also { Bridge.queryAllProxyGroups(it) }
|
||||
.filterNotNull()
|
||||
.map { group ->
|
||||
ProxyGroup(group.name,
|
||||
Proxy.Type.fromString(group.type),
|
||||
group.delay,
|
||||
group.current,
|
||||
ProxyCollectionImpl().also { pc ->
|
||||
group.queryAllProxies(pc)
|
||||
}.filterNotNull().map {
|
||||
Proxy(it.name, Proxy.Type.fromString(it.type), it.delay)
|
||||
})
|
||||
}
|
||||
return Bridge.queryProxyGroups().toList()
|
||||
}
|
||||
|
||||
fun setSelectedProxy(name: String, selected: String): Boolean {
|
||||
return Bridge.setSelectedProxy(name, selected)
|
||||
fun setSelector(name: String, selected: String) {
|
||||
return Bridge.setSelector(name, selected)
|
||||
}
|
||||
|
||||
fun startHealthCheck(name: String): CompletableDeferred<Unit> {
|
||||
return DoneCallbackImpl().apply {
|
||||
Bridge.startUrlTest(name, this)
|
||||
}
|
||||
fun performHealthCheck(group: String): CompletableFuture<Unit> {
|
||||
return Bridge.performHealthCheck(group).thenApply { Unit }
|
||||
}
|
||||
|
||||
fun setProxyMode(mode: String) {
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.github.kr328.clash.core.bridge;
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.github.kr328.clash.core.model.General;
|
||||
import com.github.kr328.clash.core.model.ProxyGroup;
|
||||
import com.github.kr328.clash.core.model.Traffic;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Keep
|
||||
public final class Bridge {
|
||||
static {
|
||||
@@ -21,10 +24,18 @@ public final class Bridge {
|
||||
public static native General queryGeneral();
|
||||
public static native Traffic querySpeed();
|
||||
public static native Traffic queryBandwidth();
|
||||
public static native ProxyGroup[] queryProxyGroups();
|
||||
|
||||
public static native void startTunDevice(int fd, int mtu, String gateway, String mirror, String dns, int newSocket) throws ClashException;
|
||||
public static native void startTunDevice(int fd, int mtu, String gateway, String mirror, String dns, TunCallback callback) throws ClashException;
|
||||
public static native void stopTunDevice();
|
||||
|
||||
public static native void setDnsOverride(boolean overrideDns, String appendNameservers);
|
||||
public static native void setProxyMode(String mode);
|
||||
public static native void setSelector(String group, String selected);
|
||||
|
||||
public static native CompletableFuture<Object> downloadProfile(String url, String base, String output);
|
||||
public static native CompletableFuture<Object> downloadProfile(int fd, String base, String output);
|
||||
|
||||
public static native CompletableFuture<Object> loadProfile(String path, String base);
|
||||
public static native CompletableFuture<Object> performHealthCheck(String group);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.kr328.clash.core.bridge;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public interface TunCallback {
|
||||
void onNewSocket(int socket);
|
||||
void onStop();
|
||||
}
|
||||
@@ -4,11 +4,14 @@ import androidx.annotation.Keep
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Proxy @Keep constructor(
|
||||
data class Proxy constructor(
|
||||
val name: String,
|
||||
val type: Type,
|
||||
val delay: Long
|
||||
) {
|
||||
@Keep
|
||||
constructor(name: String, type: String, delay: Long): this(name, Type.fromString(type), delay)
|
||||
|
||||
enum class Type(val text: String, val group: Boolean) {
|
||||
DIRECT("Direct", false),
|
||||
REJECT("Reject", false),
|
||||
|
||||
@@ -7,11 +7,10 @@ import kotlinx.serialization.Serializable
|
||||
data class ProxyGroup(
|
||||
val name: String,
|
||||
val type: Proxy.Type,
|
||||
val delay: Long,
|
||||
val current: String,
|
||||
val proxies: List<Proxy>
|
||||
) {
|
||||
@Keep
|
||||
constructor(name: String, type: String, delay: Long, current: String, proxies: Array<Proxy>) :
|
||||
this(name, Proxy.Type.fromString(type), delay, current, proxies.toList())
|
||||
constructor(name: String, type: String, current: String, proxies: Array<Proxy>) :
|
||||
this(name, Proxy.Type.fromString(type), current, proxies.toList())
|
||||
}
|
||||
+4
-4
@@ -6,7 +6,7 @@ import com.github.kr328.clash.common.serialization.MergedParcels
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ProxyGroupList(val list: List<ProxyGroup>) : Parcelable {
|
||||
data class ProxyGroupWrapper(val list: List<ProxyGroup>) : Parcelable {
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
MergedParcels.dump(serializer(), this, parcel)
|
||||
}
|
||||
@@ -15,12 +15,12 @@ data class ProxyGroupList(val list: List<ProxyGroup>) : Parcelable {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<ProxyGroupList> {
|
||||
override fun createFromParcel(parcel: Parcel): ProxyGroupList {
|
||||
companion object CREATOR : Parcelable.Creator<ProxyGroupWrapper> {
|
||||
override fun createFromParcel(parcel: Parcel): ProxyGroupWrapper {
|
||||
return MergedParcels.load(serializer(), parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ProxyGroupList?> {
|
||||
override fun newArray(size: Int): Array<ProxyGroupWrapper?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.github.kr328.clash.core.transact
|
||||
|
||||
import bridge.DoneCallback
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
|
||||
class DoneCallbackImpl : DoneCallback, CompletableDeferred<Unit> by CompletableDeferred() {
|
||||
override fun doneWithError(e: Exception?) {
|
||||
completeExceptionally(e ?: return done())
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
complete(Unit)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.github.kr328.clash.core.transact
|
||||
|
||||
import bridge.ProxyCollection
|
||||
import bridge.ProxyGroupCollection
|
||||
import bridge.ProxyGroupItem
|
||||
import bridge.ProxyItem
|
||||
import java.util.*
|
||||
|
||||
class ProxyCollectionImpl : LinkedList<ProxyItem?>(), ProxyCollection
|
||||
class ProxyGroupCollectionImpl : LinkedList<ProxyGroupItem?>(), ProxyGroupCollection
|
||||
@@ -2,7 +2,7 @@ package com.github.kr328.clash.service
|
||||
|
||||
import com.github.kr328.clash.core.Clash
|
||||
import com.github.kr328.clash.core.model.General
|
||||
import com.github.kr328.clash.core.model.ProxyGroupList
|
||||
import com.github.kr328.clash.core.model.ProxyGroupWrapper
|
||||
import com.github.kr328.clash.service.data.ProfileDao
|
||||
import com.github.kr328.clash.service.data.SelectedProxyDao
|
||||
import com.github.kr328.clash.service.data.SelectedProxyEntity
|
||||
@@ -18,8 +18,8 @@ class ClashManager(parent: CoroutineScope) :
|
||||
Clash.setProxyMode(requireNotNull(mode))
|
||||
}
|
||||
|
||||
override fun queryAllProxies(): ProxyGroupList {
|
||||
return ProxyGroupList(Clash.queryProxyGroups())
|
||||
override fun queryAllProxies(): ProxyGroupWrapper {
|
||||
return ProxyGroupWrapper(Clash.queryProxyGroups())
|
||||
}
|
||||
|
||||
override fun queryGeneral(): General {
|
||||
@@ -35,7 +35,7 @@ class ClashManager(parent: CoroutineScope) :
|
||||
SelectedProxyDao.setSelectedForProfile(SelectedProxyEntity(current.id, proxy, selected))
|
||||
}
|
||||
|
||||
return Clash.setSelectedProxy(proxy, selected)
|
||||
return Clash.setSelector(proxy, selected)
|
||||
}
|
||||
|
||||
override fun queryBandwidth(): Long {
|
||||
@@ -47,7 +47,7 @@ class ClashManager(parent: CoroutineScope) :
|
||||
override fun startHealthCheck(group: String?, callback: IStreamCallback?) {
|
||||
require(group != null && callback != null)
|
||||
|
||||
Clash.startHealthCheck(group).invokeOnCompletion { u ->
|
||||
Clash.performHealthCheck(group).invokeOnCompletion { u ->
|
||||
if (u != null)
|
||||
callback.completeExceptionally(u.message)
|
||||
else
|
||||
|
||||
@@ -50,7 +50,7 @@ class ReloadModule(private val context: Context) : Module() {
|
||||
).await()
|
||||
|
||||
val remove = SelectedProxyDao.querySelectedForProfile(active.id)
|
||||
.filterNot { Clash.setSelectedProxy(it.proxy, it.selected) }
|
||||
.filterNot { Clash.setSelector(it.proxy, it.selected) }
|
||||
.map { it.selected }
|
||||
|
||||
SelectedProxyDao.removeSelectedForProfile(active.id, remove)
|
||||
|
||||
Reference in new issue
Block a user