Verify Android DM commit results and document sensor cleanup retry

This commit is contained in:
evgeny committed 2026-10-29 13:34:25 +01:00
1 parent 96166cd3a7
commit f5405cdb3d
2 files changed
+52

No files matched your search

+44
View File
@@ -8,6 +8,8 @@
#include "../src/utun_instance.h"
#include "../src/chat/chat_core.h"
#include "../src/chat/chat_event.h"
#include "../src/dm/dm_core.h"
#include "../src/transport_layer/secure_channel.h"
#include "../src/media_async/attachment_send.h"
#include "../src/media_async/encrypted_file.h"
#include "../src/routing_layer/topo_node_sqlite.h"
@@ -191,6 +193,47 @@ static int check_local_sockets(void) {
u_free(json); return 0;
}
/* Android PM ждёт commit, а отказ очереди не создаёт ожидание события; текст сохраняет обычный UTF-8. */
static int check_dm_submissions(void) {
struct SC_MYKEYS peer; uint8_t ed[32];
REQUIRE(!sc_generate_keypair(&peer) && !sc_derive_ed25519_pubkey(peer.private_key, ed));
uint64_t node = sc_derive_node_id_from_pubkey(peer.public_key);
sqlite3_stmt* st = NULL;
REQUIRE(sqlite3_prepare_v2(instance->topo_sqlite_db,
"INSERT INTO nodes(node_id,x25519_pubkey,ed25519_pubkey,name) VALUES(?,?,?,'Android PM peer')", -1, &st, NULL) == SQLITE_OK);
sqlite3_bind_int64(st, 1, (sqlite3_int64)node);
sqlite3_bind_blob(st, 2, peer.public_key, 32, SQLITE_STATIC); sqlite3_bind_blob(st, 3, ed, 32, SQLITE_STATIC);
int rc = sqlite3_step(st); sqlite3_finalize(st); REQUIRE(rc == SQLITE_DONE);
REQUIRE(!dm_start(instance, node, peer.public_key, ed, "Android PM peer", NULL));
char conv[64], before[8192], after[8192], value[256]; size_t length = 0;
snprintf(conv, sizeof(conv), "%llu", (unsigned long long)dm_derive_conv_id(instance->node_id, node));
uint64_t generation = dm_history_generation(instance, conv); REQUIRE(generation);
const uint8_t text[] = "Привет 😀 \"PM\"\nследующая строка";
REQUIRE(!utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 11) && wait_result(11) == 1);
REQUIRE(!dm_list_messages_json(instance, conv, 20, 0, before, sizeof(before), &length));
REQUIRE(!extract(before, "$[0].data", value, sizeof(value)) && !strcmp(value, (const char*)text));
REQUIRE(!utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation + 1, 12) && wait_result(12) == -1);
REQUIRE(sqlite3_exec(instance->topo_sqlite_db, "CREATE TEMP TRIGGER android_dm_write_fail BEFORE INSERT ON dm_outbox"
" BEGIN SELECT RAISE(FAIL,'android fixture write failure'); END", NULL, NULL, NULL) == SQLITE_OK);
REQUIRE(!utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 13) && wait_result(13) == -1);
REQUIRE(sqlite3_exec(instance->topo_sqlite_db, "DROP TRIGGER android_dm_write_fail", NULL, NULL, NULL) == SQLITE_OK);
REQUIRE(!dm_list_messages_json(instance, conv, 20, 0, after, sizeof(after), &length) && !strcmp(before, after));
REQUIRE(!utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 14) && wait_result(14) == 1);
const uint8_t embedded_nul[] = {'a', 0, 'b'};
REQUIRE(utun_bridge_dm_send(conv, embedded_nul, sizeof(embedded_nul), generation, 15) == -1);
REQUIRE(utun_bridge_dm_send(conv, text, 0, generation, 15) == -1);
REQUIRE(utun_bridge_dm_send(conv, text, sizeof(text) - 1, 0, 15) == -1);
REQUIRE(utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 0) == -1);
uint8_t oversized[DM_DATA_MAX + 1] = {0};
REQUIRE(utun_bridge_dm_send(conv, oversized, sizeof(oversized), generation, 16) == -1);
struct UTUN_INSTANCE* saved = instance; instance = NULL;
rc = utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 17); instance = saved;
REQUIRE(rc == -1);
uasync_poll(async, 0); REQUIRE(!results[15] && !results[16] && !results[17]);
printf("[PASS] Android PM: UTF-8, commit ACK, stale generation, SQL rollback/retry and rejected requests\n");
return 0;
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
char directory[512] = "/tmp/utun_android_replies_XXXXXX", config[1024], channel[64], hex[65];
@@ -206,6 +249,7 @@ int main(void) {
REQUIRE(!extract(json, "$[0].id", channel, sizeof(channel))); u_free(json);
REQUIRE(!check_addresses(channel));
REQUIRE(!check_local_sockets());
REQUIRE(!check_dm_submissions());
uint8_t empty[32] = {0}, original[32], missing[32]; memset(missing, 0xff, sizeof(missing));
REQUIRE(!utun_bridge_send_message(channel, "Привет 😀 \"original\"\nnext line", empty, 1) && wait_result(1) == 1);
json = utun_bridge_get_messages_json(channel, 1, NULL, 0);
@@ -245,6 +245,9 @@ ConversationComposer сохраняет draft, URI и тип вложения ч
показывается ресурсом интерфейса; ошибка cleanup логируется отдельно.
SAF export в канале и PM сообщает об отказе запуска picker/записи. Null output stream
не считается успешным сохранением; ожидающий путь переживает пересоздание Activity.
PM text передаётся JNI как обычные UTF-8 bytes, generation и ненулевой request ID.
Успешный enqueue не очищает draft: composer ждёт MSG_SUBMITTED после транзакции истории/outbox.
При stale generation/SQL error draft остаётся, отказ очереди завершает ожидание сразу.
Звонок и рация используют общий C голосовой стек. Kotlin владеет AudioRecord/AudioTrack,
feed/pull workers и маршрутом Android Telecom; DSP и сетевой сигналинг разделены.
@@ -283,6 +286,11 @@ target. Detach/close продолжают освобождение незави
через 250 мс..30 с. При отказе Handler повторный release завершает её явно.
`CallAudioRouter` сохраняет device callback и каждый полученный Bluetooth proxy до
успешного unregister/close. ProximityManager также сохраняет прежний sensor listener
и его manager при отказе unregister: start ждёт cleanup, Handler повторяет его через
250 мс..30 с, повторный stop работает после отказа таймера. Старая регистрация и
повреждённые показания не дают ложного FAR; после lookup failure start повторяет поиск.
CallAudioRouter ждёт
успешного unregister/close. stop инвалидирует callbacks и снимки устройств сразу,
очищает независимые ресурсы и бросает ошибку при неподтверждённом результате.
Собственные communication device/SCO-заявки отмечаются до API и снимаются отдельно;