Files
new_kbd/docs/ble_bond_design_notes.md
skiinder 7e0f224ec8 docs(ble_bond): 添加自定义BLE Bond多身份连接设计说明文档
新增详细的自定义BLE Bond设计文档,涵盖多身份连接场景的支持说明。
文档内容包括:CAF自带BLE Bond与自定义BLE Bond的差异对比、架构图解、
多身份实现的关键工作点、实现注意事项以及推荐的分层设计方案。
2026-03-20 15:54:21 +08:00

12 KiB
Raw Permalink Blame History

自定义 BLE Bond 以支持多身份连接的设计说明

1. 背景

CAF 自带的 ble_bond 适合简单外设场景。它默认假设应用只使用 BT_ID_DEFAULT 一个本地身份,职责主要是:

  • 等待 settings_loader 完成初始化
  • 根据按键触发删除默认身份上的 bond
  • 通过 CONFIG_CAF_BLE_BOND_SUPPORTED 告知 CAF 应用实现了 BLE bond

如果应用希望像多设备键盘一样支持多个 Bluetooth identity 对应多个 slot则必须提供自定义 ble_bond。因为此时已经不只是“是否有 bond”而是“哪一个 peer 属于哪一个 slot、当前应使用哪一个 identity、切换 slot 时如何与 ble_adv/ble_state 协同”。


2. CAF 自带 BLE Bond 与自定义 BLE Bond 的差异

2.1 CAF 自带 BLE Bond

特点:

  • 只管理默认 identity
  • 不维护 slot 到 identity 的映射
  • 不发布 ble_peer_operation_event
  • 不处理多主机切换
  • 不处理 peer 所属 slot 自动归位

适用场景:

  • 单主机 BLE 外设
  • 仅需擦除 bond
  • 不需要多身份、多 slot、多 peer 路由

2.2 自定义 BLE Bond

特点:

  • 维护应用 slot 与 Bluetooth stack identity 的映射
  • 保存当前选中的 slot
  • 在 slot 切换时发布 ble_peer_operation_event
  • ble_adv 协同切换广告 identity
  • ble_state 协同确认连接落在正确 identity 上
  • 处理同一 peer 属于哪个 slot
  • 可扩展实现 peer 迁移、自动回 slot、自动重配等策略

适用场景:

  • 多设备键盘
  • 每个 slot 对应独立 bond
  • 需要在多个 identity 间切换广告和连接
  • 需要对 host 行为做应用级策略控制

3. 两版 BLE Bond 架构图

3.1 CAF 自带 BLE Bond 架构

flowchart LR
    A[settings_loader] --> B[CAF ble_bond]
    C[click_event] --> B
    B -->|bt_unpair BT_ID_DEFAULT| D[Bluetooth Stack]
    B -->|MODULE_STATE_READY| E[CAF Modules]
    D --> F[单一默认 Identity]

说明:

  • CAF ble_bond 不参与 slot 选择
  • 也不参与广告 identity 路由
  • 仅在用户触发擦除时对默认 identity 执行 bt_unpair()

3.2 自定义多身份 BLE Bond 架构

flowchart TD
    A[settings_loader] --> B[custom ble_bond]
    C[config_event / slot control] --> B
    D[ble_peer_event CONNECTED/SECURED/DISCONNECTED] --> B

    B --> E[slot <-> bt_stack_id LUT]
    B --> F[current selected slot]
    B --> G[ble_peer_operation_event]

    G --> H[CAF ble_adv]
    G --> I[slot name / GAP name provider]

    H --> J[Advertising on selected Identity]
    J --> K[Windows / Host]
    K --> D

    B -->|validate connection identity| L[bt_conn_disconnect]
    B -->|find peer owner slot| M[auto switch / reconnect]

说明:

  • 自定义版本已经成为 BLE peer 管理的核心策略层
  • 它既要维护持久化状态,也要处理运行时连接路由
  • ble_adv 只负责按事件切换 identity 广播,不负责理解 slot 语义

4. 相比 CAF 自带 BLE Bond额外必须完成的工作

4.1 身份资源规划

必须先定义:

  • 一共有多少个应用 slot
  • CONFIG_BT_ID_MAX 至少要覆盖这些 slot
  • 是否保留 BT_ID_DEFAULT
  • 每个 slot 对应哪个 stack identity

当前实现中:

  • slot_count = 3
  • APP_PEER_COUNT = CONFIG_BT_ID_MAX - 1
  • BT_ID_DEFAULT 不直接用于可切换 slot
  • slot 0/1/2 分别映射到 identity 1/2/3

4.2 持久化保存应用层状态

