Request high BLE priority for incoming Android links on the shared ACL

This commit is contained in:
evgeny committed 2026-10-29 19:58:40 +01:00
1 parent 7dab663b93
commit 8ab25c1389
2 files changed
+45 -5

No files matched your search

+9 -2
View File
@@ -1,5 +1,5 @@
/* Реальное Android↔desktop радио. Android instrumentation включает только тестовый echo.
* ble_peer_probe [connect|listen] [seconds]; 96 пакетов, TX saturation, побайтовая сверка. */
* ble_peer_probe [connect|listen] [seconds] [peer_hex]; 96 пакетов, TX saturation, побайтовая сверка. */
#include "ble_transport.h"
#include "ble_identity.h"
#include "u_async.h"
@@ -7,11 +7,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
static struct ble_transport* transport;
static ble_link_id session;
static unsigned sent, received_count, blocked;
static int failed, dial, seen, radio_ready;
static uint64_t peer, started, verified_bytes;
static uint64_t peer, expected_peer, started, verified_bytes;
static const unsigned lengths[] = {1,13,14,20,244,512,1499,1500};
static unsigned length(unsigned n) { return lengths[n % 8]; }
static uint8_t probe_byte(unsigned n, unsigned i) { return (uint8_t)(n + i * 37); }
@@ -58,6 +59,7 @@ static void status(void* arg, enum ble_state state, const char* reason) {
static void broadcast(void* arg, const struct ble_endpoint* ep, const uint8_t* data, size_t len, int rssi) {
(void)arg; struct ble_identity identity;
if (ble_identity_decode(&identity, data, len) < 0) return;
if (expected_peer && identity.node_id != expected_peer) return;
if (!seen) printf("DISCOVER node=%016llx name=%s rssi=%d endpoint=%s\n",
(unsigned long long)identity.node_id, identity.name, rssi, ep->id);
seen = 1;
@@ -66,7 +68,12 @@ static void broadcast(void* arg, const struct ble_endpoint* ep, const uint8_t* d
int main(int argc, char** argv) {
dial = argc < 2 || !strcmp(argv[1], "connect");
int seconds = argc > 2 ? atoi(argv[2]) : 90;
if (argc > 3) {
char* end; errno = 0; expected_peer = strtoull(argv[3], &end, 16);
if (errno || !expected_peer || !argv[3][0] || *end) { fprintf(stderr, "Invalid peer_hex\n"); return 2; }
}
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); debug_set_category_level(DEBUG_CATEGORY_DISCOVERY, DEBUG_LEVEL_DEBUG);
if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_TRACE);
struct UASYNC* ua = uasync_create(); struct ble_config cfg = {0};
cfg.node_id = 0x5643424c45544553ULL; cfg.company_id = 0xffff;
cfg.callbacks.status = status; cfg.callbacks.broadcast = broadcast; cfg.callbacks.connection = connected;
@@ -52,7 +52,8 @@ object BleRadio {
var gatt: BluetoothGatt? = null, var rx: BluetoothGattCharacteristic? = null,
var invite: BluetoothGattCharacteristic? = null, var subscriptions: Int = 0, var channel: Int = 0,
var mtu: Int = 23, var opened: Boolean = false, var pending: ByteArray? = null, var writing: Boolean = false,
var busySince: Long = 0)
var busySince: Long = 0,
var priorityGatt: BluetoothGatt? = null) // Клиентский handle того же ACL: server API не имеет requestConnectionPriority.
@JvmStatic external fun nativeInstall()
@JvmStatic external fun nativeConfigure(flags: Int): Boolean
@JvmStatic private external fun nativeEvent(gen: Long, token: Long, type: Int, arg: Int, text: String, bytes: ByteArray)
@@ -169,6 +170,7 @@ object BleRadio {
links[link.token] = link
emit(9, link.token, text = device.address) // Core ограничивает время ожидания обеих CCC.
preferFastPhy(device, null)
tuneIncomingPriority(link)
} else if (!accepted) {
log("WARN", "Subscription rejected address=${device.address} characteristic=${descriptor.characteristic.uuid} " +
"descriptor=${descriptor.uuid} incoming=$incoming prepared=$preparedWrite response=$responseNeeded " +
@@ -301,6 +303,34 @@ object BleRadio {
if (gatt != null) gatt.readPhy() else checkNotNull(server).readPhy(device)
} catch (e: RuntimeException) { log("WARN", "PHY tuning unavailable address=${device.address}: ${e.message}") }
}
private fun requestHighPriority(link: Link, gatt: BluetoothGatt) {
val accepted = gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
log(if (accepted) "INFO" else "WARN", "Connection priority HIGH requested link=${link.token} outgoing=${link.outgoing} accepted=$accepted")
}
// GATT client/server — роли API. Клиентский handle использует уже открытый физический ACL, без discovery/DATA.
private fun tuneIncomingPriority(link: Link) {
val gen = generation
try {
val callback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (generation != gen || links[link.token] !== link || link.priorityGatt !== gatt) return
guarded {
log(if (status == 0) "DEBUG" else "WARN", "Priority GATT link=${link.token} status=$status state=$newState")
if (status == 0 && newState == BluetoothProfile.STATE_CONNECTED) {
try { requestHighPriority(link, gatt) }
catch (e: RuntimeException) { log("WARN", "Priority tuning failed link=${link.token}: $e") }
} else if (status != 0 || newState == BluetoothProfile.STATE_DISCONNECTED) {
link.priorityGatt = null
cleanup.retain(gatt, "priority GATT link=${link.token}") { gatt.close() }; drainCleanup()
}
}
}
}
link.priorityGatt = link.device.connectGatt(context, false, callback, BluetoothDevice.TRANSPORT_LE,
BluetoothDevice.PHY_LE_1M_MASK, worker)
if (link.priorityGatt == null) log("WARN", "Priority GATT unavailable link=${link.token}")
} catch (e: RuntimeException) { log("WARN", "Incoming priority tuning unavailable link=${link.token}: $e") }
}
private fun connect(token: Long, address: String) {
check(cleanup.isEmpty) { "Previous BLE cleanup pending" }
check(links.size < 16) { "link limit" }
@@ -314,8 +344,7 @@ object BleRadio {
log(if (status == 0) "INFO" else "WARN", "Client link=$token status=$status state=$newState")
if (status != 0 || newState != BluetoothProfile.STATE_CONNECTED) close(link, "client status=$status state=$newState")
else {
val priority = gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
log(if (priority) "INFO" else "WARN", "Connection priority HIGH requested link=$token accepted=$priority")
requestHighPriority(link, gatt)
preferFastPhy(link.device, gatt)
if (!gatt.requestMtu(517)) close(link, "requestMtu refused")
}
@@ -454,6 +483,10 @@ object BleRadio {
links.remove(link.token)
log(if (report) "WARN" else "INFO", "Closed link=${link.token} address=${link.device.address} outgoing=${link.outgoing} " +
"opened=${link.opened} channel=${link.channel} pending=${link.pending?.size} writing=${link.writing} reason=$reason")
link.priorityGatt?.let { gatt ->
link.priorityGatt = null
cleanup.retain(gatt, "priority GATT link=${link.token}") { gatt.close() }
}
if (link.outgoing) link.gatt?.let { gatt ->
try { gatt.disconnect() } catch (e: RuntimeException) { log("WARN", "Disconnect failed link=${link.token}: $e") }
cleanup.retain(gatt, "client GATT link=${link.token}") { gatt.close() }