文章目录
- 一、经典蓝牙协议栈概览
- 二、设备发现流程
-
- [2.1 发现过程涉及的角色](#2.1 发现过程涉及的角色)
- [2.2 查询的两个阶段](#2.2 查询的两个阶段)
- [2.3 关键 HCI 命令与事件](#2.3 关键 HCI 命令与事件)
- [2.4 设备发现流程图](#2.4 设备发现流程图)
- [2.5 发现结果的关键字段解析](#2.5 发现结果的关键字段解析)
- [2.6 发现的底层细节(跳频机制)](#2.6 发现的底层细节(跳频机制))
- 三、连接建立流程
-
- [3.1 Paging 的核心原理](#3.1 Paging 的核心原理)
- [3.2 连接建立步骤](#3.2 连接建立步骤)
- [3.3 连接建立详细流程图](#3.3 连接建立详细流程图)
- [3.4 连接过程中的 LMP 协商细节](#3.4 连接过程中的 LMP 协商细节)
- [3.5 连接建立的时序图](#3.5 连接建立的时序图)
- [3.6 与 BLE 对比(开发提示)](#3.6 与 BLE 对比(开发提示))
- 四、配对与绑定
-
- [4.1 核心概念](#4.1 核心概念)
- [4.2 两种配对模式](#4.2 两种配对模式)
-
- [4.2.1 Legacy Pairing(传统配对,蓝牙 2.0 及以前)](#4.2.1 Legacy Pairing(传统配对,蓝牙 2.0 及以前))
- [4.2.2 Secure Simple Pairing(SSP,蓝牙 2.1+)](#4.2.2 Secure Simple Pairing(SSP,蓝牙 2.1+))
- [4.3 配对与绑定完整流程图(SSP 为主)](#4.3 配对与绑定完整流程图(SSP 为主))
- [4.4 认证(Authentication)的质询-响应细节](#4.4 认证(Authentication)的质询-响应细节)
- [4.5 绑定的数据存储结构](#4.5 绑定的数据存储结构)
- 五、服务发现流程
-
- [5.1 SDP 的基本模型](#5.1 SDP 的基本模型)
- [5.2 SDP 查询的两种方式](#5.2 SDP 查询的两种方式)
- [5.3 服务发现详细流程图](#5.3 服务发现详细流程图)
- [5.4 SDP 时序图](#5.4 SDP 时序图)
- [5.5 常见 Profile 的 SDP 查询结果示例](#5.5 常见 Profile 的 SDP 查询结果示例)
- 六、四大流程的完整时序串联
- 七、开发常见问题与调试要点
-
- [7.1 各流程常见失败点速查](#7.1 各流程常见失败点速查)
- [7.2 流程间状态依赖关系](#7.2 流程间状态依赖关系)
- [7.3 核心要点回顾](#7.3 核心要点回顾)
一、经典蓝牙协议栈概览
在深入各流程之前,先明确经典蓝牙协议栈的分层结构,这是理解后续所有流程的基础:
#mermaid-svg-ExQfsCxOiZY50d5U{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ExQfsCxOiZY50d5U .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ExQfsCxOiZY50d5U .error-icon{fill:#552222;}#mermaid-svg-ExQfsCxOiZY50d5U .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ExQfsCxOiZY50d5U .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ExQfsCxOiZY50d5U .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ExQfsCxOiZY50d5U .marker.cross{stroke:#333333;}#mermaid-svg-ExQfsCxOiZY50d5U svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ExQfsCxOiZY50d5U p{margin:0;}#mermaid-svg-ExQfsCxOiZY50d5U .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ExQfsCxOiZY50d5U .cluster-label text{fill:#333;}#mermaid-svg-ExQfsCxOiZY50d5U .cluster-label span{color:#333;}#mermaid-svg-ExQfsCxOiZY50d5U .cluster-label span p{background-color:transparent;}#mermaid-svg-ExQfsCxOiZY50d5U .label text,#mermaid-svg-ExQfsCxOiZY50d5U span{fill:#333;color:#333;}#mermaid-svg-ExQfsCxOiZY50d5U .node rect,#mermaid-svg-ExQfsCxOiZY50d5U .node circle,#mermaid-svg-ExQfsCxOiZY50d5U .node ellipse,#mermaid-svg-ExQfsCxOiZY50d5U .node polygon,#mermaid-svg-ExQfsCxOiZY50d5U .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ExQfsCxOiZY50d5U .rough-node .label text,#mermaid-svg-ExQfsCxOiZY50d5U .node .label text,#mermaid-svg-ExQfsCxOiZY50d5U .image-shape .label,#mermaid-svg-ExQfsCxOiZY50d5U .icon-shape .label{text-anchor:middle;}#mermaid-svg-ExQfsCxOiZY50d5U .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ExQfsCxOiZY50d5U .rough-node .label,#mermaid-svg-ExQfsCxOiZY50d5U .node .label,#mermaid-svg-ExQfsCxOiZY50d5U .image-shape .label,#mermaid-svg-ExQfsCxOiZY50d5U .icon-shape .label{text-align:center;}#mermaid-svg-ExQfsCxOiZY50d5U .node.clickable{cursor:pointer;}#mermaid-svg-ExQfsCxOiZY50d5U .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ExQfsCxOiZY50d5U .arrowheadPath{fill:#333333;}#mermaid-svg-ExQfsCxOiZY50d5U .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ExQfsCxOiZY50d5U .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ExQfsCxOiZY50d5U .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExQfsCxOiZY50d5U .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ExQfsCxOiZY50d5U .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExQfsCxOiZY50d5U .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ExQfsCxOiZY50d5U .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ExQfsCxOiZY50d5U .cluster text{fill:#333;}#mermaid-svg-ExQfsCxOiZY50d5U .cluster span{color:#333;}#mermaid-svg-ExQfsCxOiZY50d5U div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ExQfsCxOiZY50d5U .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ExQfsCxOiZY50d5U rect.text{fill:none;stroke-width:0;}#mermaid-svg-ExQfsCxOiZY50d5U .icon-shape,#mermaid-svg-ExQfsCxOiZY50d5U .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExQfsCxOiZY50d5U .icon-shape p,#mermaid-svg-ExQfsCxOiZY50d5U .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ExQfsCxOiZY50d5U .icon-shape .label rect,#mermaid-svg-ExQfsCxOiZY50d5U .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExQfsCxOiZY50d5U .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ExQfsCxOiZY50d5U .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ExQfsCxOiZY50d5U :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 控制器层 (Host Controller Interface 之下)
核心协议层
Profile 层
应用层
Profile 应用
A2DP / HFP / SPP / AVRCP
A2DP
HFP/HSP
SPP
AVRCP
SDP
服务发现协议
RFCOMM
串口仿真
L2CAP
逻辑链路控制与适配
AVDTP / AVCTP
HCI 主机控制接口
Baseband 基带
Link Manager 链路管理器 LM/LMP
Radio 射频 2.4GHz
关键概念:
| 概念 | 说明 |
|---|---|
| Inquiry(查询) | 设备发现机制,通过广播/响应方式找到周围设备 |
| Paging(寻呼) | 连接建立机制,主设备主动呼叫从设备 |
| ACL 链路 | 异步无连接链路,承载数据传输(L2CAP 走 ACL) |
| SCO/eSCO 链路 | 同步链路,承载语音(HFP 走 SCO/eSCO) |
| Pairing(配对) | 交换密钥、完成认证的过程 |
| Bonding(绑定) | 将配对结果(Link Key)持久化存储 |
| SDP | 服务发现协议,查询对端支持哪些服务及连接参数 |
| LMP | 链路管理协议,负责链路建立、认证、加密协商等 |
二、设备发现流程
设备发现的核心是 Inquiry(查询)过程。经典蓝牙设备默认不会被"直接看见",需要通过 Inquiry 扫描来交换设备地址、时钟、设备类别等基本信息。
2.1 发现过程涉及的角色
- Inquirer(查询方 / 主设备候选):发起查询,寻找周围设备。
- Inquiry Scanner(被查询方):周期性监听 Inquiry 广播并回复的设备。
2.2 查询的两个阶段
- Inquiry(查询):查询方发送 ID 包(含 GIAC 通用查询访问码),被查询方返回 FHS 包(包含 BD_ADDR、时钟、设备类型等信息)。
- Inquiry Response(查询响应) :被查询方在跳频序列上回应,查询方收集响应结果,随后通常还会执行 RSSI 读取(通过 HCI_Read_RSSI)获取信号强度。
2.3 关键 HCI 命令与事件
| 方向 | HCI 指令/事件 | 作用 |
|---|---|---|
| Host → Controller | HCI_Inquiry |
启动查询,可指定 LAP(GIAC/LIAC)、查询时长 |
| Controller → Host | HCI_Inquiry_Result / HCI_Inquiry_Result_With_RSSI / HCI_Extended_Inquiry_Result (EIR) |
返回发现的设备信息(地址、CoD、RSSI、设备名称等) |
| Host → Controller | HCI_Inquiry_Cancel |
提前终止查询 |
| Controller → Host | HCI_Inquiry_Complete |
查询完成事件 |
EIR(Extended Inquiry Response):允许被查询方在响应中附带更多数据(如设备名称、支持的 UUID 列表、厂商自定义数据),减少后续连接后再读取的次数。
2.4 设备发现流程图
#mermaid-svg-5uqQwjyJvTepDP7D{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5uqQwjyJvTepDP7D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5uqQwjyJvTepDP7D .error-icon{fill:#552222;}#mermaid-svg-5uqQwjyJvTepDP7D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5uqQwjyJvTepDP7D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5uqQwjyJvTepDP7D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5uqQwjyJvTepDP7D .marker.cross{stroke:#333333;}#mermaid-svg-5uqQwjyJvTepDP7D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5uqQwjyJvTepDP7D p{margin:0;}#mermaid-svg-5uqQwjyJvTepDP7D .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5uqQwjyJvTepDP7D .cluster-label text{fill:#333;}#mermaid-svg-5uqQwjyJvTepDP7D .cluster-label span{color:#333;}#mermaid-svg-5uqQwjyJvTepDP7D .cluster-label span p{background-color:transparent;}#mermaid-svg-5uqQwjyJvTepDP7D .label text,#mermaid-svg-5uqQwjyJvTepDP7D span{fill:#333;color:#333;}#mermaid-svg-5uqQwjyJvTepDP7D .node rect,#mermaid-svg-5uqQwjyJvTepDP7D .node circle,#mermaid-svg-5uqQwjyJvTepDP7D .node ellipse,#mermaid-svg-5uqQwjyJvTepDP7D .node polygon,#mermaid-svg-5uqQwjyJvTepDP7D .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5uqQwjyJvTepDP7D .rough-node .label text,#mermaid-svg-5uqQwjyJvTepDP7D .node .label text,#mermaid-svg-5uqQwjyJvTepDP7D .image-shape .label,#mermaid-svg-5uqQwjyJvTepDP7D .icon-shape .label{text-anchor:middle;}#mermaid-svg-5uqQwjyJvTepDP7D .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5uqQwjyJvTepDP7D .rough-node .label,#mermaid-svg-5uqQwjyJvTepDP7D .node .label,#mermaid-svg-5uqQwjyJvTepDP7D .image-shape .label,#mermaid-svg-5uqQwjyJvTepDP7D .icon-shape .label{text-align:center;}#mermaid-svg-5uqQwjyJvTepDP7D .node.clickable{cursor:pointer;}#mermaid-svg-5uqQwjyJvTepDP7D .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5uqQwjyJvTepDP7D .arrowheadPath{fill:#333333;}#mermaid-svg-5uqQwjyJvTepDP7D .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5uqQwjyJvTepDP7D .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5uqQwjyJvTepDP7D .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5uqQwjyJvTepDP7D .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5uqQwjyJvTepDP7D .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5uqQwjyJvTepDP7D .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5uqQwjyJvTepDP7D .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5uqQwjyJvTepDP7D .cluster text{fill:#333;}#mermaid-svg-5uqQwjyJvTepDP7D .cluster span{color:#333;}#mermaid-svg-5uqQwjyJvTepDP7D div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5uqQwjyJvTepDP7D .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5uqQwjyJvTepDP7D rect.text{fill:none;stroke-width:0;}#mermaid-svg-5uqQwjyJvTepDP7D .icon-shape,#mermaid-svg-5uqQwjyJvTepDP7D .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5uqQwjyJvTepDP7D .icon-shape p,#mermaid-svg-5uqQwjyJvTepDP7D .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5uqQwjyJvTepDP7D .icon-shape .label rect,#mermaid-svg-5uqQwjyJvTepDP7D .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5uqQwjyJvTepDP7D .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5uqQwjyJvTepDP7D .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5uqQwjyJvTepDP7D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
是
否
否
是
被查询设备侧 (Inquiry Scanner)
是
周期性进入 Inquiry Scan 状态
监听 GIAC 广播
收到 ID 包?
延迟 0~1023 个 slot 后回应 FHS 包
避免多设备同时响应冲突
可选: 携带 EIR 扩展查询响应
包含设备名/UUID列表
Host 应用层: 启动设备发现
Host 发送 HCI_Inquiry 命令
参数: LAP=GIAC 通用查询访问码
Inquiry_Length=10.24s 查询时长
Num_Responses=0 不限数量
Controller 返回
HCI_Command_Complete?
Controller 进入 Inquiry 状态
按 Inquiry 跳频序列发送 ID 广播包
收到某设备 FHS 包
Controller 上报
HCI_Inquiry_Result_With_RSSI 事件
BD_ADDR + Class of Device + RSSI
还有新设备响应?
查询时长结束?
或达到 Num_Responses?
Controller 上报 HCI_Inquiry_Complete
Host 读取设备名称
可选: HCI_Remote_Name_Request
2.5 发现结果的关键字段解析
以 HCI_Inquiry_Result_With_RSSI 事件为例,每个发现条目包含:
- BD_ADDR(48 位蓝牙地址) :设备唯一标识。地址类型分为
00:xx(Public)、40:xx(Random Static)等,经典蓝牙主要使用 Public 地址。 - Page_Scan_Repetition_Mode(PSRM):对端寻呼扫描的重复模式,影响后续 Paging 的调度策略。
- Class of Device(CoD,设备类别):24 位字段,指示设备类型(手机、耳机、电脑、音频设备等)及服务类别(Audio、Rendering、Networking 等),应用层可据此过滤目标设备。
- Clock_Offset :对端本地时钟与查询方时钟的偏移,用于加速后续 Paging 过程。
- RSSI:信号强度,用于距离估计与排序。
2.6 发现的底层细节(跳频机制)
被查询方 (Scanner) 查询方 (Inquirer) 被查询方 (Scanner) 查询方 (Inquirer) #mermaid-svg-IxAsbJeD7eRjqyCr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IxAsbJeD7eRjqyCr .error-icon{fill:#552222;}#mermaid-svg-IxAsbJeD7eRjqyCr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IxAsbJeD7eRjqyCr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IxAsbJeD7eRjqyCr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IxAsbJeD7eRjqyCr .marker.cross{stroke:#333333;}#mermaid-svg-IxAsbJeD7eRjqyCr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IxAsbJeD7eRjqyCr p{margin:0;}#mermaid-svg-IxAsbJeD7eRjqyCr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IxAsbJeD7eRjqyCr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IxAsbJeD7eRjqyCr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IxAsbJeD7eRjqyCr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IxAsbJeD7eRjqyCr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IxAsbJeD7eRjqyCr .sequenceNumber{fill:white;}#mermaid-svg-IxAsbJeD7eRjqyCr #sequencenumber{fill:#333;}#mermaid-svg-IxAsbJeD7eRjqyCr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IxAsbJeD7eRjqyCr .messageText{fill:#333;stroke:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IxAsbJeD7eRjqyCr .labelText,#mermaid-svg-IxAsbJeD7eRjqyCr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .loopText,#mermaid-svg-IxAsbJeD7eRjqyCr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IxAsbJeD7eRjqyCr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IxAsbJeD7eRjqyCr .noteText,#mermaid-svg-IxAsbJeD7eRjqyCr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IxAsbJeD7eRjqyCr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IxAsbJeD7eRjqyCr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IxAsbJeD7eRjqyCr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IxAsbJeD7eRjqyCr .actorPopupMenu{position:absolute;}#mermaid-svg-IxAsbJeD7eRjqyCr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IxAsbJeD7eRjqyCr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IxAsbJeD7eRjqyCr .actor-man circle,#mermaid-svg-IxAsbJeD7eRjqyCr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IxAsbJeD7eRjqyCr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询状态: 每 1.28s 切换 16 个跳频信道之一 loop 每个响应槽 (625μs slot) Inquiry Scan: 以较慢速率轮询监听信道 记录设备信息 + Clock_Offset 若支持 EIR, 随后还有扩展响应数据包 ID 包 (携带 GIAC 访问码) FHS 包 (BD_ADDR + CLK + CoD)
要点总结:
- Inquiry 使用 32 个专用查询跳频信道,采用 A/B 双频段交替策略。
- 被查询方的响应延迟是随机的(0~1023 slots),这是蓝牙规范的冲突避免机制。
- 查询过程不建立连接,两个设备仍处于"未连接"状态。
- 查询时长(Inquiry Length)典型值 10.24s,时长越长发现设备越完整,但功耗和时延越高。
三、连接建立流程
发现设备之后,主设备通过 Paging(寻呼) 过程与目标设备建立 ACL 链路。连接建立后才会进行认证、加密和服务访问。
3.1 Paging 的核心原理
Paging 利用在 Inquiry 阶段获取的 BD_ADDR 和 Clock_Offset:
- 由 BD_ADDR 计算出目标设备的 Page 跳频序列(约 32 个信道)。
- 由 Clock_Offset 推算对端当前使用的信道,从而加速锁定(约 2 个半时隙内可命中),否则退化为全信道轮询(最坏 10ms 以上)。
3.2 连接建立步骤
- 主设备发送
HCI_Create_Connection,携带 BD_ADDR、Packet_Type、Page_Scan_Repetition_Mode、Clock_Offset、Allow_Role_Switch 等参数。 - 控制器执行 Page:主设备在 Page 信道上发送 ID 包(含被叫设备的 Device Access Code, DAC)。
- 从设备在 Page Scan 状态收到 ID 包后回应 ID 包(携带相同 DAC)。
- 主设备发送 FHS 包,其中携带主设备的实时时钟和 BD_ADDR,用于从设备同步跳频序列。
- 从设备回应一个 ACK(NULL/POLL 包),至此 物理链路同步完成。
- 控制器上报
HCI_Connection_Complete事件,Host 获得 Connection_Handle。 - 随后 LMP 层进行链路参数协商(见 3.4)。
3.3 连接建立详细流程图
#mermaid-svg-tuiaMzbEe39nkvLw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tuiaMzbEe39nkvLw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tuiaMzbEe39nkvLw .error-icon{fill:#552222;}#mermaid-svg-tuiaMzbEe39nkvLw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tuiaMzbEe39nkvLw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tuiaMzbEe39nkvLw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tuiaMzbEe39nkvLw .marker.cross{stroke:#333333;}#mermaid-svg-tuiaMzbEe39nkvLw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tuiaMzbEe39nkvLw p{margin:0;}#mermaid-svg-tuiaMzbEe39nkvLw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tuiaMzbEe39nkvLw .cluster-label text{fill:#333;}#mermaid-svg-tuiaMzbEe39nkvLw .cluster-label span{color:#333;}#mermaid-svg-tuiaMzbEe39nkvLw .cluster-label span p{background-color:transparent;}#mermaid-svg-tuiaMzbEe39nkvLw .label text,#mermaid-svg-tuiaMzbEe39nkvLw span{fill:#333;color:#333;}#mermaid-svg-tuiaMzbEe39nkvLw .node rect,#mermaid-svg-tuiaMzbEe39nkvLw .node circle,#mermaid-svg-tuiaMzbEe39nkvLw .node ellipse,#mermaid-svg-tuiaMzbEe39nkvLw .node polygon,#mermaid-svg-tuiaMzbEe39nkvLw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tuiaMzbEe39nkvLw .rough-node .label text,#mermaid-svg-tuiaMzbEe39nkvLw .node .label text,#mermaid-svg-tuiaMzbEe39nkvLw .image-shape .label,#mermaid-svg-tuiaMzbEe39nkvLw .icon-shape .label{text-anchor:middle;}#mermaid-svg-tuiaMzbEe39nkvLw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tuiaMzbEe39nkvLw .rough-node .label,#mermaid-svg-tuiaMzbEe39nkvLw .node .label,#mermaid-svg-tuiaMzbEe39nkvLw .image-shape .label,#mermaid-svg-tuiaMzbEe39nkvLw .icon-shape .label{text-align:center;}#mermaid-svg-tuiaMzbEe39nkvLw .node.clickable{cursor:pointer;}#mermaid-svg-tuiaMzbEe39nkvLw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tuiaMzbEe39nkvLw .arrowheadPath{fill:#333333;}#mermaid-svg-tuiaMzbEe39nkvLw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tuiaMzbEe39nkvLw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tuiaMzbEe39nkvLw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tuiaMzbEe39nkvLw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tuiaMzbEe39nkvLw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tuiaMzbEe39nkvLw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tuiaMzbEe39nkvLw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tuiaMzbEe39nkvLw .cluster text{fill:#333;}#mermaid-svg-tuiaMzbEe39nkvLw .cluster span{color:#333;}#mermaid-svg-tuiaMzbEe39nkvLw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tuiaMzbEe39nkvLw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tuiaMzbEe39nkvLw rect.text{fill:none;stroke-width:0;}#mermaid-svg-tuiaMzbEe39nkvLw .icon-shape,#mermaid-svg-tuiaMzbEe39nkvLw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tuiaMzbEe39nkvLw .icon-shape p,#mermaid-svg-tuiaMzbEe39nkvLw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tuiaMzbEe39nkvLw .icon-shape .label rect,#mermaid-svg-tuiaMzbEe39nkvLw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tuiaMzbEe39nkvLw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tuiaMzbEe39nkvLw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tuiaMzbEe39nkvLw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LMP 链路协商阶段
Paging 物理层同步
已有
无
超时未响应
收到响应
是
否
Host: 请求连接某设备
HCI_Create_Connection
BD_ADDR + Clock_Offset
-
Page_Scan_Repetition_Mode
-
Allow_Role_Switch
Controller: 是否已有到该设备的链路?
直接返回已有 Connection_Handle
流程结束
进入 Page 状态
根据 BD_ADDR 计算 Page 跳频序列
发送 ID 包 含目标 DAC 访问码
从设备在 Page Scan
收到并回 ID 包?
重试 / 退避
重新 Page
主设备发送 FHS 包
携带主设备实时 CLK + BD_ADDR
从设备 ACK 确认
双方跳频序列同步完成
上报 HCI_Connection_Complete
获得 Connection_Handle
ACL 链路建立
LMP 交换: 版本/特性协商
LMP_version_req/res
LMP 协商: 加密模式
LMP_encryption_mode_req
LMP 协商: 时钟偏移/槽偏移
LMP_slot_offset + LMP_timing_accuracy
LMP 请求: 改变链路角色?
LMP_switch_req
主从切换
LMP 设置: 链路监督超时
LMP_supervision_timeout
Host 是否要求认证+加密?
进入认证流程
见第四章
链路就绪
可进行 L2CAP 数据交互
3.4 连接过程中的 LMP 协商细节
ACL 链路建立后,Link Manager 之间自动进行以下 LMP 过程(Host 无需介入):
| LMP 过程 | 内容 | 说明 |
|---|---|---|
LMP_version_req/res |
蓝牙版本、公司标识 | 确定双方支持的特性 |
LMP_features_req/res |
特性掩码(加密、EIR、BR/EDR、5-slot 包等) | 决定后续能力 |
LMP_name_req/res |
设备名称 | 可在 ACL 建立后补读名称 |
LMP_encryption_mode_req |
加密模式(不加密/仅点对点/点对点+广播) | 通常要求先认证 |
LMP_encryption_key_size_req |
密钥长度(7~16 字节) | 经典蓝牙 legacy 配对密钥可协商 |
LMP_switch_req |
主从角色切换 | 连接发起方不一定是最终 Master |
LMP_supervision_timeout |
链路监督超时(0.625ms~32s) | 超时未收到包则断开,用于死链检测 |
3.5 连接建立的时序图
Slave Host Master Host Slave (被叫方) Master (发起方) Slave Host Master Host Slave (被叫方) Master (发起方) #mermaid-svg-kGOciiyFLSrgEGl8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kGOciiyFLSrgEGl8 .error-icon{fill:#552222;}#mermaid-svg-kGOciiyFLSrgEGl8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kGOciiyFLSrgEGl8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kGOciiyFLSrgEGl8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kGOciiyFLSrgEGl8 .marker.cross{stroke:#333333;}#mermaid-svg-kGOciiyFLSrgEGl8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kGOciiyFLSrgEGl8 p{margin:0;}#mermaid-svg-kGOciiyFLSrgEGl8 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kGOciiyFLSrgEGl8 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kGOciiyFLSrgEGl8 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-kGOciiyFLSrgEGl8 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-kGOciiyFLSrgEGl8 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-kGOciiyFLSrgEGl8 .sequenceNumber{fill:white;}#mermaid-svg-kGOciiyFLSrgEGl8 #sequencenumber{fill:#333;}#mermaid-svg-kGOciiyFLSrgEGl8 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-kGOciiyFLSrgEGl8 .messageText{fill:#333;stroke:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kGOciiyFLSrgEGl8 .labelText,#mermaid-svg-kGOciiyFLSrgEGl8 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .loopText,#mermaid-svg-kGOciiyFLSrgEGl8 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kGOciiyFLSrgEGl8 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-kGOciiyFLSrgEGl8 .noteText,#mermaid-svg-kGOciiyFLSrgEGl8 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-kGOciiyFLSrgEGl8 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kGOciiyFLSrgEGl8 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kGOciiyFLSrgEGl8 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kGOciiyFLSrgEGl8 .actorPopupMenu{position:absolute;}#mermaid-svg-kGOciiyFLSrgEGl8 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-kGOciiyFLSrgEGl8 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kGOciiyFLSrgEGl8 .actor-man circle,#mermaid-svg-kGOciiyFLSrgEGl8 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-kGOciiyFLSrgEGl8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Page 状态: 32 信道跳频寻呼 物理同步完成, 进入 Connection 状态 LMP 自动协商 ACL 链路就绪, Connection_Handle 分配 HCI_Create_Connection(BD_ADDR, CLK_offset) ID 包 (DAC) ID 包 (DAC) 响应 FHS 包 (含 Master CLK, BD_ADDR) POLL/NULL (ACK) HCI_Connection_Complete HCI_Connection_Complete LMP_version_req LMP_version_res LMP_features_req LMP_features_res LMP_encryption_mode_req LMP_accepted
3.6 与 BLE 对比(开发提示)
- 经典蓝牙 Paging 是定向快速连接(利用时钟偏移加速),而 BLE 是周期性广播+扫描窗口机制。
- 经典蓝牙连接建立时间典型为 < 100ms(已知时钟偏移时更快),BLE 取决于广播间隔,通常为几十到几百 ms。
四、配对与绑定
配对(Pairing)解决认证与密钥生成 问题;绑定(Bonding)解决密钥持久化 问题。经典蓝牙的配对由 LMP 认证 + LMP 配对过程完成。
4.1 核心概念
| 概念 | 说明 |
|---|---|
| Link Key | 链路密钥,双方共享的 128 位密钥,是配对的核心产物 |
| PIN Code | 传统配对(Legacy Pairing)中用户输入的密码(4~16 位数字/字符) |
| Simple Pairing (SSP) | 蓝牙 2.1+ 引入的安全简单配对,支持 Numeric Comparison、Passkey Entry、Just Works、Out-of-Band (OOB) 四种模式 |
| Authentication | 基于 Link Key 的质询-响应(Challenge-Response),每次连接都会执行 |
| Bonding | 将 Link Key 写入双方 Flash(NVRAM)的持久化动作 |
| Encryption | 基于认证后的 Cipher Key(由 Link Key 派生:E3 算法)对 ACL 载荷加密 |
4.2 两种配对模式
4.2.1 Legacy Pairing(传统配对,蓝牙 2.0 及以前)
双方由 PIN 码生成 Init/Comb Key:
#mermaid-svg-DJJ8ktxGraUZ3Xf8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .error-icon{fill:#552222;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .marker.cross{stroke:#333333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 p{margin:0;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster-label text{fill:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster-label span{color:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster-label span p{background-color:transparent;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .label text,#mermaid-svg-DJJ8ktxGraUZ3Xf8 span{fill:#333;color:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node rect,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node circle,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node ellipse,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node polygon,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .rough-node .label text,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node .label text,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .image-shape .label,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .rough-node .label,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node .label,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .image-shape .label,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .icon-shape .label{text-align:center;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node.clickable{cursor:pointer;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .arrowheadPath{fill:#333333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster text{fill:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .cluster span{color:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .icon-shape,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .icon-shape p,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .icon-shape .label rect,#mermaid-svg-DJJ8ktxGraUZ3Xf8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DJJ8ktxGraUZ3Xf8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
双方输入相同 PIN Code
各自计算:
E21 算法基于 PIN + 对方地址
生成 Initialization Key K_init
交换随机数 RAND 与质询
计算 Comb Key
K_link = E21 K_init, RAND, BD_ADDR
得到最终 Link Key
执行 LMP 认证质询-响应
验证双方密钥一致
认证通过?
配对成功, 进入绑定/加密
配对失败: PIN 错误或超时
缺点:PIN 短则安全性弱(易受暴力破解);无用户界面设备(耳机)只能用固定 PIN(如 0000/1234),安全性差。因此蓝牙 2.1 引入 SSP。
4.2.2 Secure Simple Pairing(SSP,蓝牙 2.1+)
SSP 基于 Diffie-Hellman(使用 P-192 椭圆曲线或 P-256,取决于 Secure Connections),核心流程为四个阶段:
- Public Key Exchange:交换公钥。
- Authentication Stage 1:根据 IO Capability 选择四种模式之一完成用户确认。
- Authentication Stage 2:双方确认 DH Key 一致(通过 Nonce 校验)。
- Link Key Calculation:由 DH Key 派生 Link Key。
四种关联模型:
| 模式 | IO 要求 | 中间人防护 | 适用场景 |
|---|---|---|---|
| Numeric Comparison | 双方均有显示 | 有 | 手机-电脑 |
| Passkey Entry | 一方显示一方输入 | 有 | 手机-键盘 |
| Just Works | 无显示无输入 | 无 | 耳机、音箱(仅抗被动窃听) |
| Out-of-Band (OOB) | 通过 NFC/其他通道交换 | 有 | NFC 辅助配对 |
4.3 配对与绑定完整流程图(SSP 为主)
#mermaid-svg-pOsRkRNTG9JjFsQv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pOsRkRNTG9JjFsQv .error-icon{fill:#552222;}#mermaid-svg-pOsRkRNTG9JjFsQv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pOsRkRNTG9JjFsQv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pOsRkRNTG9JjFsQv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pOsRkRNTG9JjFsQv .marker.cross{stroke:#333333;}#mermaid-svg-pOsRkRNTG9JjFsQv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pOsRkRNTG9JjFsQv p{margin:0;}#mermaid-svg-pOsRkRNTG9JjFsQv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster-label text{fill:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster-label span{color:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster-label span p{background-color:transparent;}#mermaid-svg-pOsRkRNTG9JjFsQv .label text,#mermaid-svg-pOsRkRNTG9JjFsQv span{fill:#333;color:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv .node rect,#mermaid-svg-pOsRkRNTG9JjFsQv .node circle,#mermaid-svg-pOsRkRNTG9JjFsQv .node ellipse,#mermaid-svg-pOsRkRNTG9JjFsQv .node polygon,#mermaid-svg-pOsRkRNTG9JjFsQv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pOsRkRNTG9JjFsQv .rough-node .label text,#mermaid-svg-pOsRkRNTG9JjFsQv .node .label text,#mermaid-svg-pOsRkRNTG9JjFsQv .image-shape .label,#mermaid-svg-pOsRkRNTG9JjFsQv .icon-shape .label{text-anchor:middle;}#mermaid-svg-pOsRkRNTG9JjFsQv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pOsRkRNTG9JjFsQv .rough-node .label,#mermaid-svg-pOsRkRNTG9JjFsQv .node .label,#mermaid-svg-pOsRkRNTG9JjFsQv .image-shape .label,#mermaid-svg-pOsRkRNTG9JjFsQv .icon-shape .label{text-align:center;}#mermaid-svg-pOsRkRNTG9JjFsQv .node.clickable{cursor:pointer;}#mermaid-svg-pOsRkRNTG9JjFsQv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pOsRkRNTG9JjFsQv .arrowheadPath{fill:#333333;}#mermaid-svg-pOsRkRNTG9JjFsQv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pOsRkRNTG9JjFsQv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pOsRkRNTG9JjFsQv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pOsRkRNTG9JjFsQv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pOsRkRNTG9JjFsQv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pOsRkRNTG9JjFsQv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster text{fill:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv .cluster span{color:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pOsRkRNTG9JjFsQv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pOsRkRNTG9JjFsQv rect.text{fill:none;stroke-width:0;}#mermaid-svg-pOsRkRNTG9JjFsQv .icon-shape,#mermaid-svg-pOsRkRNTG9JjFsQv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pOsRkRNTG9JjFsQv .icon-shape p,#mermaid-svg-pOsRkRNTG9JjFsQv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pOsRkRNTG9JjFsQv .icon-shape .label rect,#mermaid-svg-pOsRkRNTG9JjFsQv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pOsRkRNTG9JjFsQv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pOsRkRNTG9JjFsQv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pOsRkRNTG9JjFsQv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 绑定 Bonding 阶段 (Host 可选)
SSP 配对阶段 (P0)
有
无
Numeric Comparison
Passkey Entry
Just Works
OOB
失败
通过
否
是
是
否
触发条件: ACL 建立后
Host 请求认证/加密
或访问受保护服务
发起方发送 LMP_in_rand
携带 128-bit 随机数 RAND
对端是否有双方
已绑定的 Link Key?
进入 Authentication
质询-响应阶段 D
进入配对 Pairing 流程
阶段1: 交换 IO Capability
LMP_IO_capability_req/res
决定关联模型
阶段2: 交换公钥
DH Key 协商开始
IO 决定关联模型
双方显示 6 位数字
用户确认一致
一方显示 6 位数字
另一方输入
无用户交互
自动继续
通过 NFC 等带外信道
交换确认数据
阶段3: 认证确认
校验 DH Key 一致
HMAC-SHA-256 Nonce
阶段4: 计算 Link Key
f5 函数派生
双方各自执行 LMP 认证:
验证方发 LMP_in_rand RAND
声称方用 Link Key 计算 SRES = E1 Key, RAND, BD_ADDR
回送 LMP_sres
SRES 校验通过?
上报 HCI_Authentication_Complete
Status = 错误 认证失败
可选重试/重新配对
Host 是否请求加密?
HCI_Set_Connection_Encryption
认证完成
可继续明文通信
LMP_encryption_mode_req
协商加密模式与密钥长度
基于 Link Key 派生 Cipher Key
E3: Kcipher = E3 LinkKey, RAND, COF
双方启用硬件加密引擎
ACL 载荷 AES 加密传输
Host 决定绑定?
HCI_Write_Stored_Link_Key
双方 Link Key + BD_ADDR
写入 NVRAM 持久化
后续连接免配对
Link Key 仅存内存
断链后失效
配对+认证+加密 完成
4.4 认证(Authentication)的质询-响应细节
每次连接建立后,只要访问受保护资源或双方要求加密,都会执行 LMP 认证。这一过程每次都执行,与是否已绑定无关:
Claimant 声称方 Verifier 验证方 (主动发起) Claimant 声称方 Verifier 验证方 (主动发起) #mermaid-svg-BsYybRkymG3KUgJI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BsYybRkymG3KUgJI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BsYybRkymG3KUgJI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BsYybRkymG3KUgJI .error-icon{fill:#552222;}#mermaid-svg-BsYybRkymG3KUgJI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BsYybRkymG3KUgJI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BsYybRkymG3KUgJI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BsYybRkymG3KUgJI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BsYybRkymG3KUgJI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BsYybRkymG3KUgJI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BsYybRkymG3KUgJI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BsYybRkymG3KUgJI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BsYybRkymG3KUgJI .marker.cross{stroke:#333333;}#mermaid-svg-BsYybRkymG3KUgJI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BsYybRkymG3KUgJI p{margin:0;}#mermaid-svg-BsYybRkymG3KUgJI .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BsYybRkymG3KUgJI text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BsYybRkymG3KUgJI .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BsYybRkymG3KUgJI .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BsYybRkymG3KUgJI .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BsYybRkymG3KUgJI .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BsYybRkymG3KUgJI #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BsYybRkymG3KUgJI .sequenceNumber{fill:white;}#mermaid-svg-BsYybRkymG3KUgJI #sequencenumber{fill:#333;}#mermaid-svg-BsYybRkymG3KUgJI #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BsYybRkymG3KUgJI .messageText{fill:#333;stroke:none;}#mermaid-svg-BsYybRkymG3KUgJI .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BsYybRkymG3KUgJI .labelText,#mermaid-svg-BsYybRkymG3KUgJI .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BsYybRkymG3KUgJI .loopText,#mermaid-svg-BsYybRkymG3KUgJI .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BsYybRkymG3KUgJI .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BsYybRkymG3KUgJI .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BsYybRkymG3KUgJI .noteText,#mermaid-svg-BsYybRkymG3KUgJI .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BsYybRkymG3KUgJI .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BsYybRkymG3KUgJI .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BsYybRkymG3KUgJI .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BsYybRkymG3KUgJI .actorPopupMenu{position:absolute;}#mermaid-svg-BsYybRkymG3KUgJI .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-BsYybRkymG3KUgJI .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BsYybRkymG3KUgJI .actor-man circle,#mermaid-svg-BsYybRkymG3KUgJI line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BsYybRkymG3KUgJI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SRES = E1(Link Key, RAND, Claimant BD_ADDR) 本地同样计算 E1 结果并比较 认证通过 失败, 可触发重新配对 alt SRES 匹配 SRES 不匹配 LMP_in_rand (128-bit RAND) LMP_sres (32-bit SRES) LMP_accepted LMP_rejected (Authentication Failure)
4.5 绑定的数据存储结构
绑定信息在 Host 侧通常按如下结构存储:
NVRAM 绑定记录(每设备一条):
┌─────────────────────────────────┐
│ BD_ADDR (6 bytes) │
│ Link Key (16 bytes) │
│ Key Type (4 bits): │
│ - Combination Key │
│ - Local Unit Key │
│ - Remote Unit Key │
│ - Debug Combination Key │
│ - Unauthenticated Combination │
│ Key (Just Works 产生) │
│ - Authenticated Combination │
│ Key │
│ PIN Length (legacy, 1 byte) │
│ 配对时使用的 IO Capabilities │
│ 上次连接时间(应用层可选) │
└─────────────────────────────────┘
开发提示:
- 使用
HCI_Read_Stored_Link_Key/HCI_Write_Stored_Link_Key管理绑定信息。 - Just Works 模式 产生的 Key Type 是 Unauthenticated,意味着它不能用于要求 MITM 防护的场景(如某些 HID 键盘服务)。
- 清除配对记录时要通知对端删除 (
HCI_Delete_Stored_Link_Key+ LMP),否则对端仍持有旧 Key,会导致下次连接认证失败。
五、服务发现流程
ACL 链路建立并(可选)完成认证加密后,应用层通常需要查询对端支持哪些 Profile 服务、使用哪个 L2CAP PSM、RFCOMM 通道号是多少 ------这就是 SDP(Service Discovery Protocol)。
5.1 SDP 的基本模型
- SDP Server:运行于每个蓝牙设备上,维护一张服务记录表(Service Record)。
- SDP Client:查询方,通过 L2CAP(PSM = 0x0001)与 Server 通信。
- Service Record :由一系列 Attribute(属性) 组成,每个属性是
(Attribute ID, Attribute Value)对,用 UUID 标识。
关键属性:
| Attribute ID | 名称 | 说明 |
|---|---|---|
| 0x0000 | ServiceRecordHandle | 记录句柄 |
| 0x0001 | ServiceClassIDList | 服务类 UUID 列表(核心) |
| 0x0002 | ServiceRecordState | 记录状态(变更检测) |
| 0x0004 | ProtocolDescriptorList | 协议栈描述(L2CAP PSM、RFCOMM 通道号等,核心) |
| 0x0005 | BrowseGroupList | 浏览分组(PublicBrowseGroup = 0x1002) |
| 0x0006 | LanguageBaseAttributeIDList | 语言基准 |
| 0x0009 | BluetoothProfileDescriptorList | Profile 描述(如 A2DP Sink UUID + 版本) |
| 0x0100 | ServiceName | 服务名称 |
5.2 SDP 查询的两种方式
- Service Search(服务搜索):按 Service UUID 搜索,返回匹配记录的 Record Handle 列表。
- Service Attribute Retrieval(属性获取):按 Record Handle + Attribute ID 列表,提取具体属性(主要是 ProtocolDescriptorList 获取 PSM/通道号)。
实际中常组合为 Service Search Attribute Request(SSAR):一次性按 UUID 搜索并返回指定属性。
5.3 服务发现详细流程图
#mermaid-svg-8CEqiNFiyL0W1lIO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8CEqiNFiyL0W1lIO .error-icon{fill:#552222;}#mermaid-svg-8CEqiNFiyL0W1lIO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8CEqiNFiyL0W1lIO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8CEqiNFiyL0W1lIO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8CEqiNFiyL0W1lIO .marker.cross{stroke:#333333;}#mermaid-svg-8CEqiNFiyL0W1lIO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8CEqiNFiyL0W1lIO p{margin:0;}#mermaid-svg-8CEqiNFiyL0W1lIO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster-label text{fill:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster-label span{color:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster-label span p{background-color:transparent;}#mermaid-svg-8CEqiNFiyL0W1lIO .label text,#mermaid-svg-8CEqiNFiyL0W1lIO span{fill:#333;color:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO .node rect,#mermaid-svg-8CEqiNFiyL0W1lIO .node circle,#mermaid-svg-8CEqiNFiyL0W1lIO .node ellipse,#mermaid-svg-8CEqiNFiyL0W1lIO .node polygon,#mermaid-svg-8CEqiNFiyL0W1lIO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8CEqiNFiyL0W1lIO .rough-node .label text,#mermaid-svg-8CEqiNFiyL0W1lIO .node .label text,#mermaid-svg-8CEqiNFiyL0W1lIO .image-shape .label,#mermaid-svg-8CEqiNFiyL0W1lIO .icon-shape .label{text-anchor:middle;}#mermaid-svg-8CEqiNFiyL0W1lIO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8CEqiNFiyL0W1lIO .rough-node .label,#mermaid-svg-8CEqiNFiyL0W1lIO .node .label,#mermaid-svg-8CEqiNFiyL0W1lIO .image-shape .label,#mermaid-svg-8CEqiNFiyL0W1lIO .icon-shape .label{text-align:center;}#mermaid-svg-8CEqiNFiyL0W1lIO .node.clickable{cursor:pointer;}#mermaid-svg-8CEqiNFiyL0W1lIO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8CEqiNFiyL0W1lIO .arrowheadPath{fill:#333333;}#mermaid-svg-8CEqiNFiyL0W1lIO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8CEqiNFiyL0W1lIO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8CEqiNFiyL0W1lIO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8CEqiNFiyL0W1lIO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8CEqiNFiyL0W1lIO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8CEqiNFiyL0W1lIO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster text{fill:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO .cluster span{color:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8CEqiNFiyL0W1lIO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8CEqiNFiyL0W1lIO rect.text{fill:none;stroke-width:0;}#mermaid-svg-8CEqiNFiyL0W1lIO .icon-shape,#mermaid-svg-8CEqiNFiyL0W1lIO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8CEqiNFiyL0W1lIO .icon-shape p,#mermaid-svg-8CEqiNFiyL0W1lIO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8CEqiNFiyL0W1lIO .icon-shape .label rect,#mermaid-svg-8CEqiNFiyL0W1lIO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8CEqiNFiyL0W1lIO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8CEqiNFiyL0W1lIO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8CEqiNFiyL0W1lIO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 拒绝
接受
是
否
是
否
应用层: 需要查询对端服务
如查找 A2DP Sink
Host 建立到对端的 ACL 链路
已完成认证/加密
Host 发送 SDP 连接请求
L2CAP 连接 PSM = 0x0001 SDP
对端 SDP Server
接受 L2CAP 连接?
失败处理
对端 SDP 不可达
可能是设备类型不支持
L2CAP 通道建立
Config 协商 MTU
构造 SDP_ServiceSearchAttributeRequest
参数1: ServiceSearchPattern
= A2DP Sink UUID 0x110B
参数2: AttributeIDList
= ProtocolDescriptorList 0x0004
- BluetoothProfileDescriptorList 0x0009
响应数据大小
超过 L2CAP MTU?
Server 分段响应
ContinuationState 非空
Client 发送 Continuation 请求
携带 ContinuationState
循环拉取剩余数据
收到完整 SDP 响应
ContinuationState 为空
解析 ProtocolDescriptorList:
L2CAP PSM = AVDTP 0x0019
AVDTP 版本
解析 ProfileDescriptor:
A2DP Profile UUID + v1.3
应用还需要其他服务?
如 AVRCP TG 0x110C
断开 SDP L2CAP 连接
或复用通道继续通信
服务发现完成
应用获得连接所需全部参数
5.4 SDP 时序图
SDP Server (对端) SDP Client (查询方 Host) SDP Server (对端) SDP Client (查询方 Host) #mermaid-svg-jMiHWpnKMiGxwkuG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jMiHWpnKMiGxwkuG .error-icon{fill:#552222;}#mermaid-svg-jMiHWpnKMiGxwkuG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jMiHWpnKMiGxwkuG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jMiHWpnKMiGxwkuG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jMiHWpnKMiGxwkuG .marker.cross{stroke:#333333;}#mermaid-svg-jMiHWpnKMiGxwkuG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jMiHWpnKMiGxwkuG p{margin:0;}#mermaid-svg-jMiHWpnKMiGxwkuG .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jMiHWpnKMiGxwkuG text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jMiHWpnKMiGxwkuG .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-jMiHWpnKMiGxwkuG .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-jMiHWpnKMiGxwkuG #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-jMiHWpnKMiGxwkuG .sequenceNumber{fill:white;}#mermaid-svg-jMiHWpnKMiGxwkuG #sequencenumber{fill:#333;}#mermaid-svg-jMiHWpnKMiGxwkuG #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-jMiHWpnKMiGxwkuG .messageText{fill:#333;stroke:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jMiHWpnKMiGxwkuG .labelText,#mermaid-svg-jMiHWpnKMiGxwkuG .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .loopText,#mermaid-svg-jMiHWpnKMiGxwkuG .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jMiHWpnKMiGxwkuG .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jMiHWpnKMiGxwkuG .noteText,#mermaid-svg-jMiHWpnKMiGxwkuG .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-jMiHWpnKMiGxwkuG .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jMiHWpnKMiGxwkuG .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jMiHWpnKMiGxwkuG .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jMiHWpnKMiGxwkuG .actorPopupMenu{position:absolute;}#mermaid-svg-jMiHWpnKMiGxwkuG .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-jMiHWpnKMiGxwkuG .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jMiHWpnKMiGxwkuG .actor-man circle,#mermaid-svg-jMiHWpnKMiGxwkuG line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-jMiHWpnKMiGxwkuG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ACL 已建立并完成认证 alt 数据可一次返回 需要分段 解析得到: AVDTP PSM = 0x0019, Profile 版本 L2CAP Connection Request (PSM = 0x0001) L2CAP Connection Response (CID 分配) L2CAP Config Request (MTU 协商) L2CAP Config Response SDP_ServiceSearchAttributeRequest SearchPattern: A2DP Sink UUID AttrList: 0x0004, 0x0009 SDP_Response (Complete) ContinuationState = NULL SDP_Response (Partial) ContinuationState = 0x01... SDP Request + ContinuationState SDP_Response (Complete) ContinuationState = NULL L2CAP Disconnection Request L2CAP Disconnection Response
5.5 常见 Profile 的 SDP 查询结果示例
以搜索 A2DP Audio Sink(音箱/耳机) 为例,返回的属性值:
ServiceClassIDList:
├─ Audio Sink (0x110B)
ProtocolDescriptorList:
├─ L2CAP (0x0100), PSM = AVDTP (0x0019)
└─ AVDTP (0x0019), Version = 0x0103 (v1.3)
BluetoothProfileDescriptorList:
└─ Advanced Audio Distribution (0x110D), v1.3
再以 SPP(串口) 为例,应用真正需要的是 RFCOMM 通道号:
ProtocolDescriptorList:
├─ L2CAP (0x0100), PSM = RFCOMM (0x0003)
└─ RFCOMM (0x0003), Channel = 0x05 ← 应用连接此通道号
六、四大流程的完整时序串联
实际场景中(如手机连接蓝牙音箱听音乐),四大流程依次衔接:
音箱 Controller 音箱 Host 手机 Controller 手机 Host 音箱 Controller 音箱 Host 手机 Controller 手机 Host #mermaid-svg-KJrEcUs3sQo4aR83{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KJrEcUs3sQo4aR83 .error-icon{fill:#552222;}#mermaid-svg-KJrEcUs3sQo4aR83 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KJrEcUs3sQo4aR83 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KJrEcUs3sQo4aR83 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KJrEcUs3sQo4aR83 .marker.cross{stroke:#333333;}#mermaid-svg-KJrEcUs3sQo4aR83 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KJrEcUs3sQo4aR83 p{margin:0;}#mermaid-svg-KJrEcUs3sQo4aR83 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KJrEcUs3sQo4aR83 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KJrEcUs3sQo4aR83 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-KJrEcUs3sQo4aR83 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-KJrEcUs3sQo4aR83 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-KJrEcUs3sQo4aR83 .sequenceNumber{fill:white;}#mermaid-svg-KJrEcUs3sQo4aR83 #sequencenumber{fill:#333;}#mermaid-svg-KJrEcUs3sQo4aR83 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-KJrEcUs3sQo4aR83 .messageText{fill:#333;stroke:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KJrEcUs3sQo4aR83 .labelText,#mermaid-svg-KJrEcUs3sQo4aR83 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .loopText,#mermaid-svg-KJrEcUs3sQo4aR83 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KJrEcUs3sQo4aR83 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-KJrEcUs3sQo4aR83 .noteText,#mermaid-svg-KJrEcUs3sQo4aR83 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-KJrEcUs3sQo4aR83 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KJrEcUs3sQo4aR83 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KJrEcUs3sQo4aR83 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KJrEcUs3sQo4aR83 .actorPopupMenu{position:absolute;}#mermaid-svg-KJrEcUs3sQo4aR83 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-KJrEcUs3sQo4aR83 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KJrEcUs3sQo4aR83 .actor-man circle,#mermaid-svg-KJrEcUs3sQo4aR83 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-KJrEcUs3sQo4aR83 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 设备发现 Inquiry ② 连接建立 Paging ③ 配对与绑定 用户确认 (Just Works/数字比较) Link Key 写入双方 NVRAM (绑定) ④ 服务发现 SDP ⑤ 应用数据传输 (A2DP 音频流) HCI_Inquiry 1 ID 广播 (GIAC) 2 FHS + EIR (名称, CoD=Audio) 3 HCI_Inquiry_Result_With_RSSI 4 HCI_Inquiry_Complete 5 HCI_Create_Connection (BD_ADDR, CLK_Offset) 6 Page (ID, FHS...) 7 同步确认 8 HCI_Connection_Complete (Handle) 9 LMP 版本/特性/加密协商 10 LMP IO Capabilities 交换 11 公钥交换 (DH) 12 LMP 认证 (质询-响应) 13 HCI_Authentication_Complete 14 启用加密 15 L2CAP 连接 PSM=0x0001 16 SDP 查询: A2DP Sink UUID 17 返回: AVDTP PSM=0x0019 18 L2CAP 连接 PSM=0x0019 (AVDTP) 19 AVDTP 流建立 → 音频 RTP 包经加密 ACL 传输 20
七、开发常见问题与调试要点
7.1 各流程常见失败点速查
| 流程 | 常见失败原因 | 排查手段 |
|---|---|---|
| 设备发现 | 对端未处于可发现模式;Inquiry 时长过短;射频干扰 | 用 Ellisys/Frontline 抓包看是否有 FHS 响应 |
| 连接建立 | Clock_Offset 过期(对端时钟漂移);对端 Page Scan 间隔过长;PSRM 不匹配 | 延长 Page Timeout(默认 5.12s,可调至 61.44s) |
| 配对 | Just Works 与 Passkey 模式不匹配;一方删除绑定另一方未删;PIN 错误(Legacy) | 查看 HCI_Authentication_Complete 的 Status 码 |
| 服务发现 | 对端无 SDP Server 响应;Continuation 处理错误导致数据截断 | 解析 SDP 响应的 ContinuationState |
7.2 流程间状态依赖关系
#mermaid-svg-v5rsiuYHxcwppMaa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-v5rsiuYHxcwppMaa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v5rsiuYHxcwppMaa .error-icon{fill:#552222;}#mermaid-svg-v5rsiuYHxcwppMaa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v5rsiuYHxcwppMaa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v5rsiuYHxcwppMaa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v5rsiuYHxcwppMaa .marker.cross{stroke:#333333;}#mermaid-svg-v5rsiuYHxcwppMaa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v5rsiuYHxcwppMaa p{margin:0;}#mermaid-svg-v5rsiuYHxcwppMaa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-v5rsiuYHxcwppMaa .cluster-label text{fill:#333;}#mermaid-svg-v5rsiuYHxcwppMaa .cluster-label span{color:#333;}#mermaid-svg-v5rsiuYHxcwppMaa .cluster-label span p{background-color:transparent;}#mermaid-svg-v5rsiuYHxcwppMaa .label text,#mermaid-svg-v5rsiuYHxcwppMaa span{fill:#333;color:#333;}#mermaid-svg-v5rsiuYHxcwppMaa .node rect,#mermaid-svg-v5rsiuYHxcwppMaa .node circle,#mermaid-svg-v5rsiuYHxcwppMaa .node ellipse,#mermaid-svg-v5rsiuYHxcwppMaa .node polygon,#mermaid-svg-v5rsiuYHxcwppMaa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-v5rsiuYHxcwppMaa .rough-node .label text,#mermaid-svg-v5rsiuYHxcwppMaa .node .label text,#mermaid-svg-v5rsiuYHxcwppMaa .image-shape .label,#mermaid-svg-v5rsiuYHxcwppMaa .icon-shape .label{text-anchor:middle;}#mermaid-svg-v5rsiuYHxcwppMaa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-v5rsiuYHxcwppMaa .rough-node .label,#mermaid-svg-v5rsiuYHxcwppMaa .node .label,#mermaid-svg-v5rsiuYHxcwppMaa .image-shape .label,#mermaid-svg-v5rsiuYHxcwppMaa .icon-shape .label{text-align:center;}#mermaid-svg-v5rsiuYHxcwppMaa .node.clickable{cursor:pointer;}#mermaid-svg-v5rsiuYHxcwppMaa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-v5rsiuYHxcwppMaa .arrowheadPath{fill:#333333;}#mermaid-svg-v5rsiuYHxcwppMaa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-v5rsiuYHxcwppMaa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-v5rsiuYHxcwppMaa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v5rsiuYHxcwppMaa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-v5rsiuYHxcwppMaa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v5rsiuYHxcwppMaa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-v5rsiuYHxcwppMaa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-v5rsiuYHxcwppMaa .cluster text{fill:#333;}#mermaid-svg-v5rsiuYHxcwppMaa .cluster span{color:#333;}#mermaid-svg-v5rsiuYHxcwppMaa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-v5rsiuYHxcwppMaa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-v5rsiuYHxcwppMaa rect.text{fill:none;stroke-width:0;}#mermaid-svg-v5rsiuYHxcwppMaa .icon-shape,#mermaid-svg-v5rsiuYHxcwppMaa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v5rsiuYHxcwppMaa .icon-shape p,#mermaid-svg-v5rsiuYHxcwppMaa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-v5rsiuYHxcwppMaa .icon-shape .label rect,#mermaid-svg-v5rsiuYHxcwppMaa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v5rsiuYHxcwppMaa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-v5rsiuYHxcwppMaa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-v5rsiuYHxcwppMaa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 无
有
是
否
Inquiry
设备发现
Page
ACL 连接建立
有绑定 Link Key?
Pairing
配对生成 Link Key
Authentication
直接认证
需要加密?
Encryption
启用加密
SDP
服务发现
Profile 连接
L2CAP/RFCOMM/AVDTP
应用数据交互
7.3 核心要点回顾
- 设备发现(Inquiry) 是广播式查询,产出 BD_ADDR、CoD、Clock_Offset------其中 Clock_Offset 直接服务于下一步加速连接。
- 连接建立(Paging) 是定向快速同步过程,FHS 包完成跳频同步,之后 LMP 自动完成链路协商。
- 配对 的核心产物是 Link Key ;SSP 通过 DH 密钥交换 + IO 能力匹配四种关联模型解决安全性与易用性矛盾;绑定 即 Link Key 持久化。
- 认证 是基于 Link Key 的质询-响应,每次连接都可执行;加密 密钥由 Link Key 经 E3 派生。
- SDP 运行在 L2CAP PSM 0x0001 上,通过 "UUID 搜索 + 属性提取" 两步(或 SSAR 一步)获取 Profile 连接参数(PSM、RFCOMM Channel 等),是 Profile 连接的前置必经步骤。