
이번 글에서는 Android에서 Bluetooth 알림(notification) 기능을 설정하는 방법에 대해 공부한 내용을 정리해 두고자 합니다.
특히, 00002902-0000-1000-8000-00805f9b34fb UUID를 사용하는 CCCD(Client Characteristic Configuration Descriptor) 설정, 알림 활성화 과정, 그리고 Android 버전별(API 33 Tiramisu 이상과 이하) writeDescriptor() 및 onCharacteristicChanged() 메서드의 처리 방법에 대해 기록 해 보겠습니다.
1. Bluetooth 알림 개요
Bluetooth GATT(GATT: Generic Attribute Profile) 프로토콜을 통해 GATT 서버가 클라이언트에게 알림을 보내려면, 클라이언트는 사전에 해당 알림 기능을 활성화해야 합니다. 이를 위해 CCCD가 사용되며, 알림을 활성화할 때 CCCD의 UUID 00002902-0000-1000-8000-00805f9b34fb를 사용합니다.
- CCCD(Client Characteristic Configuration Descriptor): GATT 클라이언트가 특정 특성의 알림 또는 표시 기능을 활성화하도록 설정하는 디스크립터입니다.
- Notify와 Indicate의 차이점: 일반적인 GATT 통신은 클라이언트가 서버에 요청을 보내고 응답을 받는 구조이지만, Notify와 Indicate는 서버가 데이터 변경 사항을 클라이언트에게 먼저 알려주는 방식입니다.
2. 알림 설정 과정
Bluetooth 장치와 통신하여 알림 기능을 활성화하려면 다음 절차를 따릅니다:
- setCharacteristicNotification() 호출: 알림을 활성화할 특성(characteristic)에 대해 알림을 설정합니다.
- writeDescriptor()를 통해 CCCD 설정: 알림이 설정된 후, CCCD의 값을 ENABLE_NOTIFICATION_VALUE로 설정하여 클라이언트가 알림을 받을 수 있도록 구성합니다.
// 알림 활성화/비활성 설정
val notificationSet = bleGatt?.setCharacteristicNotification(characteristic, enable) == true
// CCCD 설정 (알림 활성화)
val descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
bleGatt?.writeDescriptor(descriptor)
위 코드를 통해 알림이 활성화되면, 특성의 값이 변경될 때마다 onCharacteristicChanged() 메서드가 호출됩니다.
3. Android 버전별 writeDescriptor() 및 onCharacteristicChanged() 처리
Android 13(API 33)부터 writeDescriptor() 메서드가 deprecated되었으며, onCharacteristicChanged() 메서드의 인자도 변경되었습니다. 따라서 Android 버전에 맞춰 적절하게 분기 처리를 해야 합니다.
3.1 writeDescriptor() 버전별 분기 처리
Android 13 이상에서는 새로운 API를 사용해 CCCD에 쓰기 작업을 수행해야 하며, 그 이하 버전에서는 기존의 writeDescriptor() 메서드를 사용합니다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// TIRAMISU 이상 버전에서는 새로운 API 사용
bluetoothGatt.writeDescriptor(descriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
} else {
// TIRAMISU 이하 버전에서는 기존의 writeDescriptor() 사용
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
bluetoothGatt.writeDescriptor(descriptor)
}
3.2 onCharacteristicChanged() 메서드 버전별 처리
Android 13 이상에서는 onCharacteristicChanged() 메서드에 새로운 인자가 추가되었습니다. 따라서 API 33 이상과 그 이하 버전에 맞게 각각의 메서드를 사용해야 합니다.
// TIRAMISU 이상 버전 (새로운 인자 사용)
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray
) {
super.onCharacteristicChanged(gatt, characteristic, value)
val receivedValue = value.joinToString(" ") { byte -> String.format("0x%02X", byte) }
Logger.d("BleManager onCharacteristicChanged: gatt//$gatt, characteristic//$characteristic, value//$receivedValue")
handleCommand(value)
}
// Deprecated: TIRAMISU 이하 버전 (기존 방식 사용)
@Deprecated("Deprecated in Java")
override fun onCharacteristicChanged(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?
) {
super.onCharacteristicChanged(gatt, characteristic)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
val byteArray = characteristic?.value
val valueHex = byteArray?.joinToString(" ") { byte -> String.format("0x%02X", byte) }
Logger.d("BleManager onCharacteristicChanged: gatt//$gatt, characteristic//$valueHex")
byteArray?.let {
handleCommand(it)
}
}
}
4. 추가 설명: 알림 기능 활성화의 중요성
Characteristic Properties를 통해 GATT 클라이언트는 어떤 데이터를 읽고 쓸 수 있는지 알 수 있습니다. 하지만 알림(Notify) 또는 표시(Indicate)는 GATT 서버가 데이터 변경 사항을 클라이언트에게 먼저 전달하는 특별한 기능입니다. 이 기능을 활성화하려면 클라이언트가 미리 알림을 요청해야 하며, 이때 CCCD가 사용됩니다.
- Notify와 Indicate: 일반적인 GATT 통신은 클라이언트가 서버에 요청을 보내고 응답을 받는 방식이지만, Notify와 Indicate는 서버가 데이터 변경 사항을 먼저 알리는 구조입니다.
- CCCD의 역할: 클라이언트는 CCCD를 통해 알림 기능을 활성화해야 합니다. 이를 위해 00002902-0000-1000-8000-00805f9b34fb UUID를 사용하여 CCCD를 설정합니다.
참고: https://support.bluetooth.com/hc/en-us/articles/360062030092-Requesting-Assigned-Numbers
Requesting Assigned Numbers
Table of Contents: Company Identifiers | 16-bit UUIDs for Members | 16-bit UUIDs for SDOs | Custom Characteristic UUIDs | Create a unique 128-bit UUID Company Identifiers Company identifiers are...
support.bluetooth.com
'Android > Android Core' 카테고리의 다른 글
[Android/Kotlin] 기본 카메라 기능을 사용한 사진 및 비디오 자동 촬영 시도 기록 (0) | 2024.10.25 |
---|---|
[Android/Kotlin] CameraX로 자동 사진 및 비디오 촬영 기능 구현하기 (1) | 2024.10.25 |
[Android/Kotlin] Kotlin에서 abstract class와 open class의 차이점 (0) | 2024.10.04 |
[Android/Kotlin] ContentProvider란 무엇인가? (3) | 2024.09.25 |
[Android/Kotlin] Kotlin에서 @Parcelize를 사용하여 객체 전달하기 (0) | 2024.07.25 |