docs(ble_bond): 添加自定义BLE Bond多身份连接设计说明文档

新增详细的自定义BLE Bond设计文档,涵盖多身份连接场景的支持说明。
文档内容包括:CAF自带BLE Bond与自定义BLE Bond的差异对比、架构图解、
多身份实现的关键工作点、实现注意事项以及推荐的分层设计方案。
This commit is contained in:
2026-03-20 15:54:21 +08:00
parent a46b7ad8b8
commit 7e0f224ec8

View File

@@ -0,0 +1,411 @@
# 自定义 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 架构
```mermaid
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 架构
```mermaid
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 和重连应被视为正常流程,而不是异常