Android 的 bluez 和 service/characteristics 缓存问题

bluez and service/characteristics cache issue with Android

我在 Android 移动应用程序和自定义嵌入式设备之间通过 BLE 进行通信时遇到了一些问题。嵌入式设备是外围设备,使用 Bluez 定义的自定义 GATT 配置文件。 一切正常。问题是有时,我们有时会在具有新服务的嵌入式设备上部署新固件,但也经常部署新特性。我们使用的是最新的 Bluez 版本 (5.54)

IOS 一切正常。但是对于 Android,移动应用有时会随机地继续使用旧的 services/characteristics UUID。所以基本上移动应用程序发送了一个关于错误特征的请求......当然我们无法以确定的方式重现这个问题..

我们发现了一些关于缓存的东西:silabs

在网上找了一些答案后,我们找到了一些关于绑定的信息。所以我们尝试在移动应用 Android 端添加这个:

Method m = device.getClass().getMethod(“removeBond”, (Class[]) null);
            m.invoke(device, (Object[]) null);

我们还尝试从 Android 端强制刷新缓存:

final Method refresh = gatt.getClass().getMethod(“refresh”); refresh.invoke(gatt);

但它总是无法正常工作。我们还在我们的特征中检查客户端支持的功能和数据库哈希的存在。它似乎如预期的那样存在:

./btgatt-client -i hci0 -d xxxx
[GATT client]# Service Added - UUID: 00001800-0000-1000-8000-00805f9b34fb start: 0x0001 end: 0x0005
[GATT client]# Service Added - UUID: 00001801-0000-1000-8000-00805f9b34fb start: 0x0006 end: 0x000f
[GATT client]# Service Added - UUID: 0000df00-0000-1000-8000-00805f9b34fb start: 0x0010 end: 0x0012
[GATT client]# Service Added - UUID: 0000d200-0000-1000-8000-00805f9b34fb start: 0x0013 end: 0x0023
[GATT client]# Service Added - UUID: 0000d010-0000-1000-8000-00805f9b34fb start: 0x0024 end: 0x002a
[GATT client]# Service Added - UUID: 0000d100-0000-1000-8000-00805f9b34fb start: 0x002b end: 0x0031
[GATT client]# Service Added - UUID: 0000d700-0000-1000-8000-00805f9b34fb start: 0x0032 end: 0x0045
[GATT client]# Service Added - UUID: 0000d900-0000-1000-8000-00805f9b34fb start: 0x0046 end: 0x0050
[GATT client]# Service Added - UUID: 0000d600-0000-1000-8000-00805f9b34fb start: 0x0051 end: 0x0053
[GATT client]# Service Added - UUID: 0000d500-0000-1000-8000-00805f9b34fb start: 0x0054 end: 0x0072
[GATT client]# Service Added - UUID: 0000d850-0000-1000-8000-00805f9b34fb start: 0x0073 end: 0x0075
[GATT client]# Service Added - UUID: 0000d400-0000-1000-8000-00805f9b34fb start: 0x0076 end: 0x007a
[GATT client]# Service Added - UUID: 0000da00-0000-1000-8000-00805f9b34fb start: 0x007b end: 0x0085
[GATT client]# Service Added - UUID: 0000d300-0000-1000-8000-00805f9b34fb start: 0x0086 end: 0x0099
[GATT client]# Service Added - UUID: 0000d200-0000-1000-8000-00805f9b34fb start: 0x009a end: 0x00b1
[GATT client]# Service Added - UUID: 0000d800-0000-1000-8000-00805f9b34fb start: 0x00b2 end: 0x00ba
[GATT client]# GATT discovery procedures complete

service - start: 0x0001, end: 0x0005, type: primary, uuid: 00001800-0000-1000-8000-00805f9b34fb
      charac - start: 0x0002, value: 0x0003, props: 0x02, ext_props: 0x0000, uuid: 00002a00-0000-1000-8000-00805f9b34fb
      charac - start: 0x0004, value: 0x0005, props: 0x02, ext_props: 0x0000, uuid: 00002a01-0000-1000-8000-00805f9b34fb

service - start: 0x0006, end: 0x000f, type: primary, uuid: 00001801-0000-1000-8000-00805f9b34fb
      charac - start: 0x0007, value: 0x0008, props: 0x20, ext_props: 0x0000, uuid: 00002a05-0000-1000-8000-00805f9b34fb
          descr - handle: 0x0009, uuid: 00002902-0000-1000-8000-00805f9b34fb
      charac - start: 0x000a, value: 0x000b, props: 0x0a, ext_props: 0x0000, uuid: 00002b29-0000-1000-8000-00805f9b34fb
      charac - start: 0x000c, value: 0x000d, props: 0x02, ext_props: 0x0000, uuid: 00002b2a-0000-1000-8000-00805f9b34fb
      charac - start: 0x000e, value: 0x000f, props: 0x02, ext_props: 0x0000, uuid: 00002b3a-0000-1000-8000-00805f9b34fb

