From 067c1cd496f7befb639924ff84312e909c8a914c Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 21:23:18 +0100 Subject: [PATCH] Own queued Android phone state data within the worker lifetime --- .../jni_bridge/android_jni_bridge.c | 20 +-------- .../libutun_lite/instance_lite.c | 44 +++++++++++++++++++ .../libutun_lite/instance_lite.h | 6 +++ 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 65fc6474..e9136256 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -1370,26 +1370,8 @@ int utun_bridge_set_active(int active) { return instance_lite_set_active(active); } -struct phone_state_request { struct UTUN_INSTANCE* inst; int state; unsigned policy; }; -static void phone_state_execute(void* arg) { - struct phone_state_request* req = arg; - call_set_phone_state(req->inst, req->state, req->policy); u_free(req); -} - int utun_bridge_set_phone_state(int state, unsigned policy) { - struct UTUN_INSTANCE* inst = instance_lite_get_instance(); - struct UASYNC* ua = instance_lite_get_uasync(); - if (!inst || !ua || !uasync_is_running(ua) || state < 0 || state > 2 || policy > 31) { - DEBUG_WARN(DEBUG_CATEGORY_CALL, "phone state JNI unavailable state=%d policy=%u", state, policy); return -1; - } - struct phone_state_request* req = u_calloc(1, sizeof(*req)); - struct posted_task* task = u_calloc(1, sizeof(*task)); - if (!req || !task) { - DEBUG_ERROR(DEBUG_CATEGORY_CALL, "phone state JNI allocation failed"); u_free(req); u_free(task); return -1; - } - req->inst = inst; req->state = state; req->policy = policy; - task->callback = phone_state_execute; task->arg = req; uasync_post_reserved(ua, task); - return 0; + return instance_lite_set_phone_state(state, policy); } static void bridge_notify_network_active_trampoline(void* arg) { diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index 103d205c..a182655a 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -31,6 +31,7 @@ #include "../jni_bridge/android_jni_bridge.h" #include "standby.h" #include "stream_bridge.h" +#include "call/call.h" #include "call/call_video_port.h" #ifdef UTUN_WITH_VOICE #include "call/call_audio.h" @@ -716,6 +717,49 @@ int instance_lite_set_active(int active) { return 0; } +/* posted — первое поле: uasync освобождает всё выделение и после callback, и при отмене очереди. */ +struct phone_state_task { + struct posted_task posted; + int state; + unsigned policy; +}; + +/* Только worker: request заимствован у исполняемой задачи, instance берём в момент применения. */ +static void set_phone_state_trampoline(void* arg) { + const struct phone_state_task* request = arg; + struct UTUN_INSTANCE* inst = __atomic_load_n(&g_inst, __ATOMIC_ACQUIRE); + if (!inst || __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) { + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "Android phone state command cancelled: worker is stopping"); return; + } + call_set_phone_state(inst, request->state, request->policy); + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "Android phone state applied state=%d policy=%u", request->state, request->policy); +} + +int instance_lite_set_phone_state(int state, unsigned policy) { + if (state < 0 || state > 2 || policy > 31) { + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "Android phone state request invalid state=%d policy=%u", state, policy); return -1; + } + if (g_is_worker) { + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "Android phone state request refused: frontend thread required"); return -1; + } + pthread_mutex_lock(&g_stop_mutex); + struct UASYNC* ua = __atomic_load_n(&g_ua, __ATOMIC_ACQUIRE); + if (!ua || !__atomic_load_n(&g_running, __ATOMIC_ACQUIRE) || __atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) { + pthread_mutex_unlock(&g_stop_mutex); + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "Android phone state request rejected: core is not ready"); return -1; + } + struct phone_state_task* request = u_malloc(sizeof(*request)); + if (!request) { + pthread_mutex_unlock(&g_stop_mutex); + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "Android phone state request allocation failed"); return -1; + } + request->state = state; request->policy = policy; + request->posted.callback = set_phone_state_trampoline; request->posted.arg = request; + uasync_post_reserved(ua, &request->posted); + pthread_mutex_unlock(&g_stop_mutex); + return 0; +} + /* ── Лёгкое обновление сокетов (auto_sockets=android) ── */ static void update_sockets_trampoline(void* arg) { diff --git a/tools/chatgui-android/libutun_lite/instance_lite.h b/tools/chatgui-android/libutun_lite/instance_lite.h index d53c4ba0..c5db5651 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.h +++ b/tools/chatgui-android/libutun_lite/instance_lite.h @@ -71,6 +71,12 @@ int instance_lite_regenerate_keys(const char* config_text); * При stop ожидающая команда может отмениться. Результат применения/stream hold логируется в потоке ядра. */ int instance_lite_set_active(int active); +/* Frontend: поставить состояние SIM (0 UNKNOWN/1 IDLE/2 BUSY) и маску call_policy (0..31) живому worker. + * 0 — данные скопированы в задачу под защитой времени жизни uasync; -1 — неверные аргументы/поток, неготовое ядро или OOM. + * Применение асинхронно в текущем ядре; stop/restart может отменить ожидающую команду, освободив её данные. + * После SERVICE_STARTED frontend повторно отправляет актуальные state/policy. */ +int instance_lite_set_phone_state(int state, unsigned policy); + /* Копирует INI, будит poll-loop и пересоздаёт instance и uasync внутри worker. * До остановки проверяет конфиг и сохраняет отсутствующие ключи; отказ оставляет прежнее ядро. * При инициализации ставит запрос в очередь; при завершившемся worker делает join/start.