CAF 自带实现不保存 slot 语义。自定义版本必须自行保存:

  • 当前选中的 slot
  • slot 到 stack identity 的 LUT

否则重启后:

  • ble_adv 可能在错误 identity 上广播
  • 连接会落到旧 identity
  • 导致加密后被应用判定为错误 slot

4.3 向 CAF 明确声明“应用实现了 BLE bond”

如果应用没有通过 Kconfig select CAF_BLE_BOND_SUPPORTED:

  • ble_adv 不会等待自定义 ble_bond ready
  • 开机可能在错误 identity 上提前广播
  • 最终出现连接到 old id、加密后又断开的异常

因此自定义 BLE bond 不能只写 C 文件,还必须补齐 Kconfig 接线。

4.4 设计并发布 peer operation 事件

CAF 自带 ble_bond 不发布 ble_peer_operation_event,但多身份实现必须发布,例如:

  • PEER_OPERATION_SELECTED
  • PEER_OPERATION_ERASE_ADV
  • PEER_OPERATION_ERASED

这些事件会驱动:

  • ble_adv 切换广告 identity
  • 名称提供器更新 GAP/广告名
  • LED/状态模块同步 UI

4.5 处理 slot 切换时的现有连接

当用户切换 slot 时,应用必须决定:

  • 现有 LE 连接是否断开
  • 断开后是否立刻以新 identity 重启广告

如果不主动断开:

  • 连接仍停留在旧 identity
  • 逻辑上已经切到新 slot但链路仍属于旧 slot
  • 后续加密、重连、广告状态都会错乱

4.6 在连接建立后校验“连接是否属于当前 slot”

多身份实现必须在 PEER_STATE_SECURED 甚至更早的 CONNECTED 时校验:

  • 当前连接的 info.id
  • 当前选中 slot 对应的 bt_stack_id

如果不一致:

  • 这是旧 identity、旧连接或错误路由
  • 必须主动断开,避免错误 bond 被继续使用

4.7 处理 peer 与 slot 的归属关系

CAF 自带实现没有“peer 属于哪个 slot”这个概念。多身份实现必须处理:

  • 同一个 peer 之前绑定在哪个 slot
  • 当前连接是不是连错 slot
  • 是否自动切回该 peer 所属 slot
  • 是否允许 peer 从一个 slot 迁移到另一个 slot

当前 C1 实现已经支持:

  • 当 peer 连到错误 slot 时
  • 自动识别该 peer 真实所属 slot
  • 自动切回正确 slot
  • 主动断开一次,等待 host 重连

4.8 处理 bond 擦除和 identity reset

多身份实现通常不能只 bt_unpair(BT_ID_DEFAULT, NULL),还要区分:

  • 删除某个 slot 的 peer
  • 删除全部 slot
  • 是否要 bt_id_reset()
  • 擦除后是否需要重启该 identity 的广告会话

这部分已经不再是单纯 bond 删除而是“identity 生命周期管理”。


5. 自定义 BLE Bond 实现中的重点注意事项

5.1 先解决 Kconfig 接线,再调连接逻辑

这是最容易遗漏的点。

如果没有正确让应用 select CAF_BLE_BOND_SUPPORTED:

  • ble_adv 启动顺序会不对
  • 自定义 ble_bond 即使逻辑正确,也会在启动阶段表现异常

这属于架构接线问题,不是连接状态机问题。

5.2 广播名和 GAP 名必须统一策略

多 slot 场景下,名称策略必须明确:

  • 是每个 slot 不同名字
  • 还是所有 slot 同名

如果广告名和 GAP Device Name 不一致:

  • Windows 可能扫描到一个名字
  • 配对后读到另一个名字
  • 容易造成驱动实例重建、名字重命名、缓存混乱

当前项目已经验证:

  • 统一 GAP 名与广告名后
  • Windows 行为更稳定

5.3 不要假设 Windows 删除设备就等于双方 bond 都删了

这在多 slot 里尤其关键。

Windows 端删设备后:

  • 主机端密钥可能删了
  • 设备端旧 slot 的本地 bond 仍然存在

这会导致:

  • 新 slot 上重新配对被 SMP 拒绝
  • 日志出现 Refusing new pairing. The old bond must be unpaired first.

因此自定义 BLE bond 必须明确“本地 bond”和“主机侧配对记录”不是同一件事。

5.4 host 删除密钥无法直接读取,只能通过行为推断

设备无法直接读取 Windows 是否删除了配对密钥。

只能根据以下现象推断:

  • 已绑定 slot 上能否顺利加密
  • 是否重新发起 pairing
  • 是否出现安全失败或 SMP 冲突