service - start: 0x0010, end: 0x0012, type: primary, uuid: 0000df00-0000-1000-8000-00805f9b34fb
      charac - start: 0x0011, value: 0x0012, props: 0x08, ext_props: 0x0000, uuid: 0000df01-0000-1000-8000-00805f9b34fb

service - start: 0x0013, end: 0x0023, type: primary, uuid: 0000d200-0000-1000-8000-00805f9b34fb
      charac - start: 0x0014, value: 0x0015, props: 0x0a, ext_props: 0x0000, uuid: 0000d008-0000-1000-8000-00805f9b34fb
      charac - start: 0x0016, value: 0x0017, props: 0x0a, ext_props: 0x0000, uuid: 0000d007-0000-1000-8000-00805f9b34fb
      charac - start: 0x0018, value: 0x0019, props: 0x08, ext_props: 0x0000, uuid: 0000d006-0000-1000-8000-00805f9b34fb
      charac - start: 0x001a, value: 0x001b, props: 0x08, ext_props: 0x0000, uuid: 0000d005-0000-1000-8000-00805f9b34fb
      charac - start: 0x001c, value: 0x001d, props: 0x08, ext_props: 0x0000, uuid: 0000d004-0000-1000-8000-00805f9b34fb
      charac - start: 0x001e, value: 0x001f, props: 0x02, ext_props: 0x0000, uuid: 0000d003-0000-1000-8000-00805f9b34fb
      charac - start: 0x0020, value: 0x0021, props: 0x08, ext_props: 0x0000, uuid: 0000d002-0000-1000-8000-00805f9b34fb
      charac - start: 0x0022, value: 0x0023, props: 0x08, ext_props: 0x0000, uuid: 0000d001-0000-1000-8000-00805f9b34fb

service - start: 0x0024, end: 0x002a, type: primary, uuid: 0000d010-0000-1000-8000-00805f9b34fb
      charac - start: 0x0025, value: 0x0026, props: 0x02, ext_props: 0x0000, uuid: 0000d013-0000-1000-8000-00805f9b34fb
      charac - start: 0x0027, value: 0x0028, props: 0x08, ext_props: 0x0000, uuid: 0000d012-0000-1000-8000-00805f9b34fb
      charac - start: 0x0029, value: 0x002a, props: 0x08, ext_props: 0x0000, uuid: 0000d011-0000-1000-8000-00805f9b34fb

service - start: 0x002b, end: 0x0031, type: primary, uuid: 0000d100-0000-1000-8000-00805f9b34fb
      charac - start: 0x002c, value: 0x002d, props: 0x02, ext_props: 0x0000, uuid: 0000d103-0000-1000-8000-00805f9b34fb
      charac - start: 0x002e, value: 0x002f, props: 0x0a, ext_props: 0x0000, uuid: 0000d102-0000-1000-8000-00805f9b34fb
      charac - start: 0x0030, value: 0x0031, props: 0x02, ext_props: 0x0000, uuid: 0000d101-0000-1000-8000-00805f9b34fb

service - start: 0x0032, end: 0x0045, type: primary, uuid: 0000d700-0000-1000-8000-00805f9b34fb
      charac - start: 0x0033, value: 0x0034, props: 0x02, ext_props: 0x0000, uuid: 0000d709-0000-1000-8000-00805f9b34fb
      charac - start: 0x0035, value: 0x0036, props: 0x08, ext_props: 0x0000, uuid: 0000d706-0000-1000-8000-00805f9b34fb
      charac - start: 0x0037, value: 0x0038, props: 0x10, ext_props: 0x0000, uuid: 0000d704-0000-1000-8000-00805f9b34fb
          descr - handle: 0x0039, uuid: 00002902-0000-1000-8000-00805f9b34fb
      charac - start: 0x003a, value: 0x003b, props: 0x08, ext_props: 0x0000, uuid: 0000d707-0000-1000-8000-00805f9b34fb
      charac - start: 0x003c, value: 0x003d, props: 0x08, ext_props: 0x0000, uuid: 0000d703-0000-1000-8000-00805f9b34fb
      charac - start: 0x003e, value: 0x003f, props: 0x02, ext_props: 0x0000, uuid: 0000d708-0000-1000-8000-00805f9b34fb
      charac - start: 0x0040, value: 0x0041, props: 0x02, ext_props: 0x0000, uuid: 0000d702-0000-1000-8000-00805f9b34fb
      charac - start: 0x0042, value: 0x0043, props: 0x02, ext_props: 0x0000, uuid: 0000d705-0000-1000-8000-00805f9b34fb
      charac - start: 0x0044, value: 0x0045, props: 0x02, ext_props: 0x0000, uuid: 0000d701-0000-1000-8000-00805f9b34fb

