object CHatServer{
private fun setupGattService(): BluetoothGattService {
//......
val rrepCharacteristic = BluetoothGattCharacteristic(
RREP_UUID,
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY ,
BluetoothGattCharacteristic.PERMISSION_READ
)
val cccd = BluetoothGattDescriptor(
CCCD_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
)
rrepCharacteristic.addDescriptor(cccd)
service.addCharacteristic(rrepCharacteristic)
val uuid = BluetoothGattCharacteristic(
app?.let { UUIDManager.getStoredUUID(it.applicationContext) },
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ
)
service.addCharacteristic(uuid)
return service
}
private class GattClientCallback : BluetoothGattCallback() {
@SuppressLint("MissingPermission")
//.......
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@SuppressLint("MissingPermission")
override fun onServicesDiscovered(discoveredGatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(discoveredGatt, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered: Have gatt $discoveredGatt")
gatt = discoveredGatt
val service = discoveredGatt.getService(SERVICE_UUID)
if (service!=null){
if(service.getCharacteristic(MESSAGE_UUID)!=null){
messageCharacteristic = service.getCharacteristic(MESSAGE_UUID)
}
if(service.getCharacteristic(RREQ_UUID)!=null){
rreqCharacteristic = service.getCharacteristic(RREQ_UUID)
}
if(service.getCharacteristic(RREP_UUID)!=null){
rrepCharacteristic = service.getCharacteristic(RREP_UUID)
enableNotificationsForRrepCharacteristic(gatt, rrepCharacteristic)
}
val uuid = getUUID(service)
notifyConnectionSuccess(gatt!!.device,uuid)
Log.d(TAG, "UUID Discovered: $uuid")
gatt!!.requestMtu(GATT_MAX_MTU_SIZE)
}
}
}
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray
) {
super.onCharacteristicChanged(gatt, characteristic, value)
Log.d(TAG, "Received RREP: ${gatt.device}")
val packet = deserializePacket(value)
if (packet != null) {
if(packet.src == appUUID.toString()){
Log.d(TAG, "Path Found from A to C")
}
}
_rrepRequests.postValue(packet)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@SuppressLint("MissingPermission")
fun enableNotificationsForRrepCharacteristic(gatt: BluetoothGatt?, rrepCharacteristic: BluetoothGattCharacteristic?) {
// Step 1: Enable notifications locally
val notificationEnabled = gatt?.setCharacteristicNotification(rrepCharacteristic, true) ?: false
if (!notificationEnabled) {
Log.w(TAG, "Failed to enable local notifications for RREP_UUID")
return
}
// Step 2: Write to the CCCD to enable notifications on the peripheral
val cccd = rrepCharacteristic?.getDescriptor(CCCD_UUID)
if (cccd == null) {
Log.w(TAG, "RREP_UUID characteristic does not have a CCCD")
return
}
val success = gatt?.writeDescriptor(cccd,BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
if (success != BluetoothStatusCodes.SUCCESS) {
Log.w(TAG, "Failed to write CCCD value for enabling notifications")
} else {
Log.d(TAG, "Successfully requested to enable notifications for RREP_UUID")
}
}
}
I think the issue is the writeDescriptor() function, because we are writing when we are still in onDiscoveredServices() callback, in between GATT operation, This is causing the 201 error (BUSY ERROR). But if we can not do that then how can we enable notification.?
When I use only setCharacteristicNotification() function in onServiceDiscovered() callback, then i am able to write to any characteristic but i wont be able to receive the notification. Because i heard you need descriptor to do that.`
In general, you need to queue all BLE operations so that one can fully complete before you start the next one. All BLE libraries out there do this.
In your case it might actually be the requestMTU() method is the issue. Because the write to the descriptor has not finished yet. Just debug it to see which gatt operation is giving you the error....