因此设计 C2 这类“自动清旧 bond 并重配”的逻辑时,必须以“推断”而不是“查询”来实现。

5.5 连接事件与安全事件分工要清楚

推荐分层:

  • CONNECTED 阶段处理 peer 归属和 slot 自动路由
  • SECURED 阶段确认连接确实落在当前选中 identity
  • DISCONNECTED 阶段清理自动切换或迁移的临时状态

如果所有逻辑都堆到 SECURED:

  • 处理时机偏晚
  • 经常已经进入 SMP/安全过程
  • 更容易触发 host 侧异常

5.6 自动切 slot 时要接受“一次断开再重连”

这是应用策略设计里的现实约束。

当 peer 连错 slot 时:

  • 应用切换到正确 slot
  • 需要让旧连接断开
  • 然后等待 host 按正确 identity 重新连入

不要追求“同一条连接无感切换 identity”这在 BLE identity 语义上不现实,也不稳定。

5.7 bt_foreach_bond() 和地址匹配要谨慎

如果实现 peer 所属 slot 判断,通常会用:

  • bt_conn_get_dst(conn)
  • bt_foreach_bond(local_id, ...)
  • bt_addr_le_cmp()

需要注意:

  • public address 与 random/RPA 地址行为不同
  • 已绑定设备可能因为隐私地址导致匹配行为更复杂
  • 实际测试必须覆盖 Windows、Android、iOS 等 host

当前项目日志里 host 使用 public address因此匹配较直接。

5.8 identity reset 的语义要和 UI/配置动作对齐

在自定义多 slot 实现中,erase_peer() 往往同时包含:

  • 删除 bond
  • reset identity
  • 触发广告重启

因此必须保证 UI 动作和内部语义一致。例如:

  • “清当前 slot 配对” 是否意味着彻底 reset 当前 identity
  • “清全部配对” 是否会影响所有 slot 广播身份

如果定义不清,后续调试会非常混乱。

5.9 必须大量依赖日志建立可观测性

自定义 BLE bond 不像 CAF 默认版那样简单。建议至少保留以下日志:

  • 当前 slot 与 stack identity
  • settings 恢复结果
  • slot 切换请求
  • PEER_OPERATION_SELECTED
  • 连接建立的 info.id
  • SECURED 时的 identity 匹配结果
  • peer 所属 slot 自动识别结果
  • 自动切 slot 开始与结束

没有这些日志,多身份调试成本会非常高。


6. 推荐的实现分层

为了避免自定义 BLE bond 过度膨胀,建议分层如下:

  • ble_bond 负责 slot/identity 映射、持久化状态、peer 归属、配对策略
  • ble_adv 负责在指定 identity 上广播
  • slot_name / GAP name provider 负责名称策略
  • slot_ctrl 负责把按键或配置命令转换成 slot 选择请求
  • ble_state 负责连接、安全、断开事件广播

这个分层里最重要的原则是:

  • ble_adv 不理解 slot 语义
  • ble_state 不理解 slot 策略
  • slot 语义统一由自定义 ble_bond 决策

7. 当前项目自定义 BLE Bond 已实现的内容

当前 new_kbd 自定义 ble_bond 已经具备:

  • 3 个应用 slot
  • slot 到 identity 的持久化映射
  • 当前 slot 的持久化保存
  • settings_loader 后初始化 identity
  • PEER_OPERATION_SELECTED 广播给 ble_adv
  • slot 切换时主动断开现有 LE 连接
  • SECURED 时校验当前连接是否落在期望 identity
  • C1: peer 连错 slot 时自动识别所属 slot 并切回

尚未自动化的部分:

  • C2: 主机端删密钥后,本地自动删除旧 bond 并允许重配
  • peer 迁移到新 slot 的完整自动化策略
  • 更复杂 host 场景下的异常恢复

8. 结论

CAF 自带 ble_bond 是“单 identity 的 bond 擦除模块”。 一旦要支持多 slot、多 identity、多 host 路由,自定义 ble_bond 就不再只是替代品,而是整套 BLE peer 管理策略的核心。

相对 CAF 自带实现,额外工作主要集中在:

  • identity 规划
  • 状态持久化
  • ble_adv/ble_state 的事件协同
  • slot 选择与连接路由
  • peer 归属识别
  • 多 host / host 缓存 / bond 生命周期处理

实现时最需要注意的是:

  • Kconfig 接线必须正确
  • 广播 identity 与当前 slot 必须严格一致
  • 广告名/GAP 名策略必须统一
  • host 删除配对记录不等于本地 bond 已删除
  • 自动切 slot 和重连应被视为正常流程,而不是异常