service - start: 0x0046, end: 0x0050, type: primary, uuid: 0000d900-0000-1000-8000-00805f9b34fb
      charac - start: 0x0047, value: 0x0048, props: 0x08, ext_props: 0x0000, uuid: 0000d905-0000-1000-8000-00805f9b34fb
      charac - start: 0x0049, value: 0x004a, props: 0x02, ext_props: 0x0000, uuid: 0000d904-0000-1000-8000-00805f9b34fb
      charac - start: 0x004b, value: 0x004c, props: 0x02, ext_props: 0x0000, uuid: 0000d903-0000-1000-8000-00805f9b34fb
      charac - start: 0x004d, value: 0x004e, props: 0x08, ext_props: 0x0000, uuid: 0000d902-0000-1000-8000-00805f9b34fb
      charac - start: 0x004f, value: 0x0050, props: 0x08, ext_props: 0x0000, uuid: 0000d901-0000-1000-8000-00805f9b34fb

service - start: 0x0051, end: 0x0053, type: primary, uuid: 0000d600-0000-1000-8000-00805f9b34fb
      charac - start: 0x0052, value: 0x0053, props: 0x08, ext_props: 0x0000, uuid: 0000d601-0000-1000-8000-00805f9b34fb

service - start: 0x0054, end: 0x0072, type: primary, uuid: 0000d500-0000-1000-8000-00805f9b34fb
      charac - start: 0x0055, value: 0x0056, props: 0x0a, ext_props: 0x0000, uuid: 0000d516-0000-1000-8000-00805f9b34fb
      charac - start: 0x0057, value: 0x0058, props: 0x0a, ext_props: 0x0000, uuid: 0000d517-0000-1000-8000-00805f9b34fb
      charac - start: 0x0059, value: 0x005a, props: 0x02, ext_props: 0x0000, uuid: 0000d514-0000-1000-8000-00805f9b34fb
      charac - start: 0x005b, value: 0x005c, props: 0x02, ext_props: 0x0000, uuid: 0000d513-0000-1000-8000-00805f9b34fb
      charac - start: 0x005d, value: 0x005e, props: 0x02, ext_props: 0x0000, uuid: 0000d512-0000-1000-8000-00805f9b34fb
      charac - start: 0x005f, value: 0x0060, props: 0x08, ext_props: 0x0000, uuid: 0000d511-0000-1000-8000-00805f9b34fb
      charac - start: 0x0061, value: 0x0062, props: 0x02, ext_props: 0x0000, uuid: 0000d509-0000-1000-8000-00805f9b34fb
      charac - start: 0x0063, value: 0x0064, props: 0x08, ext_props: 0x0000, uuid: 0000d510-0000-1000-8000-00805f9b34fb
      charac - start: 0x0065, value: 0x0066, props: 0x08, ext_props: 0x0000, uuid: 0000d508-0000-1000-8000-00805f9b34fb
      charac - start: 0x0067, value: 0x0068, props: 0x08, ext_props: 0x0000, uuid: 0000d518-0000-1000-8000-00805f9b34fb
      charac - start: 0x0069, value: 0x006a, props: 0x08, ext_props: 0x0000, uuid: 0000d506-0000-1000-8000-00805f9b34fb
      charac - start: 0x006b, value: 0x006c, props: 0x08, ext_props: 0x0000, uuid: 0000d505-0000-1000-8000-00805f9b34fb
      charac - start: 0x006d, value: 0x006e, props: 0x08, ext_props: 0x0000, uuid: 0000d504-0000-1000-8000-00805f9b34fb
      charac - start: 0x006f, value: 0x0070, props: 0x08, ext_props: 0x0000, uuid: 0000d507-0000-1000-8000-00805f9b34fb
      charac - start: 0x0071, value: 0x0072, props: 0x0a, ext_props: 0x0000, uuid: 0000d501-0000-1000-8000-00805f9b34fb

service - start: 0x0073, end: 0x0075, type: primary, uuid: 0000d850-0000-1000-8000-00805f9b34fb
      charac - start: 0x0074, value: 0x0075, props: 0x08, ext_props: 0x0000, uuid: 0000d853-0000-1000-8000-00805f9b34fb

