Use unconfirmed BLE ACK writes and notifications on Android

This commit is contained in:
evgeny committed 2026-10-30 04:39:05 +01:00
1 parent 452ab50f9e
commit 09e045a45d
2 files changed
+16 -11

No files matched your search

+1
View File
@@ -2,6 +2,7 @@
* Каждая отправка адресована link/subscribedClient; одна незавершённая операция на link.
* Слабые ссылки в WinRT handlers и отменяемый posted_task исключают обращения к уничтоженному core. */
#include "ble_backend.h"
#include "ble_identity.h"
#include "../u_async.h"
#include "../mem.h"
#include "../debug_config.h"
@@ -176,8 +176,8 @@ object BleRadio {
}
override fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor,
preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) = post {
val expected = if (descriptor.characteristic.uuid == txUuid) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
val expected = if (descriptor.characteristic.uuid == inviteUuid) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
else BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
val valid = incoming && descriptor.uuid == cccUuid && descriptor.characteristic.uuid in listOf(txUuid, inviteUuid, ackUuid) &&
!preparedWrite && offset == 0 && value.contentEquals(expected)
var link = links.values.find { !it.outgoing && it.device.address == device.address }
@@ -207,7 +207,7 @@ object BleRadio {
preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) = post {
val link = links.values.find { !it.outgoing && it.device.address == device.address }
val valid = link != null && link.opened && characteristic.uuid in listOf(rxUuid, inviteUuid, ackUuid) &&
!preparedWrite && responseNeeded == (characteristic.uuid != rxUuid) && offset == 0
!preparedWrite && responseNeeded == (characteristic.uuid == inviteUuid) && offset == 0
if (valid) emit(3, link!!.token, arg = wireChannel(characteristic.uuid), data = value.copyOf())
if (responseNeeded) respond(device, requestId, if (valid) 0 else BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED)
if (!valid) log("WARN", "Write rejected address=${device.address} characteristic=${characteristic.uuid} " +
@@ -253,7 +253,7 @@ object BleRadio {
}
service.addCharacteristic(invite)
ack = BluetoothGattCharacteristic(ackUuid,
BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_INDICATE,
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE).apply {
addDescriptor(BluetoothGattDescriptor(cccUuid, BluetoothGattDescriptor.PERMISSION_WRITE))
}
@@ -405,8 +405,10 @@ object BleRadio {
if (characteristic.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY == 0) {
close(link, "TX lacks Notification properties=${characteristic.properties}"); return@dispatch
}
val controlProperties = BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_INDICATE
for (control in listOf(checkNotNull(link.invite), checkNotNull(link.ack))) {
val controlProperties = if (control.uuid == inviteUuid)
BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_INDICATE
else BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or BluetoothGattCharacteristic.PROPERTY_NOTIFY
if (control.properties and controlProperties != controlProperties || control.getDescriptor(cccUuid) == null) {
close(link, "Control characteristic invalid uuid=${control.uuid} properties=${control.properties}"); return@dispatch
}
@@ -414,7 +416,7 @@ object BleRadio {
if (!gatt.setCharacteristicNotification(characteristic, true)) {
close(link, "DATA local subscription refused characteristic=${characteristic.uuid}"); return@dispatch
}
log("INFO", "Client DATA configured link=$token write=without_response receive=notification mtu=${link.mtu}")
log("INFO", "Client DATA/ACK configured link=$token write=without_response receive=notification mtu=${link.mtu}")
val result = if (Build.VERSION.SDK_INT >= 33) gatt.writeDescriptor(ccc, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
else { ccc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; if (gatt.writeDescriptor(ccc)) 0 else -1 }
if (result != 0) close(link, "DATA CCC write refused status=$result")
@@ -427,8 +429,10 @@ object BleRadio {
if (ccc == null || !gatt.setCharacteristicNotification(characteristic, true)) {
close(link, "control subscription unavailable after=${descriptor.characteristic.uuid}"); return@dispatch
}
val result = if (Build.VERSION.SDK_INT >= 33) gatt.writeDescriptor(ccc, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
else { ccc.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE; if (gatt.writeDescriptor(ccc)) 0 else -1 }
val value = if (characteristic?.uuid == inviteUuid) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
else BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
val result = if (Build.VERSION.SDK_INT >= 33) gatt.writeDescriptor(ccc, value)
else { ccc.value = value; if (gatt.writeDescriptor(ccc)) 0 else -1 }
if (result != 0) close(link, "Control CCC write refused uuid=${characteristic?.uuid} status=$result")
} else if (descriptor.characteristic.uuid == ackUuid) { link.opened = true; emit(2, token, link.mtu, address) }
else close(link, "unexpected CCC completion")
@@ -458,7 +462,7 @@ object BleRadio {
val bytes = checkNotNull(link.pending)
val status = if (link.outgoing) {
val characteristic = checkNotNull(when (link.channel) { 0 -> link.rx; 1 -> link.invite; else -> link.ack })
val type = if (link.channel != 0) BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
val type = if (link.channel == 1) BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
else BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
if (Build.VERSION.SDK_INT >= 33) link.gatt!!.writeCharacteristic(characteristic, bytes, type)
else {
@@ -467,10 +471,10 @@ object BleRadio {
}
} else {
val characteristic = checkNotNull(when (link.channel) { 0 -> tx; 1 -> invite; else -> ack })
if (Build.VERSION.SDK_INT >= 33) server!!.notifyCharacteristicChanged(link.device, characteristic, link.channel != 0, bytes)
if (Build.VERSION.SDK_INT >= 33) server!!.notifyCharacteristicChanged(link.device, characteristic, link.channel == 1, bytes)
else {
characteristic.value = bytes
if (server!!.notifyCharacteristicChanged(link.device, characteristic, link.channel != 0)) 0 else -1
if (server!!.notifyCharacteristicChanged(link.device, characteristic, link.channel == 1)) 0 else -1
}
}
if (status == 0) {