Route remaining JNI producers through the core task owner
This commit is contained in:
1 parent
95f0e89567
commit
86f0ac7625
3 files changed
+105
-112
No files matched your search
@@ -66,11 +66,12 @@ UTUN-сервис включается отдельно через `[gui] utun_e
|
||||
существующий запуск; он не подтверждает готовность сервисов. Готовность проверяется
|
||||
по событию и `instance_lite_is_running()`.
|
||||
|
||||
Команды UI обычно копируют аргументы и ставят работу в uasync. При этом C JSON-getters
|
||||
и некоторые настройки читают SQLite/живые структуры синхронно в вызывающем потоке;
|
||||
C `utun_bridge_set_member_nick()` пишет локальный nickname напрямую. C setter chat_setting
|
||||
также синхронный, тогда как JNI nativeSetChatSetting использует trampoline.
|
||||
Нельзя считать весь bridge автоматически выполняющимся в потоке ядра.
|
||||
Команды UI копируют аргументы и передают их владельцу worker. JNI JSON-getters и короткие проверки
|
||||
читают SQLite/живые структуры через `instance_lite_call`; C getters и `utun_bridge_set_member_nick()`
|
||||
исполняются только в потоке владельца. JNI nativeSetChatSetting использует отдельный trampoline.
|
||||
Реестры команд освобождают также PCM/compressor и собственные cache-файлы при отмене до исполнения.
|
||||
JNI строки преобразуются через обычный UTF-8 byte[] в обе стороны: имена, JSON и пути сохраняют emoji.
|
||||
NUL во входной строке отклоняется, ошибки выделения возвращают NULL/отказ, а не усечённую строку.
|
||||
Заимствованные inst/ua не удерживают ресурс; они пригодны только в потоке владельца.
|
||||
`instance_lite_post` публикует команду под защитой времени жизни worker и заполняет её поле instance.
|
||||
Аргумент принадлежит callback, а при отмене — discard; отдельный реестр переживает удаление posted_task.
|
||||
|
||||
@@ -1351,13 +1351,19 @@ char* utun_bridge_get_local_sockets_json(void) {
|
||||
return json;
|
||||
}
|
||||
|
||||
void utun_bridge_on_network_change(void) {
|
||||
static void bridge_network_change_run(void* unused) {
|
||||
(void)unused;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (!inst) return;
|
||||
auto_socket_on_network_change(inst);
|
||||
bridge_log(BLEV_DEBUG, "network change notified to auto_socket");
|
||||
}
|
||||
|
||||
void utun_bridge_on_network_change(void) {
|
||||
if (instance_lite_post(bridge_network_change_run, NULL, NULL, NULL))
|
||||
bridge_log(BLEV_DEBUG, "Network change notification rejected: core not ready");
|
||||
}
|
||||
|
||||
int utun_bridge_set_active(int active) {
|
||||
return instance_lite_set_active(active);
|
||||
}
|
||||
@@ -1739,12 +1745,13 @@ static __thread int g_log_callback_active; /* Callback не может реку
|
||||
static jobject g_event_callback = NULL;
|
||||
static jmethodID g_event_mid = NULL;
|
||||
|
||||
enum bridge_query_kind { QUERY_CHANNELS, QUERY_MESSAGES, QUERY_MEMBERS, QUERY_DETAIL, QUERY_LINKS, QUERY_NODE,
|
||||
enum bridge_query_kind { QUERY_CHANNELS, QUERY_MESSAGES, QUERY_MEMBERS, QUERY_DETAIL, QUERY_LINKS, QUERY_NODE, QUERY_SET_NICK,
|
||||
QUERY_OWNER, QUERY_MEMBERSHIP, QUERY_SETTING, QUERY_SOUND, QUERY_RADIO_SUBSCRIBERS, QUERY_RADIO_START };
|
||||
/* Строки/ID заимствованы до возврата instance_lite_call; JSON становится собственностью JNI caller. */
|
||||
struct bridge_query {
|
||||
enum bridge_query_kind kind;
|
||||
const char* channel;
|
||||
const char* value;
|
||||
const uint8_t* anchor;
|
||||
uint64_t node;
|
||||
int limit, exact;
|
||||
@@ -1760,6 +1767,7 @@ static void bridge_query_run(void* arg) {
|
||||
case QUERY_DETAIL: q->json = utun_bridge_get_member_detail_json(q->channel, q->node); break;
|
||||
case QUERY_LINKS: q->json = utun_bridge_get_member_links_json(q->node); break;
|
||||
case QUERY_NODE: q->number = (int64_t)utun_bridge_get_my_node_id(); break;
|
||||
case QUERY_SET_NICK: utun_bridge_set_member_nick(q->channel, q->node, q->value); break;
|
||||
case QUERY_OWNER: q->number = utun_bridge_is_channel_owner(q->channel); break;
|
||||
case QUERY_MEMBERSHIP: q->number = topo_node_sqlite_member_in_channel(instance_lite_get_instance()->topo_sqlite_db,
|
||||
q->channel, q->node); break;
|
||||
@@ -1774,6 +1782,11 @@ static int bridge_query_call(struct bridge_query* query) {
|
||||
bridge_log(BLEV_WARN, "Core query unavailable/cancelled kind=%d", query->kind); return -1;
|
||||
}
|
||||
|
||||
static uint64_t bridge_my_node_id(void) {
|
||||
struct bridge_query query = {.kind = QUERY_NODE};
|
||||
bridge_query_call(&query); return (uint64_t)query.number;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
(void)reserved;
|
||||
g_jvm = vm;
|
||||
@@ -2172,35 +2185,30 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestInviteLink
|
||||
|
||||
/* ── nativeSetEventCallback(eventCb) ── */
|
||||
|
||||
static void local_devices_run(void* unused) {
|
||||
(void)unused; chat_local_peers_trampoline(instance_lite_get_instance());
|
||||
}
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestLocalDevices(JNIEnv* env, jobject thiz) {
|
||||
(void)env; (void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (!inst || !uasync_is_running(ua)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DISCOVERY, "device list requested while core is stopped"); return JNI_FALSE;
|
||||
}
|
||||
uasync_post(ua, chat_local_peers_trampoline, inst);
|
||||
return JNI_TRUE;
|
||||
return instance_lite_post(local_devices_run, NULL, NULL, NULL) == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeSendLocalInvite(
|
||||
JNIEnv* env, jobject thiz, jlong peer, jlong request, jbyteArray link) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
jsize len = link ? (*env)->GetArrayLength(env, link) : 0;
|
||||
if (!inst || !uasync_is_running(ua) || !peer || !request || len <= 0 || len > LOCAL_TRANSPORT_DATA_MAX) {
|
||||
if (!instance_lite_is_running() || !peer || !request || len <= 0 || len > LOCAL_TRANSPORT_DATA_MAX) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DISCOVERY, "invalid/stopped local send request bytes=%d", (int)len); return JNI_FALSE;
|
||||
}
|
||||
struct chat_local_send_request* req = u_calloc(1, sizeof(*req));
|
||||
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "local send request allocation failed"); return JNI_FALSE; }
|
||||
req->inst = inst; req->peer_id = (uint64_t)peer; req->request_id = (uint64_t)request;
|
||||
req->peer_id = (uint64_t)peer; req->request_id = (uint64_t)request;
|
||||
(*env)->GetByteArrayRegion(env, link, 0, len, (jbyte*)req->link);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "copying local invitation from JNI failed"); u_free(req); return JNI_FALSE;
|
||||
}
|
||||
uasync_post(ua, chat_local_send_trampoline, req);
|
||||
return JNI_TRUE;
|
||||
int posted = bridge_post_request(chat_local_send_trampoline, req, &req->inst);
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetEventCallback(
|
||||
@@ -2266,30 +2274,29 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeSendMessage(
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeEditMessage(
|
||||
JNIEnv* env, jobject thiz, jstring channelId, jbyteArray messageId, jlong version, jbyteArray text, jlong requestId) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync(); uint8_t id[32];
|
||||
if (!uasync_is_running(ua) || !text || !channelId || version <= 0 || !messageId ||
|
||||
uint8_t id[32];
|
||||
if (!instance_lite_is_running() || !text || !channelId || version <= 0 || !messageId ||
|
||||
(*env)->GetArrayLength(env, messageId) != 32 || jni_message_id(env, messageId, id) < 0) {
|
||||
bridge_log(BLEV_ERROR, "edit: unavailable core/invalid target"); return JNI_FALSE;
|
||||
}
|
||||
jsize length = (*env)->GetArrayLength(env, text);
|
||||
if (length <= 0 || length > DB_SYNC_MESSAGE_MAX) { bridge_log(BLEV_ERROR, "edit: invalid bytes=%d", length); return JNI_FALSE; }
|
||||
struct chat_msg_submit* req = u_calloc(1, sizeof(*req) + length);
|
||||
struct posted_task* post = u_calloc(1, sizeof(*post));
|
||||
if (!req || !post) { bridge_log(BLEV_ERROR, "edit: allocation failed"); u_free(req); u_free(post); return JNI_FALSE; }
|
||||
req->inst = instance_lite_get_instance(); req->data = (uint8_t*)(req + 1); req->data_len = length;
|
||||
if (!req) { bridge_log(BLEV_ERROR, "edit: allocation failed"); u_free(req); return JNI_FALSE; }
|
||||
req->data = (uint8_t*)(req + 1); req->data_len = length;
|
||||
req->expected_ver = (uint64_t)version; req->request_id = (uint64_t)requestId; memcpy(req->message_id, id, 32);
|
||||
(*env)->GetByteArrayRegion(env, text, 0, length, (jbyte*)req->data);
|
||||
const char* channel = jni_utf8(env, channelId);
|
||||
if (!channel || (*env)->ExceptionCheck(env) || strlen(channel) >= sizeof(req->channel_id)) {
|
||||
bridge_log(BLEV_ERROR, "edit: JNI copy failed");
|
||||
if (channel) u_free((void*)channel);
|
||||
u_free(req); u_free(post); return JNI_FALSE;
|
||||
u_free(req); return JNI_FALSE;
|
||||
}
|
||||
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel);
|
||||
u_free((void*)channel);
|
||||
post->callback = chat_core_submit_trampoline; post->arg = req; uasync_post_reserved(ua, post);
|
||||
int posted = bridge_post_request(chat_core_submit_trampoline, req, &req->inst);
|
||||
bridge_log(BLEV_INFO, "edit queued ver=%llu request=%llu", (unsigned long long)version, (unsigned long long)requestId);
|
||||
return JNI_TRUE;
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeCreateChannel(
|
||||
@@ -2343,9 +2350,7 @@ JNIEXPORT jstring JNICALL Java_com_utun_chat_data_NativeLib_nativeGetMessages(
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFeedbackCommand(
|
||||
JNIEnv* env, jobject thiz, jbyteArray json, jlong request_id) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (!json || !inst || !uasync_is_running(ua)) {
|
||||
if (!json || !instance_lite_is_running()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "bridge: feedback core unavailable"); return JNI_FALSE;
|
||||
}
|
||||
jsize len = (*env)->GetArrayLength(env, json);
|
||||
@@ -2354,10 +2359,10 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFeedbackComma
|
||||
}
|
||||
struct chat_feedback_request* req = u_malloc(sizeof(*req) + len + 1);
|
||||
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "bridge: feedback allocation failed"); return JNI_FALSE; }
|
||||
req->inst = inst; req->request_id = (uint64_t)request_id;
|
||||
req->request_id = (uint64_t)request_id;
|
||||
(*env)->GetByteArrayRegion(env, json, 0, len, (jbyte*)(req + 1));
|
||||
if ((*env)->ExceptionCheck(env)) { u_free(req); DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "bridge: feedback JNI copy failed"); return JNI_FALSE; }
|
||||
((char*)(req + 1))[len] = 0; uasync_post(ua, chat_feedback_command_trampoline, req); return JNI_TRUE;
|
||||
((char*)(req + 1))[len] = 0; int posted = bridge_post_request(chat_feedback_command_trampoline, req, &req->inst); return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeDmStart(
|
||||
@@ -2392,31 +2397,27 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeDmSend(
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeDmDelete(
|
||||
JNIEnv* env, jobject thiz, jstring conversation, jlong generation, jlong request) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!uasync_is_running(ua) || generation <= 0 || request <= 0) {
|
||||
if (!instance_lite_is_running() || generation <= 0 || request <= 0) {
|
||||
bridge_log(BLEV_WARN, "DM delete: stopped core or invalid generation/request"); return JNI_FALSE;
|
||||
}
|
||||
const char* conv = jni_utf8(env, conversation);
|
||||
struct dm_delete_req* req = u_calloc(1, sizeof(*req));
|
||||
struct posted_task* post = u_calloc(1, sizeof(*post));
|
||||
if (!conv || !req || !post || !conv[0] || strlen(conv) >= sizeof(req->conv_id) || strspn(conv, "0123456789") != strlen(conv)) {
|
||||
if (!conv || !req || !conv[0] || strlen(conv) >= sizeof(req->conv_id) || strspn(conv, "0123456789") != strlen(conv)) {
|
||||
bridge_log(BLEV_ERROR, "DM delete: invalid conversation or allocation failed");
|
||||
if (conv) u_free((void*)conv);
|
||||
u_free(req); u_free(post); return JNI_FALSE;
|
||||
u_free(req); return JNI_FALSE;
|
||||
}
|
||||
req->inst = instance_lite_get_instance(); req->generation = (uint64_t)generation; req->request_id = (uint64_t)request;
|
||||
req->generation = (uint64_t)generation; req->request_id = (uint64_t)request;
|
||||
snprintf(req->conv_id, sizeof(req->conv_id), "%s", conv);
|
||||
u_free((void*)conv);
|
||||
post->callback = dm_delete_trampoline; post->arg = req; uasync_post_reserved(ua, post);
|
||||
return JNI_TRUE;
|
||||
int posted = bridge_post_request(dm_delete_trampoline, req, &req->inst);
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeDmInvite(
|
||||
JNIEnv* env, jobject thiz, jlong request_id, jlong peer, jstring source, jlong channel, jstring name, jboolean cancel) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (!uasync_is_running(ua) || !inst) { bridge_log(BLEV_ERROR, "invite PM: core unavailable"); return JNI_FALSE; }
|
||||
if (!instance_lite_is_running()) { bridge_log(BLEV_ERROR, "invite PM: core unavailable"); return JNI_FALSE; }
|
||||
struct dm_invite_req* req = u_calloc(1, sizeof(*req));
|
||||
if (!req) { bridge_log(BLEV_ERROR, "invite PM: request allocation failed"); return JNI_FALSE; }
|
||||
const char* src = jni_utf8(env, source);
|
||||
@@ -2425,13 +2426,13 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeDmInvite(
|
||||
if (src) u_free((void*)src);
|
||||
bridge_log(BLEV_ERROR, "invite PM: string conversion failed"); u_free(req); return JNI_FALSE;
|
||||
}
|
||||
req->inst = inst; req->request_id = (uint64_t)request_id; req->peer_node_id = (uint64_t)peer;
|
||||
req->request_id = (uint64_t)request_id; req->peer_node_id = (uint64_t)peer;
|
||||
req->channel_id = (uint64_t)channel; req->cancel = cancel ? 1 : 0;
|
||||
snprintf(req->source_ch_id, sizeof(req->source_ch_id), "%s", src);
|
||||
snprintf(req->peer_name, sizeof(req->peer_name), "%s", nm);
|
||||
u_free((void*)src); u_free((void*)nm);
|
||||
uasync_post(ua, dm_invite_trampoline, req);
|
||||
return JNI_TRUE;
|
||||
int posted = bridge_post_request(dm_invite_trampoline, req, &req->inst);
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
/* Чтение членства для выбора групп и открытия уже присоединённого канала. */
|
||||
@@ -2456,15 +2457,14 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeDmRequestConversa
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeDmRequestCallRoute(
|
||||
JNIEnv* env, jobject thiz, jstring convId) {
|
||||
(void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!uasync_is_running(ua)) { bridge_log(BLEV_WARN, "PM call route: core stopped"); return; }
|
||||
if (!instance_lite_is_running()) { bridge_log(BLEV_WARN, "PM call route: core stopped"); return; }
|
||||
const char* conv = jni_utf8(env, convId);
|
||||
if (!conv) return;
|
||||
struct dm_messages_req* req = u_calloc(1, sizeof(*req));
|
||||
if (req) {
|
||||
req->inst = instance_lite_get_instance();
|
||||
|
||||
snprintf(req->conv_id, sizeof(req->conv_id), "%s", conv);
|
||||
uasync_post(ua, dm_call_route_trampoline, req);
|
||||
bridge_post_request(dm_call_route_trampoline, req, &req->inst);
|
||||
} else bridge_log(BLEV_ERROR, "PM call route: allocation failed");
|
||||
u_free((void*)conv);
|
||||
}
|
||||
@@ -2540,17 +2540,13 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativePing(
|
||||
utun_bridge_ping();
|
||||
}
|
||||
|
||||
static void cpu_load_run(void* unused) {
|
||||
(void)unused; chat_core_collect_cpu_load_trampoline(instance_lite_get_instance());
|
||||
}
|
||||
/* GUI запрашивает готовый снимок асинхронно, CPU-часы читает только поток ядра. */
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestCpuLoad(JNIEnv* env, jobject thiz) {
|
||||
(void)env; (void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (!ua || !inst || !instance_lite_is_running()) return JNI_FALSE;
|
||||
struct posted_task* task = u_calloc(1, sizeof(*task));
|
||||
if (!task) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "CPU load request allocation failed"); return JNI_FALSE; }
|
||||
task->callback = chat_core_collect_cpu_load_trampoline; task->arg = inst;
|
||||
uasync_post_reserved(ua, task);
|
||||
return JNI_TRUE;
|
||||
return instance_lite_post(cpu_load_run, NULL, NULL, NULL) == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeConnectChannel(
|
||||
@@ -2600,8 +2596,16 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetChatSetting(
|
||||
(void)thiz;
|
||||
const char* n = jni_utf8(env, name);
|
||||
const char* v = jni_utf8(env, value);
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
if (inst) chat_core_set_setting(inst, n, v);
|
||||
if (n && v) {
|
||||
struct chat_setting_arg* req = u_calloc(1, sizeof(*req));
|
||||
size_t name_size = strlen(n) + 1, value_size = strlen(v) + 1;
|
||||
if (!req || name_size + value_size > sizeof(req->data)) {
|
||||
bridge_log(BLEV_ERROR, "Chat setting rejected: allocation/size name=%s", n); u_free(req);
|
||||
} else {
|
||||
memcpy(req->data, n, name_size); memcpy(req->data + name_size, v, value_size);
|
||||
bridge_post_request(chat_core_set_setting_trampoline, req, &req->inst);
|
||||
}
|
||||
}
|
||||
if (v) u_free((void*)v);
|
||||
if (n) u_free((void*)n);
|
||||
}
|
||||
@@ -2846,9 +2850,7 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeVoiceDecodeClose(
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestMemberList(
|
||||
JNIEnv* env, jobject thiz, jstring channelId) {
|
||||
(void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!inst || !ua || !channelId) {
|
||||
if (!instance_lite_is_running() || !channelId) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "request_member_list: missing instance/uasync/channel"); return JNI_FALSE;
|
||||
}
|
||||
const char* ch = jni_utf8(env, channelId);
|
||||
@@ -2858,12 +2860,11 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestMember
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "request_member_list: allocation/invalid channel ch=%s", ch);
|
||||
u_free(req); u_free((void*)ch); return JNI_FALSE;
|
||||
}
|
||||
req->inst = inst;
|
||||
memcpy(req->ch_id, ch, strlen(ch) + 1);
|
||||
memcpy(req->ch_id, ch, strlen(ch) + 1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT_SYNC, "request_member_list: queued ch=%s", ch);
|
||||
u_free((void*)ch);
|
||||
uasync_post(ua, chat_core_request_member_list_trampoline, req);
|
||||
return JNI_TRUE;
|
||||
int posted = bridge_post_request(chat_core_request_member_list_trampoline, req, &req->inst);
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_utun_chat_data_NativeLib_nativeGetChannelMembers(
|
||||
@@ -2908,7 +2909,10 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetMemberNick(
|
||||
(void)thiz;
|
||||
const char* ch = jni_utf8(env, channelId);
|
||||
const char* nk = nick ? jni_utf8(env, nick) : NULL;
|
||||
utun_bridge_set_member_nick(ch, (uint64_t)nodeId, nk);
|
||||
if (ch && (!nick || nk)) {
|
||||
struct bridge_query query = {.kind = QUERY_SET_NICK, .channel = ch, .node = (uint64_t)nodeId, .value = nk};
|
||||
bridge_query_call(&query);
|
||||
}
|
||||
if (nk) u_free((void*)nk);
|
||||
u_free((void*)ch);
|
||||
}
|
||||
@@ -2976,9 +2980,8 @@ static void local_sockets_trampoline(void* arg) {
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeRequestLocalSockets(
|
||||
JNIEnv* env, jobject thiz) {
|
||||
(void)env; (void)thiz;
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!uasync_is_running(ua)) { bridge_log(BLEV_DEBUG, "local sockets request: core stopped"); return; }
|
||||
uasync_post(ua, local_sockets_trampoline, NULL);
|
||||
if (instance_lite_post(local_sockets_trampoline, NULL, NULL, NULL))
|
||||
bridge_log(BLEV_DEBUG, "local sockets request: core not ready");
|
||||
}
|
||||
|
||||
/* ── Network change notification ── */
|
||||
@@ -3040,21 +3043,17 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeCallAccept(
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRadioCall(
|
||||
JNIEnv* env, jobject thiz, jlong id, jlong group, jlong peer, jint action) {
|
||||
(void)env; (void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!inst || !ua || !uasync_is_running(ua) || !id || action < RADIO_CALL_START || action > RADIO_CALL_CONTINUE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio call JNI rejected id=%lld action=%d core=%p", (long long)id, action, (void*)inst);
|
||||
if (!instance_lite_is_running() || !id || action < RADIO_CALL_START || action > RADIO_CALL_CONTINUE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio call JNI rejected id=%lld action=%d", (long long)id, action);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
struct radio_call_request* req = u_calloc(1, sizeof(*req));
|
||||
struct posted_task* task = u_calloc(1, sizeof(*task));
|
||||
if (!req || !task) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio call JNI allocation failed"); u_free(req); u_free(task); return JNI_FALSE;
|
||||
if (!req) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio call JNI allocation failed"); u_free(req); return JNI_FALSE;
|
||||
}
|
||||
req->inst = inst; req->id = (uint64_t)id; req->group = (uint64_t)group; req->peer = (uint64_t)peer; req->action = action;
|
||||
task->callback = radio_call_trampoline; task->arg = req;
|
||||
uasync_post_reserved(ua, task);
|
||||
return JNI_TRUE;
|
||||
req->id = (uint64_t)id; req->group = (uint64_t)group; req->peer = (uint64_t)peer; req->action = action;
|
||||
int posted = bridge_post_request(radio_call_trampoline, req, &req->inst);
|
||||
return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeCallDecline(
|
||||
@@ -3369,15 +3368,14 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeAvatarOperati
|
||||
JNIEnv* env, jobject thiz, jint op, jlong id, jlong group, jlong server, jlong owner,
|
||||
jstring remote, jstring local, jbyteArray data) {
|
||||
(void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!inst || !ua || !uasync_is_running(ua) || !id || (!group && op != 7) || op < 0 || op > 8) {
|
||||
if (!instance_lite_is_running() || !id || (!group && op != 7) || op < 0 || op > 8) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CHAT, "Android avatar: unavailable/invalid operation=%d id=%lld group=%llx", op,
|
||||
(long long)id, (unsigned long long)group); return JNI_FALSE;
|
||||
}
|
||||
const char* path = remote ? jni_utf8(env, remote) : NULL;
|
||||
const char* file = local ? jni_utf8(env, local) : NULL;
|
||||
void* req = NULL; uasync_post_callback_t callback = NULL;
|
||||
struct UTUN_INSTANCE** instance_field = NULL;
|
||||
if ((remote && !path) || (local && !file)) goto invalid;
|
||||
if (((op >= 1 && op <= 4) || op == 8) && (!server || !owner || !path ||
|
||||
(op == 3 || op == 8 ? strcmp(path, "avatars") != 0 :
|
||||
@@ -3385,7 +3383,7 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeAvatarOperati
|
||||
if (op == 0 || op == 6) {
|
||||
struct group_avatar_gui_request* r = u_calloc(1, sizeof(*r)); req = r;
|
||||
if (!r) goto invalid;
|
||||
r->inst = inst; r->id = id; r->group = group;
|
||||
instance_field = &r->inst; r->id = id; r->group = group;
|
||||
if (op == 6) {
|
||||
jsize size = data ? (*env)->GetArrayLength(env, data) : 0;
|
||||
if (size > GROUP_AVATAR_MAX) goto invalid;
|
||||
@@ -3398,7 +3396,7 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeAvatarOperati
|
||||
if (!file || !file[0] || strlen(file) > MF_PATH_MAX) goto invalid;
|
||||
struct mf_transfer_request* r = u_calloc(1, sizeof(*r)); req = r;
|
||||
if (!r) goto invalid;
|
||||
r->inst = inst; r->id = id; r->group = group; r->server = server; r->owner = owner;
|
||||
instance_field = &r->inst; r->id = id; r->group = group; r->server = server; r->owner = owner;
|
||||
uint8_t limit[8];
|
||||
if (!data || (*env)->GetArrayLength(env, data) != 8) goto invalid;
|
||||
(*env)->GetByteArrayRegion(env, data, 0, 8, (jbyte*)limit);
|
||||
@@ -3410,7 +3408,7 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeAvatarOperati
|
||||
} else if (op == 3 || op == 4 || op == 8) {
|
||||
struct mf_gui_request* r = u_calloc(1, sizeof(*r)); req = r;
|
||||
if (!r) goto invalid;
|
||||
r->inst = inst; r->group = group; r->server = server;
|
||||
instance_field = &r->inst; r->group = group; r->server = server;
|
||||
r->message.kind = MF_REQUEST; r->message.command = op == 3 ? MF_MKDIR : op == 8 ? MF_LIST : MF_STAT;
|
||||
r->message.request_id = id; r->message.owner = owner;
|
||||
if (op == 8) {
|
||||
@@ -3425,19 +3423,19 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeAvatarOperati
|
||||
if (server && (!path || !mf_avatar_manifest_valid(path))) goto invalid;
|
||||
struct chat_avatar_req* r = u_calloc(1, sizeof(*r)); req = r;
|
||||
if (!r) goto invalid;
|
||||
r->inst = inst; r->id = id; r->group = group; r->server = server;
|
||||
instance_field = &r->inst; r->id = id; r->group = group; r->server = server;
|
||||
if (server) memcpy(r->file, path, strlen(path) + 1);
|
||||
callback = chat_core_set_my_avatar_trampoline;
|
||||
} else {
|
||||
struct mf_cancel_request* r = u_calloc(1, sizeof(*r)); req = r;
|
||||
if (!r) goto invalid;
|
||||
r->inst = inst; r->id = id; callback = member_files_gui_cancel;
|
||||
instance_field = &r->inst; r->id = id; callback = member_files_gui_cancel;
|
||||
}
|
||||
if (path) u_free((void*)path);
|
||||
if (file) u_free((void*)file);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT, "Android avatar: queued operation=%d id=%lld group=%016llx server=%016llx owner=%016llx",
|
||||
op, (long long)id, (unsigned long long)group, (unsigned long long)server, (unsigned long long)owner);
|
||||
uasync_post(ua, callback, req); return JNI_TRUE;
|
||||
return bridge_post_request(callback, req, instance_field) == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
invalid:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "Android avatar: invalid arguments/allocation operation=%d id=%lld group=%016llx",
|
||||
op, (long long)id, (unsigned long long)group);
|
||||
@@ -3450,11 +3448,9 @@ invalid:
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileRequest(
|
||||
JNIEnv* env, jobject thiz, jlong group, jlong server, jbyteArray wire) {
|
||||
(void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
jsize size = wire ? (*env)->GetArrayLength(env, wire) : 0;
|
||||
struct mf_gui_request* q = NULL;
|
||||
if (!inst || !ua || !uasync_is_running(ua) || !group || !server || size < MF_WIRE_HEADER || size > MF_WIRE_MAX) goto invalid;
|
||||
if (!instance_lite_is_running() || !group || !server || size < MF_WIRE_HEADER || size > MF_WIRE_MAX) goto invalid;
|
||||
uint8_t bytes[MF_WIRE_MAX];
|
||||
(*env)->GetByteArrayRegion(env, wire, 0, size, (jbyte*)bytes);
|
||||
if ((*env)->ExceptionCheck(env)) goto invalid;
|
||||
@@ -3466,17 +3462,17 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileRequest(
|
||||
if (m->kind != MF_REQUEST || (!read && !write) || m->data_len || m->offset || m->size || m->more ||
|
||||
!mf_path_valid(m->path, read) || (!m->owner && (m->command != MF_LIST || m->path[0])) ||
|
||||
(m->command == MF_RENAME ? !mf_path_valid(m->target, 0) : m->target[0] != 0)) goto invalid;
|
||||
if (write && (m->owner != inst->node_id ||
|
||||
if (write && (m->owner != bridge_my_node_id() ||
|
||||
(!strncasecmp(m->path, "avatars", 7) && (!m->path[7] || m->path[7] == '/')))) goto invalid;
|
||||
if (m->command == MF_RENAME && !strncasecmp(m->target, "avatars", 7) && (!m->target[7] || m->target[7] == '/')) goto invalid;
|
||||
q->inst = inst; q->group = (uint64_t)group; q->server = (uint64_t)server;
|
||||
q->group = (uint64_t)group; q->server = (uint64_t)server;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "Android files: request id=%llu cmd=%s group=%llu server=%llu owner=%llu path=%s",
|
||||
(unsigned long long)m->request_id, mf_command_text(m->command), (unsigned long long)group,
|
||||
(unsigned long long)server, (unsigned long long)m->owner, m->path);
|
||||
uasync_post(ua, member_files_gui_request, q); return JNI_TRUE;
|
||||
int posted = bridge_post_request(member_files_gui_request, q, &q->inst); return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
invalid:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: request rejected group=%llu server=%llu bytes=%d ready=%d",
|
||||
(unsigned long long)group, (unsigned long long)server, (int)size, inst && ua && uasync_is_running(ua));
|
||||
(unsigned long long)group, (unsigned long long)server, (int)size, instance_lite_is_running());
|
||||
u_free(q); return JNI_FALSE;
|
||||
}
|
||||
|
||||
@@ -3494,22 +3490,20 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileTransfer(
|
||||
JNIEnv* env, jobject thiz, jlong id, jlong group, jlong server, jlong owner,
|
||||
jbyteArray remote, jbyteArray local, jboolean upload, jbyteArray version) {
|
||||
(void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
struct mf_transfer_request* q = NULL;
|
||||
if (!inst || !ua || !uasync_is_running(ua) || !id || !group || !server || !owner ||
|
||||
if (!instance_lite_is_running() || !id || !group || !server || !owner ||
|
||||
!version || (*env)->GetArrayLength(env, version) != 16) goto invalid;
|
||||
q = u_calloc(1, sizeof(*q));
|
||||
if (!q || android_file_path(env, remote, q->remote) || android_file_path(env, local, q->local) ||
|
||||
!mf_path_valid(q->remote, 0) || q->local[0] != '/') goto invalid;
|
||||
(*env)->GetByteArrayRegion(env, version, 0, 16, (jbyte*)q->version);
|
||||
if ((*env)->ExceptionCheck(env) || (!upload && mf_zero(q->version, 16))) goto invalid;
|
||||
if (upload && (owner != (jlong)inst->node_id ||
|
||||
if (upload && (owner != (jlong)bridge_my_node_id() ||
|
||||
(!strncasecmp(q->remote, "avatars", 7) && (!q->remote[7] || q->remote[7] == '/')))) goto invalid;
|
||||
q->inst = inst; q->id = id; q->group = group; q->server = server; q->owner = owner; q->upload = upload != JNI_FALSE;
|
||||
q->id = id; q->group = group; q->server = server; q->owner = owner; q->upload = upload != JNI_FALSE;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "Android files: transfer queued id=%llu group=%llu server=%llu owner=%llu upload=%d path=%s",
|
||||
(unsigned long long)id, (unsigned long long)group, (unsigned long long)server, (unsigned long long)owner, q->upload, q->remote);
|
||||
uasync_post(ua, member_files_gui_transfer, q); return JNI_TRUE;
|
||||
int posted = bridge_post_request(member_files_gui_transfer, q, &q->inst); return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
invalid:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: transfer rejected id=%llu group=%llu server=%llu owner=%llu upload=%d",
|
||||
(unsigned long long)id, (unsigned long long)group, (unsigned long long)server, (unsigned long long)owner, upload);
|
||||
@@ -3519,16 +3513,14 @@ invalid:
|
||||
/* Отмена по уникальному ID не затрагивает передачи других владельцев. */
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeFileCancel(JNIEnv* env, jobject thiz, jlong id) {
|
||||
(void)env; (void)thiz;
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!id || !inst || !ua || !uasync_is_running(ua)) {
|
||||
if (!id || !instance_lite_is_running()) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "Android files: cancel unavailable id=%llu", (unsigned long long)id); return JNI_FALSE;
|
||||
}
|
||||
struct mf_cancel_request* q = u_calloc(1, sizeof(*q));
|
||||
if (!q) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "Android files: cancel allocation failed"); return JNI_FALSE; }
|
||||
q->inst = inst; q->id = id;
|
||||
q->id = id;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "Android files: cancel queued id=%llu", (unsigned long long)id);
|
||||
uasync_post(ua, member_files_gui_cancel, q); return JNI_TRUE;
|
||||
int posted = bridge_post_request(member_files_gui_cancel, q, &q->inst); return posted == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
#endif /* __ANDROID__ */
|
||||
@@ -174,7 +174,7 @@ int utun_bridge_call_hangup(uint64_t call_id);
|
||||
|
||||
/* Аудио-движок звонка (call_audio.c, единый голосовой стек): PCM из AudioRecord →
|
||||
* Opus → peer (feed); принятый Opus → джиттер+декод+stretch → PCM (pull). */
|
||||
/* GUI-поток: начать DSP после готовности ядра; call_audio уже привязан его worker. 0 = успех, -1 = ошибка. */
|
||||
/* Начать DSP после готовности ядра; call_audio уже привязан его worker. 0 = успех, -1 = ошибка. */
|
||||
int utun_bridge_call_audio_start(uint64_t call_id);
|
||||
/* Capture worker: настоящий raw PCM, ровно 960 mono-сэмплов @48кГц; muted!=0 применяется после AEC/AGC.
|
||||
* capture_pts_us — монотонная метка первого сэмпла либо INT64_MIN. 0 = поставлено в uasync, -1 = отказ/ошибка. */
|
||||
@@ -188,7 +188,7 @@ void utun_bridge_call_audio_stop(void);
|
||||
|
||||
void utun_bridge_radio_set_active(uint64_t group_id, int on);
|
||||
int utun_bridge_radio_subscribers(uint64_t group_id);
|
||||
/* Создать/привязать DSP и начать рацию: capture_channels=0 только RX, 1/2 RX+TX. 0/-1.
|
||||
/* В потоке ядра создать/привязать DSP и начать рацию: capture_channels=0 только RX, 1/2 RX+TX. 0/-1.
|
||||
* Один TX writer для feed/talk_begin/end и отдельный RX writer для pull; lifecycle сериализует frontend. */
|
||||
int utun_bridge_radio_audio_start(uint64_t group_id, int capture_channels);
|
||||
/* PCM 48kHz int16 interleaved, count — число int16, кратное числу каналов; длина произвольная. См. radio_audio.h. */
|
||||
|
||||
Reference in new issue
Block a user