service - start: 0x0076, end: 0x007a, type: primary, uuid: 0000d400-0000-1000-8000-00805f9b34fb
      charac - start: 0x0077, value: 0x0078, props: 0x02, ext_props: 0x0000, uuid: 0000d402-0000-1000-8000-00805f9b34fb
      charac - start: 0x0079, value: 0x007a, props: 0x02, ext_props: 0x0000, uuid: 0000d401-0000-1000-8000-00805f9b34fb

service - start: 0x007b, end: 0x0085, type: primary, uuid: 0000da00-0000-1000-8000-00805f9b34fb
      charac - start: 0x007c, value: 0x007d, props: 0x02, ext_props: 0x0000, uuid: 0000da05-0000-1000-8000-00805f9b34fb
      charac - start: 0x007e, value: 0x007f, props: 0x08, ext_props: 0x0000, uuid: 0000da04-0000-1000-8000-00805f9b34fb
      charac - start: 0x0080, value: 0x0081, props: 0x02, ext_props: 0x0000, uuid: 0000da03-0000-1000-8000-00805f9b34fb
      charac - start: 0x0082, value: 0x0083, props: 0x02, ext_props: 0x0000, uuid: 0000da02-0000-1000-8000-00805f9b34fb
      charac - start: 0x0084, value: 0x0085, props: 0x08, ext_props: 0x0000, uuid: 0000da01-0000-1000-8000-00805f9b34fb

service - start: 0x0086, end: 0x0099, type: primary, uuid: 0000d300-0000-1000-8000-00805f9b34fb
      charac - start: 0x0087, value: 0x0088, props: 0x02, ext_props: 0x0000, uuid: 0000d309-0000-1000-8000-00805f9b34fb
      charac - start: 0x0089, value: 0x008a, props: 0x08, ext_props: 0x0000, uuid: 0000d30a-0000-1000-8000-00805f9b34fb
      charac - start: 0x008b, value: 0x008c, props: 0x02, ext_props: 0x0000, uuid: 0000d308-0000-1000-8000-00805f9b34fb
      charac - start: 0x008d, value: 0x008e, props: 0x0a, ext_props: 0x0000, uuid: 0000d307-0000-1000-8000-00805f9b34fb
      charac - start: 0x008f, value: 0x0090, props: 0x02, ext_props: 0x0000, uuid: 0000d306-0000-1000-8000-00805f9b34fb
      charac - start: 0x0091, value: 0x0092, props: 0x02, ext_props: 0x0000, uuid: 0000d305-0000-1000-8000-00805f9b34fb
      charac - start: 0x0093, value: 0x0094, props: 0x02, ext_props: 0x0000, uuid: 0000d304-0000-1000-8000-00805f9b34fb
      charac - start: 0x0095, value: 0x0096, props: 0x08, ext_props: 0x0000, uuid: 0000d302-0000-1000-8000-00805f9b34fb
      charac - start: 0x0097, value: 0x0098, props: 0x12, ext_props: 0x0000, uuid: 0000d301-0000-1000-8000-00805f9b34fb
          descr - handle: 0x0099, uuid: 00002902-0000-1000-8000-00805f9b34fb

service - start: 0x009a, end: 0x00b1, type: primary, uuid: 0000d200-0000-1000-8000-00805f9b34fb
      charac - start: 0x009b, value: 0x009c, props: 0x02, ext_props: 0x0000, uuid: 0000d211-0000-1000-8000-00805f9b34fb
      charac - start: 0x009d, value: 0x009e, props: 0x08, ext_props: 0x0000, uuid: 0000d210-0000-1000-8000-00805f9b34fb
      charac - start: 0x009f, value: 0x00a0, props: 0x12, ext_props: 0x0000, uuid: 0000d209-0000-1000-8000-00805f9b34fb
          descr - handle: 0x00a1, uuid: 00002902-0000-1000-8000-00805f9b34fb
      charac - start: 0x00a2, value: 0x00a3, props: 0x02, ext_props: 0x0000, uuid: 0000d208-0000-1000-8000-00805f9b34fb
      charac - start: 0x00a4, value: 0x00a5, props: 0x08, ext_props: 0x0000, uuid: 0000d206-0000-1000-8000-00805f9b34fb
      charac - start: 0x00a6, value: 0x00a7, props: 0x08, ext_props: 0x0000, uuid: 0000d207-0000-1000-8000-00805f9b34fb
      charac - start: 0x00a8, value: 0x00a9, props: 0x08, ext_props: 0x0000, uuid: 0000d205-0000-1000-8000-00805f9b34fb
      charac - start: 0x00aa, value: 0x00ab, props: 0x02, ext_props: 0x0000, uuid: 0000d204-0000-1000-8000-00805f9b34fb
      charac - start: 0x00ac, value: 0x00ad, props: 0x02, ext_props: 0x0000, uuid: 0000d203-0000-1000-8000-00805f9b34fb
      charac - start: 0x00ae, value: 0x00af, props: 0x02, ext_props: 0x0000, uuid: 0000d202-0000-1000-8000-00805f9b34fb
      charac - start: 0x00b0, value: 0x00b1, props: 0x02, ext_props: 0x0000, uuid: 0000d201-0000-1000-8000-00805f9b34fb

service - start: 0x00b2, end: 0x00ba, type: primary, uuid: 0000d800-0000-1000-8000-00805f9b34fb
      charac - start: 0x00b3, value: 0x00b4, props: 0x02, ext_props: 0x0000, uuid: 0000d805-0000-1000-8000-00805f9b34fb
      charac - start: 0x00b5, value: 0x00b6, props: 0x08, ext_props: 0x0000, uuid: 0000d804-0000-1000-8000-00805f9b34fb
      charac - start: 0x00b7, value: 0x00b8, props: 0x02, ext_props: 0x0000, uuid: 0000d803-0000-1000-8000-00805f9b34fb
      charac - start: 0x00b9, value: 0x00ba, props: 0x08, ext_props: 0x0000, uuid: 0000d801-0000-1000-8000-00805f9b34fb

服务发现中存在 uuid 2B29 和 2B2A...

这也很有趣,因为当我使用 5.54 的 btgatt-client 时,恶魔蓝牙崩溃了。我必须 运行 使用 5.50 gatt 客户端进行测试...

编辑1:

我能够在客户设备上重现该问题。我试图通过在 /etc/bluetooth/main.conf:

下放入 bluetoothd conf 来禁用缓存
[GATT]
Cache = no

此更改后问题始终存在。顺便说一句,我试图通过删除 /var/lib/bluetooth 中的目录来删除缓存。但实际上我没有这个目录。事实上,我们所有的主要 rootfs 在我们的设备上都是只读的。所以 bluetoothd 无法创建目录。所以也许这不是真正的缓存问题?

说明

某些版本的 Android 在绑定(与 LE)操作发生时存在问题。在Nordic的源代码中,确实有一条注释(source)证实了这一点:

// 1. On devices running Android 4.3-5.x, 8.x and 9.0 the Service Changed
//    characteristic needs to be enabled by the app (for bonded devices).
//    The request will be ignored if there is no Service Changed characteristic.
// This "fix" broke this in Android 8:
// https://android-review.googlesource.com/c/platform/system/bt/+/239970
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
    || Build.VERSION.SDK_INT == Build.VERSION_CODES.O
    || Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1
    || Build.VERSION.SDK_INT == Build.VERSION_CODES.P)
        initQueue.addFirst(Request.newEnableServiceChangedIndicationsRequest());

基本上,有一个特性负责指示当服务发生变化时是否应更新缓存的属性。它在某些时候被打破了。

解决方案

Nordic 的评论相当self-explanatory:您需要手动启用已更改的服务。以下是他们的做法:part 1 and part 2。 这是一个简洁的 Kotlin 版本:

private val GENERIC_ATTRIBUTE_SERVICE =
    UUID.fromString("00001801-0000-1000-8000-00805f9b34fb")
private val SERVICE_CHANGED_CHARACTERISTIC =
    UUID.fromString("00002a05-0000-1000-8000-00805f9b34fb")
private val CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID =
    UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")

private fun enableServiceChanged(gatt: BluetoothGatt) {
    val service = gatt.getService(GENERIC_ATTRIBUTE_SERVICE) ?: return
    val characteristic = service.getCharacteristic(SERVICE_CHANGED_CHARACTERISTIC)
        ?: return
    
    val properties = characteristic.properties
    if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE === 0)
        return
    
    gatt.setCharacteristicNotification(characteristic, true)
    characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID)?.apply {
        value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
        internalWriteDescriptorWorkaround(gatt, this)
    }
}

private fun internalWriteDescriptorWorkaround(
        gatt: BluetoothGatt,
        descriptor: BluetoothGattDescriptor
) {
        with (descriptor.characteristic) {
            val originalWriteType = writeType
            writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
            gatt.writeDescriptor(descriptor)
            writeType = originalWriteType
        }
}