本篇我们通过分析一个具体的实例,来直观感受一下 l2cap 中通道的 状态变化。
1. 环境描述:
- 车机: a2dp sink
- 手机: a2dp source
- 场景: 手机主动 触发 连车机
声明一下: 分析的btsnoop 和 logcat 还有源码, 均是作为车机来分析的。
图中 总共分为 第一部分和第二部分。
本文的重点是分析 第一部分。 第二部分不再本篇分析之内,会将第二部分的分析单独分享在 avdtp 章节。
2. 案例分析
从第一部分看,可以分为一下6步:
- Rcvd Connection Request (AVDTP, SCID: 0x0041) : 车机侧接收到 手机发起的 avdtp 连接请求【手机 --> 车机】
- Sent Connection Response - Success (SCID: 0x0041, DCID: 0x0046): 车机向手机发送同意连接【手机 <-- 车机】
- Sent Configure Request (DCID: 0x0041): 车机发起配置请求 【手机 <-- 车机】
- Rcvd Configure Request (DCID: 0x0046):接收到手机的配置请求 【手机 --> 车机】
- Sent Configure Response - Success (SCID: 0x0041): 向手机发送配置应答 【手机 <-- 车机】
- Rcvd Configure Response - Success (SCID: 0x0046): 接收到手机的配置应答 【手机 --> 车机】
我们就依次来分析一下这6 步,在我们协议栈中是如何处理的。
上述几步都是发生在 l2cap 信令通道里面的。
- CID: L2CAP Signaling Channel (0x0001)
所以调用路径基本是在
// system/stack/l2cap/l2c_main.cc
void l2c_rcv_acl_data(BT_HDR* p_msg) {
// 信令通道基本都是调用 process_l2cap_cmd 来处理
/* Send the data through the channel state machine */
if (rcv_cid == L2CAP_SIGNALLING_CID) {
process_l2cap_cmd(p_lcb, p, l2cap_len);
osi_free(p_msg);
return;
}
}
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
tL2C_CONN_INFO con_info;
/* if l2cap command received in CID 1 on top of an LE link, ignore this
* command */
if (p_lcb->transport == BT_TRANSPORT_LE) {
LOG_INFO("Dropping data on CID 1 for LE link");
return;
}
/* Reject the packet if it exceeds the default Signalling Channel MTU */
bool pkt_size_rej = false;
if (pkt_len > L2CAP_DEFAULT_MTU) {
/* Core Spec requires a single response to the first command found in a
* multi-command L2cap packet. If only responses in the packet, then it
* will be ignored. Here we simply mark the bad packet and decide which cmd
* ID to reject later */
pkt_size_rej = true;
LOG_WARN("Signaling pkt_len=%d exceeds MTU size %d", pkt_len,
L2CAP_DEFAULT_MTU);
}
uint8_t* p_next_cmd = p;
uint8_t* p_pkt_end = p + pkt_len;
tL2CAP_CFG_INFO cfg_info;
memset(&cfg_info, 0, sizeof(cfg_info));
/* An L2CAP packet may contain multiple commands */
// 一个 l2cap 包中可能包含多个 命令, 所以这里 需要 while 循环遍历每一个 cmd.
/*
想象你是一个邮局的接信柜台:
1.每个 L2CAP 信令包就像一沓装在一个信封里的指令信。
2.你每次拆开信封(调用 process_l2cap_cmd),要一封一封读信(while 循环)。
3.每封信的信头告诉你是什么业务(连接请求、配置、断开等)。
4. 你交给不同科室处理(switch-case 分发)。
5. 每个科室根据信的内容,更新内部状态(状态机驱动)。
*/
while (true) {
/* Smallest command is 4 bytes */
p = p_next_cmd;
if (p > (p_pkt_end - 4)) break;
uint8_t cmd_code, id;
uint16_t cmd_len;
STREAM_TO_UINT8(cmd_code, p);
STREAM_TO_UINT8(id, p);
STREAM_TO_UINT16(cmd_len, p);
if (cmd_len > BT_SMALL_BUFFER_SIZE) {
LOG_WARN("Command size %u exceeds limit %d", cmd_len,
BT_SMALL_BUFFER_SIZE);
l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, 0, 0);
return;
}
/* Check command length does not exceed packet length */
p_next_cmd = p + cmd_len;
if (p_next_cmd > p_pkt_end) {
LOG_WARN("cmd_len > pkt_len, pkt_len=%d, cmd_len=%d, code=%d", pkt_len,
cmd_len, cmd_code);
break;
}
LOG_DEBUG("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len);
/* Bad L2CAP packet length, look for cmd to reject */
if (pkt_size_rej) {
/* If command found rejected it and we're done, otherwise keep looking */
if (l2c_is_cmd_rejected(cmd_code, id, p_lcb)) {
LOG_WARN("Rejected command %d due to bad packet length", cmd_code);
return;
} else {
LOG_WARN("No need to reject command %d for bad packet len", cmd_code);
continue; /* Look for next cmd/response in current packet */
}
}
// 将提前到的 cmd 分发到对应分支处理
switch (cmd_code) {
case L2CAP_CMD_REJECT:
...
break;
case L2CAP_CMD_CONN_REQ:
...
break;
case L2CAP_CMD_CONN_RSP:
...
break;
case L2CAP_CMD_CONFIG_REQ:
...
break;
case L2CAP_CMD_CONFIG_RSP:
...
break;
case L2CAP_CMD_DISC_REQ:
...
break;
case L2CAP_CMD_DISC_RSP:
...
break;
case L2CAP_CMD_ECHO_REQ:
...
break;
case L2CAP_CMD_INFO_REQ:
...
break;
case L2CAP_CMD_INFO_RSP:
...
break;
default:
LOG_WARN("Bad cmd code: %d", cmd_code);
l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0,
0);
return;
}
}
}
- 我们直接从 process_l2cap_cmd 函数开始分析。
1. 车机侧接收到 avdtp 连接请求
1. log 部分
1081 2025-04-24 15:56:22.504410 vivoMobi_91:b0:62 (cbx) 22:22:96:de:b1:39 (leo 8295 chan) L2CAP 17 Rcvd Connection Request (AVDTP, SCID: 0x0041)
Frame 1081: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 8
CID: L2CAP Signaling Channel (0x0001)
Command: Connection Request
Command Code: Connection Request (0x02)
Command Identifier: 0x11
Command Length: 4
PSM: AVDTP (0x0019)
Source CID: Dynamically Allocated Channel (0x0041)
[Disconnect in frame: 0]
04-24 15:56:22.504630 6130 6190 I bt_l2c_main: packages/modules/Bluetooth/system/stack/l2cap/l2c_main.cc:310 process_l2cap_cmd: cmd_code: 2, id:17, cmd_len:4
04-24 15:56:22.504647 6130 6190 I l2c_utils: packages/modules/Bluetooth/system/stack/l2cap/l2c_utils.cc:1356 l2cu_allocate_ccb: is_dynamic = 1, cid 0x0000
04-24 15:56:22.504669 6130 6190 I bt_l2cap: packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: l2cu_enqueue_ccb CID: 0x0046 priority: 2
04-24 15:56:22.504765 6130 6190 I l2c_link: packages/modules/Bluetooth/system/stack/l2cap/l2c_link.cc:739 l2c_link_adjust_chnl_allocation: CID:0x0046 FCR Mode:0 Priority:2 TxDataRate:1 RxDataRate:1 Quota:200
04-24 15:56:22.504776 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CLOSED [0], event=PEER_CONNECT_REQ [10]
04-24 15:56:22.504783 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:199 l2c_csm_closed: LCID: 0x0046 st: CLOSED evt: PEER_CONNECT_REQ
04-24 15:56:22.504798 6130 6190 I bt_btm_sec: packages/modules/Bluetooth/system/stack/btm/btm_sec.cc:1826 btm_sec_l2cap_access_req: is_originator:0, psm=0x0019
04-24 15:56:22.504813 6130 6190 I bt_btm_sec: packages/modules/Bluetooth/system/stack/btm/btm_sec.cc:1608 btm_sec_l2cap_access_req_by_requirement: Checking l2cap access requirements peer:xx:xx:xx:xx:b0:62 security:0x1082 is_initiator:false
04-24 15:56:22.504822 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: btm_find_or_alloc_dev
04-24 15:56:22.504833 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_sec_l2cap_access_req_by_requirement() sm4:0x11, sec_flags:0x40be, security_required:0x1086 chk:1
04-24 15:56:22.504840 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: (SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x2, enc: x4
04-24 15:56:22.504846 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_sec_check_upgrade()
04-24 15:56:22.504854 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_sec_is_upgrade_possible() is_possible: 0 sec_flags: 0x40be
04-24 15:56:22.504862 6130 6190 I bt_btm_sec: packages/modules/Bluetooth/system/stack/btm/btm_sec.cc:4493 btm_sec_execute_procedure: security_required:0x1086 security_flags:0x40be security_state:BTM_SEC_STATE_IDLE[0]
04-24 15:56:22.504869 6130 6190 I bt_btm_sec: packages/modules/Bluetooth/system/stack/btm/btm_sec.cc:4591 btm_sec_execute_procedure: Encryption not required
# 安全模块检查通过
04-24 15:56:22.504876 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: Security Manager: access granted
04-24 15:56:22.504883 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_sec_l2cap_access_req_by_requirement: p_dev_rec=0xb800004b18420208, clearing callback. old p_callback=0x7a3fcaf9cc
# 回调 l2c_link_sec_comp
04-24 15:56:22.504893 6130 6190 I l2c_link: packages/modules/Bluetooth/system/stack/l2cap/l2c_link.cc:284 l2c_link_sec_comp2: btm_status=BTM_SUCCESS, BD_ADDR=xx:xx:xx:xx:b0:62, transport=BT_TRANSPORT_BR_EDR
2. 源码分析
#define L2CAP_CMD_CONN_REQ 0x02
// process_l2cap_cmd 中对于 Connection Request 的处理
case L2CAP_CMD_CONN_REQ: {
uint16_t rcid; // 变量 rcid,用于存储对端分配的 Channel ID(Remote CID)。
if (p + 4 > p_next_cmd) {
// 检查当前剩余数据是否足够解析 PSM(2 字节)+ Remote CID(2 字节),总共 4 字节。如果不足说明包不完整,记录警告并直接返回。
LOG_WARN("Not enough data for L2CAP_CMD_CONN_REQ");
return;
}
// 从 PDU 中解析出 PSM 和对端为本连接指定的 CID。这里的 con_info 是一个局部或上下文结构,用于传递连接相关信息。
STREAM_TO_UINT16(con_info.psm, p); // con_info.psm = 0x0019:AVDTP
STREAM_TO_UINT16(rcid, p); // rcid = 0x0041
// 查找是否有注册过该 PSM 的服务(RCB = Registration Control Block),PSM 就像是协议层的端口号,只有注册了的 PSM 才能接受连接。
tL2C_RCB* p_rcb = l2cu_find_rcb_by_psm(con_info.psm); // 我们在初始化就已经注册了 0x0019, 所以这里肯定是可以找到 p_rcb 的
if (!p_rcb || con_info.psm == BT_PSM_ATT) {
} else {
// 尝试为此次连接请求分配一个新的 Channel Control Block(信道控制块),这代表一次独立的 L2CAP 信道。
tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0); // p_ccb->chnl_state = CST_CLOSED
// 初始化 CCB:
p_ccb->remote_id = id; // : 当前信令命令的 ID,用于后续响应时对上。
p_ccb->p_rcb = p_rcb; // 记录该连接使用的是哪个 PSM 的注册服务。
p_ccb->remote_cid = rcid; // 对端分配的 Channel ID。
p_ccb->connection_initiator = L2CAP_INITIATOR_REMOTE; // 标记连接是对端发起的
// 启动状态机,处理 `L2CAP_CONNECT_REQ` 事件,进入连接建立流程,通常会发一个 Connection Response 作为回应。
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info);
break;
}
总结:
- 通过 l2cu_find_rcb_by_psm 传入 psm, 找到对应的服务。 这里找到了我们之前蓝牙初始化注册进 l2cap 的 avdtp 服务。
- 通过 l2cu_allocate_ccb 分配 一个 Channel Control Block ,并且将 该通道控制块的状态设置为 CST_CLOSED。 这是该通道的初始状态。
- 通过 l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info), 触发状态机处理 L2CEVT_L2CAP_CONNECT_REQ 事件。
1. l2cu_find_rcb_by_psm
- 函数声明:定义一个名叫
l2cu_find_rcb_by_psm
的函数。 - 输入参数:
psm
,是一个uint16_t
类型的数,表示要查找的 PSM (Protocol/Service Multiplexer) - 返回值:返回一个指向
tL2C_RCB
结构体的指针。如果找到匹配的RCB
(Registration Control Block),就返回它;如果找不到,就返回NULL
。
tL2C_RCB* l2cu_find_rcb_by_psm(uint16_t psm) {
/*
l2cb: 是 L2CAP 层的一个全局控制块(通常是 tL2C_CB 结构体),里面有一个 rcb_pool[]数组.
rcb_pool[]:保存了所有注册到 L2CAP 层的 PSM 服务,属于服务注册表。
*/
tL2C_RCB* p_rcb = &l2cb.rcb_pool[0]; // 准备从第一个注册表条目开始遍历。
uint16_t xx;
// 遍历到最大条目数 MAX_L2CAP_CLIENTS(即最大支持的 L2CAP 客户端数), 目前是 15
for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
/*
p_rcb->in_use:这个 RCB 是否正在使用?(即注册了)
p_rcb->psm == psm:这个 RCB 的 PSM 是否和我们要找的 psm相同?
*/
if ((p_rcb->in_use) && (p_rcb->psm == psm)) return (p_rcb);
}
/* If here, no match found */
return (NULL);
}
代码的作用:
项目 | 内容 |
---|---|
功能 | 在 l2cb.rcb_pool[] 中查找一个 符合给定 PSM 的服务注册块(RCB) |
输入 | 一个 16 位的 psm |
输出 | 匹配到的 tL2C_RCB 指针,或者 NULL |
遍历方式 | 顺序遍历,从索引 0 开始直到 MAX_L2CAP_CLIENTS |
匹配条件 | in_use == true 并且 psm == 输入 psm |
应用场景:
- 当 L2CAP 收到某个连接请求时(例如接收到
L2CAP_CONNECT_REQ
),需要根据psm
查找对应的服务。 - 如果找不到,就要拒绝连接请求。
比如:
- 手机请求通过
0x0019
(AVDTP)连接车机。 - 车机收到后,会调用
l2cu_find_rcb_by_psm(0x0019)
查找是否有注册的 AVCTP 服务处理它。
2. l2cu_allocate_ccb
-
函数名字:
l2cu_allocate_ccb
-
作用:从 CCB池(Channel Control Block池)里分配一个新的 CCB,建立一个逻辑信道。
-
参数:
p_lcb
:指向对应物理链路(LCB)的指针,代表哪个设备的连接。cid
:如果是 0,表示系统自己分配;如果非 0,表示希望指定cid
分配。
// system/stack/l2cap/l2c_utils.cc
tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {
LOG_INFO("is_dynamic = %d, cid 0x%04x", p_lcb != nullptr, cid); // 输出 p_lcb 是否为空(动态通道?)以及要分配的 CID。
/*
l2cb.p_free_ccb_first:指向第一个空闲的 CCB。
如果为空,说明没有可用 CCB了,直接报错并返回 nullptr。
*/
if (!l2cb.p_free_ccb_first) {
LOG_ERROR("First free ccb is null for cid 0x%04x", cid);
return nullptr;
}
tL2C_CCB* p_ccb; // 定义一个指针 p_ccb,用于保存即将分配的 CCB。
// 判断 cid 是不是系统自动分配
/* If a CID was passed in, use that, else take the first free one */
if (cid == 0) {
// 如果 cid==0,需要从自由链表(free list)拿第一个空闲的 CCB。
p_ccb = l2cb.p_free_ccb_first; // 拿走第一个空闲的 CCB。
l2cb.p_free_ccb_first = p_ccb->p_next_ccb; // 把空闲链表的头指针移动到下一个空闲节点。
} else { // 用户指定 cid,
// 如果 cid != 0,需要找出对应 cid 的 CCB
tL2C_CCB* p_prev = nullptr;
/*
1. L2CAP 的动态信道 CID 是从 L2CAP_BASE_APPL_CID 开始的(0x0040).
2. 数组是连续的,所以可以直接通过 (cid - BASE) 定位到对应的 CCB.
*/
p_ccb = &l2cb.ccb_pool[cid - L2CAP_BASE_APPL_CID];
// 从空闲链表里移除
if (p_ccb == l2cb.p_free_ccb_first) {
// 如果这块 CCB恰好是链表头,直接更新空闲链表头指针。
l2cb.p_free_ccb_first = p_ccb->p_next_ccb;
} else {
// 如果不是链表头,需要在链表中查找这个 CCB 的前一个节点
for (p_prev = l2cb.p_free_ccb_first; p_prev != nullptr;
p_prev = p_prev->p_next_ccb) {
if (p_prev->p_next_ccb == p_ccb) {
// 找到以后,把前一个节点的 next 指针指向 p_ccb->p_next_ccb,实现链表断开。
p_prev->p_next_ccb = p_ccb->p_next_ccb;
if (p_ccb == l2cb.p_free_ccb_last) {
// 如果删的是链表最后一个,更新 p_free_ccb_last
l2cb.p_free_ccb_last = p_prev;
}
break;
}
}
if (p_prev == nullptr) {
// 如果循环结束没找到,说明链表里没有这个 CCB,报错并返回空。
LOG_ERROR("Could not find CCB for CID 0x%04x in the free list", cid);
return nullptr;
}
}
}
// 初始化 CCB 内容
p_ccb->p_next_ccb = p_ccb->p_prev_ccb = nullptr; // 断开当前 CCB 的链表关系(确保干净)
p_ccb->in_use = true; // 标记:正在使用
/* Get a CID for the connection */
p_ccb->local_cid = L2CAP_BASE_APPL_CID + (uint16_t)(p_ccb - l2cb.ccb_pool); // 根据数组偏移重新计算本地 CID. 在当前场景下计算出来是 0x0046
// 初始化 CCB 其他关联字段
p_ccb->p_lcb = p_lcb; // 绑定 LCB
p_ccb->p_rcb = nullptr; // 还没绑定 RCB(服务)
/* Set priority then insert ccb into LCB queue (if we have an LCB) */
p_ccb->ccb_priority = L2CAP_CHNL_PRIORITY_LOW; // 默认优先级是低优先级。
// 将 CCB 插入到 LCB 的 CCB链表
if (p_lcb) l2cu_enqueue_ccb(p_ccb); // 如果有对应物理连接,把 CCB 挂到物理连接(LCB)的 CCB链表里
// 配置默认配置项
/* Put in default values for configuration */
memset(&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO)); // 清空本地配置和对端配置结构体
memset(&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO));
// 配置默认参数(两边一样), 初始化 MTU、Flush超时时间、QOS参数等,都是默认值。
/* Put in default values for local/peer configurations */
p_ccb->our_cfg.flush_to = p_ccb->peer_cfg.flush_to = L2CAP_NO_AUTOMATIC_FLUSH;
p_ccb->our_cfg.mtu = p_ccb->peer_cfg.mtu = L2CAP_DEFAULT_MTU;
p_ccb->our_cfg.qos.service_type = p_ccb->peer_cfg.qos.service_type =
L2CAP_DEFAULT_SERV_TYPE;
p_ccb->our_cfg.qos.token_rate = p_ccb->peer_cfg.qos.token_rate =
L2CAP_DEFAULT_TOKEN_RATE;
p_ccb->our_cfg.qos.token_bucket_size = p_ccb->peer_cfg.qos.token_bucket_size =
L2CAP_DEFAULT_BUCKET_SIZE;
p_ccb->our_cfg.qos.peak_bandwidth = p_ccb->peer_cfg.qos.peak_bandwidth =
L2CAP_DEFAULT_PEAK_BANDWIDTH;
p_ccb->our_cfg.qos.latency = p_ccb->peer_cfg.qos.latency =
L2CAP_DEFAULT_LATENCY;
p_ccb->our_cfg.qos.delay_variation = p_ccb->peer_cfg.qos.delay_variation =
L2CAP_DEFAULT_DELAY;
// FCR(流控制/重传)部分初始化
p_ccb->peer_cfg_already_rejected = false; // 流控配置默认没拒绝,还能尝试多次。
p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES;
// 创建ACK、监控重传定时器
alarm_free(p_ccb->fcrb.ack_timer); // 之前如果存在老的,先释放掉。
p_ccb->fcrb.ack_timer = alarm_new("l2c_fcrb.ack_timer"); // 创建ACK超时定时器
/* CSP408639 Fix: When L2CAP send amp move channel request or receive
* L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move
* request -> Stop retrans/monitor timer -> Change channel state to
* CST_AMP_MOVING. */
alarm_free(p_ccb->fcrb.mon_retrans_timer);
p_ccb->fcrb.mon_retrans_timer = alarm_new("l2c_fcrb.mon_retrans_timer"); // 创建监控重传定时器
// 接收MTU、发送MPS设置, 最大接收SDU大小、最大传输单元大小。
p_ccb->max_rx_mtu = BT_DEFAULT_BUFFER_SIZE -
(L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN);
p_ccb->tx_mps = BT_DEFAULT_BUFFER_SIZE - 32;
// 创建四个 发送/接收队列, 四个队列分别保存发送缓存、重传缓存、选择重传缓存等。
p_ccb->xmit_hold_q = fixed_queue_new(SIZE_MAX);
p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(SIZE_MAX);
p_ccb->fcrb.retrans_q = fixed_queue_new(SIZE_MAX);
p_ccb->fcrb.waiting_for_ack_q = fixed_queue_new(SIZE_MAX);
// 初始化其他小字段, 是否发生拥塞的标志、缓冲区配额。
p_ccb->cong_sent = false;
p_ccb->buff_quota = 2; /* This gets set after config */
/* If CCB was reserved Config_Done can already have some value */
if (cid == 0) { // 如果是系统分配 CID, 配置是否完成标志位。
p_ccb->config_done = 0;
} else {
LOG_INFO("cid 0x%04x config_done:0x%x", cid, p_ccb->config_done);
}
// 初始化频道状态机状态
p_ccb->chnl_state = CST_CLOSED; // 初始是 关闭状态
p_ccb->flags = 0; // 各种 flag 置 0
// 数据传输速率、是否可 flush
// 默认都是低速率传输,不支持 flush,不支持 ECOC (Enhanced Credit-Based flow control)。
p_ccb->tx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
p_ccb->rx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
p_ccb->is_flushable = false;
p_ccb->ecoc = false;
// 创建 CCB 通用超时定时器
alarm_free(p_ccb->l2c_ccb_timer);
p_ccb->l2c_ccb_timer = alarm_new("l2c.l2c_ccb_timer"); // 超时时钟,用于各种 L2CAP 超时操作
// 标记是否待移除, 初始不待删除
p_ccb->pending_remove = false;
// 调整 LCB 上信道的内存资源分配。
l2c_link_adjust_chnl_allocation();
// 更新 LCB 状态
if (p_lcb != NULL) {
// 如果 LCB 存在,说明本地客户端已经活跃了
// once a dynamic channel is opened, timeouts become active
p_lcb->with_active_local_clients = true;
}
// 最后返回新分配的 CCB
return p_ccb;
}
项目 | 内容 |
---|---|
主要功能 | 分配/初始化一个新的 L2CAP 信道控制块(CCB) |
输入参数 | p_lcb (链路指针), cid (信道ID) |
是否支持动态/指定CID | 支持 |
核心步骤 | 从空闲链表拿一个 CCB,初始化默认参数,绑定LCB,配置超时定时器 |
失败情况 | 空闲链表为空;或者指定cid找不到 |
返回 | 新分配的 tL2C_CCB* |
3. L2CEVT_L2CAP_CONNECT_REQ 事件处理
L2CEVT_L2CAP_CONNECT_REQ = 10, /* request */
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info);
// system/stack/l2cap/l2c_csm.cc
void l2c_csm_execute(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
LOG_INFO("Entry chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
switch (p_ccb->chnl_state) {
case CST_CLOSED:
l2c_csm_closed(p_ccb, event, p_data);
break;
...
}
}
l2c_csm_closed
是 L2CAP 信道状态机中处理 “CLOSED” 状态的函数。p_ccb
:当前信道的控制块(Channel Control Block)。event
:触发的事件(如:连接请求、断开等)。p_data
:附带数据,不同事件传递的内容不一样(这里只针对 CONNECT_REQ)。
static void l2c_csm_closed(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
// 把 p_data 强转成连接信息结构体 tL2C_CONN_INFO*,虽然本段后面没实际用到 p_ci。
tL2C_CONN_INFO* p_ci = (tL2C_CONN_INFO*)p_data;
uint16_t local_cid = p_ccb->local_cid; // local_cid 保存本地 CID,后续打印方便
tL2CA_DISCONNECT_IND_CB* disconnect_ind; // disconnect_ind 准备保存"断开连接回调函数"指针
// 检查是否注册了对应的 RCB
if (p_ccb->p_rcb == NULL) {
/*
p_rcb(Registration Control Block)如果为空,表示这个信道没有注册应用者,那就直接打印错误日志并返回。
因为如果没人注册,就没必要继续处理了.
*/
LOG_ERROR("LCID: 0x%04x st: CLOSED evt: %s p_rcb == NULL",
p_ccb->local_cid, l2c_csm_get_event_name(event));
return;
}
/*
取断开回调函数
从 RCB 结构中取出断开连接的回调函数(虽然本段代码中暂时没用到 disconnect_ind,留作未来其他分支扩展)。
*/
disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
// 打印当前 LCID、状态(CLOSED)、事件名称。
LOG_INFO("LCID: 0x%04x st: CLOSED evt: %s", p_ccb->local_cid,
l2c_csm_get_event_name(event));
switch (event) {
// 处理我们刚设置的 L2CEVT_L2CAP_CONNECT_REQ 事件
case L2CEVT_L2CAP_CONNECT_REQ:
// 收到来自对端的连接请求时,如果当前状态是 CLOSED,要走建立连接流程。
/* stop link timer to avoid race condition between A2MP, Security, and
* L2CAP
*
* 取消 link 层定时器(避免 race condition):
* 停止 link 级别的超时定时器,避免和 A2MP(Alternate MAC/PHY)、Security、L2CAP 同时抢占流程导致竞态问题。
* */
alarm_cancel(p_ccb->p_lcb->l2c_lcb_timer);
// 判断是不是 BLE 连接
if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) {
// 如果是 BLE 连接(而不是传统 BR/EDR 蓝牙)。
// BLE 下安全验证
p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; // 先切换状态机到 "等待安全完成"(W4_SEC_COMP)。
// 发起 BLE 认证请求(比如加密/配对),要求远端满足安全条件。
// 回调函数是 l2c_link_sec_comp2,附带 p_ccb
tL2CAP_LE_RESULT_CODE result = l2ble_sec_access_req(
p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, false,
&l2c_link_sec_comp2, p_ccb);
// 根据安全检查结果处理
switch (result) {
case L2CAP_LE_RESULT_INSUFFICIENT_AUTHORIZATION:
case L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS:
case L2CAP_LE_RESULT_INVALID_PARAMETERS:
case L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION:
case L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP_KEY_SIZE:
case L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP:
// 如果认证失败(各种错误原因),需要:
l2cu_reject_ble_connection(p_ccb, p_ccb->remote_id, result); // 发送拒绝连接
l2cu_release_ccb(p_ccb); // 释放掉分配的 p_ccb 控制块
break;
case L2CAP_LE_RESULT_CONN_OK:
case L2CAP_LE_RESULT_NO_PSM:
case L2CAP_LE_RESULT_NO_RESOURCES:
case L2CAP_LE_RESULT_INVALID_SOURCE_CID:
case L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED:
// 如果是连接成功或者一些允许继续的情况,不做处理,后续等待回调
break;
}
} else {
// 传统 BR/EDR 连接下的处理
// 尝试设置 Link Policy(Active Mode)
// 把连接设置为 Active Mode(主动通讯模式),防止进入低功耗模式导致通信卡住。
if (!BTM_SetLinkPolicyActiveMode(p_ccb->p_lcb->remote_bd_addr)) {
// 如果设置失败,打印警告。
LOG_WARN("Unable to set link policy active");
}
p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; // 先切换状态机到 "等待安全完成"(W4_SEC_COMP)。
// 发起传统蓝牙安全认证
// 通过 BTM(蓝牙管理模块)发起安全检查(加密认证等)。
// 成功返回 BTM_CMD_STARTED,代表认证正在进行。
auto status = btm_sec_l2cap_access_req(p_ccb->p_lcb->remote_bd_addr,
p_ccb->p_rcb->psm, false,
&l2c_link_sec_comp, p_ccb);
if (status == BTM_CMD_STARTED) {
// 如果认证正在进行,回复"连接等待中"
// started the security process, tell the peer to set a longer timer
// 回复 Peer 一个连接挂起(Pending)消息,告诉对方“我在验证中,请稍等”。
l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
} else {
// 如果不是正在进行,打印认证状态日志。
LOG_INFO("Check security for psm 0x%04x, status %d",
p_ccb->p_rcb->psm, status);
}
}
break;
default:
LOG_ERROR("Handling unexpected event:%s", l2c_csm_get_event_name(event));
}
// 最后统一打印一下当前信道状态和处理的事件,便于跟踪调试。
LOG_INFO("Exit chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
}
步骤 | 作用 |
---|---|
检查 RCB 是否存在 | 确保有注册应用 |
处理 CONNECT_REQ | 根据是 BLE 还是 BR/EDR 分别走安全验证流程 |
设置 Link Active Mode | 保持连接活跃 |
BLE 下可能直接拒绝连接 | 如果认证失败 |
传统蓝牙下可能发 Pending | 如果认证在进行 |
其他事件打印异常日志 | CLOSED 状态不应该接其他事件 |
4. 传统蓝牙安全认证
-
定义了一个函数
btm_sec_l2cap_access_req
。 -
功能:处理 L2CAP 在尝试建立连接时,询问安全模块(BTM,即 Bluetooth Manager)是否允许这条连接。
-
输入参数:
-
bd_addr
:对端设备的蓝牙地址。 -
psm
:L2CAP 层的协议/服务多路复用器编号(Protocol/Service Multiplexer,比如 AVDTP 是 0x0019)。 -
is_originator
:表示发起方是本地设备(true)还是对端设备(false)。 -
p_callback
:当需要异步处理(比如需要等待安全认证完成)时调用的回调函数。 -
p_ref_data
:透传数据,回调时一起带回。
-
// system/stack/btm/btm_sec.cc
/*******************************************************************************
*
* Function btm_sec_l2cap_access_req
*
* Description This function is called by the L2CAP to grant permission to
* establish L2CAP connection to or from the peer device.
*
* Parameters: bd_addr - Address of the peer device
* psm - L2CAP PSM
* is_originator - true if protocol above L2CAP originates
* connection
* p_callback - Pointer to callback function called if
* this function returns PENDING after required
* procedures are complete. MUST NOT BE NULL.
*
* Returns tBTM_STATUS
*
******************************************************************************/
tBTM_STATUS btm_sec_l2cap_access_req(const RawAddress& bd_addr, uint16_t psm,
bool is_originator,
tBTM_SEC_CALLBACK* p_callback,
void* p_ref_data) {
/*
定义固定变量 transport,设置为 BR/EDR 传输类型(经典蓝牙)。
注:并没有处理 LE(低功耗蓝牙) 的情况,因为这是给 BR/EDR 传统连接用的。
*/
// should check PSM range in LE connection oriented L2CAP connection
constexpr tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
// 打日志,记录当前是不是发起方 (is_originator),以及用的是什么 psm
LOG_INFO("is_originator:%d, psm=0x%04x", is_originator, psm);
/*
找出注册在安全模块中、匹配这个 psm 的服务记录(tBTM_SEC_SERV_REC 类型)。
btm_sec_find_first_serv 会查一张内部表,看看这个 psm 是不是有应用程序注册过。
如果找不到,说明这个 psm 未注册,不应该允许连接。
*/
// Find the service record for the PSM
tBTM_SEC_SERV_REC* p_serv_rec = btm_sec_find_first_serv(is_originator, psm);
// If there is no application registered with this PSM do not allow connection
if (!p_serv_rec) {
/*
如果没有找到服务记录:
打一条警告日志,说没有应用注册这个 psm。
调用传进来的回调函数 p_callback,告知请求失败(BTM_MODE_UNSUPPORTED)。
返回 BTM_MODE_UNSUPPORTED,表示不支持连接。
*/
LOG_WARN("PSM: 0x%04x no application registered", psm);
(*p_callback)(&bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED);
return (BTM_MODE_UNSUPPORTED);
}
/* Services level0 by default have no security */
if (psm == BT_PSM_SDP) {
/*
特殊处理:如果是 SDP(Service Discovery Protocol),不需要任何安全认证。
直接调用回调函数,返回成功(BTM_SUCCESS_NO_SECURITY),并退出.
解释:SDP 只是用来查服务列表,不涉及数据传输或隐私,所以不要求加密认证。
*/
LOG_INFO("No security required for SDP");
(*p_callback)(&bd_addr, transport, p_ref_data, BTM_SUCCESS_NO_SECURITY);
return (BTM_SUCCESS);
}
uint16_t security_required;
if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
/*
接下来,根据当前蓝牙堆栈的安全模式(btm_cb.security_mode),确定需要什么级别的安全措施。
如果是 安全模式 4(Secure Connections Only,强制 LE Secure Connection 加密),就调整一下服务的 security_flags。
*/
security_required = btm_sec_set_serv_level4_flags(
p_serv_rec->security_flags, is_originator);
} else {
// 否则,直接使用服务本身注册时要求的安全标志。
security_required = p_serv_rec->security_flags;
}
/*
最后,把地址、最终确定的 security_required、是否是发起方、回调函数、透传数据,都传给更细粒度的处理函数 btm_sec_l2cap_access_req_by_requirement。
*/
/*
这个函数负责具体执行:
检查配对情况,
是否加密,
是否认证,
是否触发配对流程等。
*/
return btm_sec_l2cap_access_req_by_requirement(
bd_addr, security_required, is_originator, p_callback, p_ref_data);
}
- 当 L2CAP 想建立连接时,调用这个函数,请求安全模块 BTM 检查是否允许建立连接。安全模块根据 PSM 类型和安全要求,决定直接通过、触发安全认证,还是直接拒绝。
安全等级介绍
// system/stack/include/btm_api_types.h
/* BTM_SEC security masks */
enum : uint16_t {
/* Nothing required */
BTM_SEC_NONE = 0x0000,
/* Inbound call requires authentication */
BTM_SEC_IN_AUTHENTICATE = 0x0002,
/* Inbound call requires encryption */
BTM_SEC_IN_ENCRYPT = 0x0004,
/* Outbound call requires authentication */
BTM_SEC_OUT_AUTHENTICATE = 0x0010,
/* Outbound call requires encryption */
BTM_SEC_OUT_ENCRYPT = 0x0020,
/* Secure Connections Only Mode */
BTM_SEC_MODE4_LEVEL4 = 0x0040,
/* Need to switch connection to be central */
BTM_SEC_FORCE_CENTRAL = 0x0100,
/* Need to switch connection to be central */
BTM_SEC_ATTEMPT_CENTRAL = 0x0200,
/* Need to switch connection to be peripheral */
BTM_SEC_FORCE_PERIPHERAL = 0x0400,
/* Try to switch connection to be peripheral */
BTM_SEC_ATTEMPT_PERIPHERAL = 0x0800,
/* inbound Do man in the middle protection */
BTM_SEC_IN_MITM = 0x1000,
/* outbound Do man in the middle protection */
BTM_SEC_OUT_MITM = 0x2000,
/* enforce a minimum of 16 digit for sec mode 2 */
BTM_SEC_IN_MIN_16_DIGIT_PIN = 0x4000,
};
宏定义 | 含义 | 适用方向 | 常见使用场景 |
---|---|---|---|
BTM_SEC_NONE (0x0000) | 不要求任何安全性 | 入站+出站 | 非敏感数据传输,比如一些开放设备广播、简单配对连接 |
BTM_SEC_IN_AUTHENTICATE (0x0002) | 入站连接需要身份验证(配对) | 入站(被连接方) | 设备作为服务器,要求连接进来的客户端至少完成配对,如蓝牙耳机接收连接 |
BTM_SEC_IN_ENCRYPT (0x0004) | 入站连接需要加密 | 入站 | 设备被连接时,要求链路加密,比如医疗设备传输隐私数据 |
BTM_SEC_OUT_AUTHENTICATE (0x0010) | 出站连接需要身份验证(配对) | 出站(发起连接方) | 手机主动连接智能手表,要求连接前认证对方身份 |
BTM_SEC_OUT_ENCRYPT (0x0020) | 出站连接需要加密 | 出站 | 手机主动连接蓝牙锁,要求链路加密保障通信安全 |
BTM_SEC_MODE4_LEVEL4 (0x0040) | 只允许 Secure Connections Only 模式连接(使用更强的 ECDH 算法) | 入站+出站 | 高安全需求场景,如车载蓝牙连接手机时要求强加密,拒绝传统配对 |
BTM_SEC_FORCE_CENTRAL (0x0100) | 强制切换角色为 Central(主机) | 出站 | 手机强制作为 Central 主动连接外围设备,比如蓝牙耳机 |
BTM_SEC_ATTEMPT_CENTRAL (0x0200) | 尝试切换为 Central(若失败也继续) | 出站 | 优先作为 Central 连接,但失败不会终止连接,比如车机同时可以是 Peripheral 和 Central |
BTM_SEC_FORCE_PERIPHERAL (0x0400) | 强制切换角色为 Peripheral(从机) | 入站 | 设备必须作为 Peripheral 接受连接,例如体重秤作为从设备 |
BTM_SEC_ATTEMPT_PERIPHERAL (0x0800) | 尝试切换为 Peripheral | 入站 | 优先作为 Peripheral,如果不行再考虑其它,比如耳机可以接收连接也可以发起 |
BTM_SEC_IN_MITM (0x1000) | 入站连接要求防中间人攻击(需要 MITM 保护的认证) | 入站 | 接收连接时要求用户确认(比如 PIN 码、数字比较),提高安全性,如支付终端 |
BTM_SEC_OUT_MITM (0x2000) | 出站连接要求防中间人攻击 | 出站 | 主动发起连接时要求进行 MITM 保护认证,如安全支付用手机连接 POS 机 |
BTM_SEC_IN_MIN_16_DIGIT_PIN (0x4000) | 入站要求使用至少16位 PIN 码(传统配对方式) | 入站 | 医疗设备或企业环境中要求高强度 PIN 码配对,防止弱 PIN 导致的安全风险 |
总结一下:
IN_*
的宏是指被连接时要求的安全措施;OUT_*
的宏是指主动发起连接时要求的安全措施;MODE4_LEVEL4
是专门为了**强制使用 LE Secure Connections (SC Only)**而设的,只有支持 ECDH 的设备才能连;FORCE
和ATTEMPT
是关于**角色切换(Central/Peripheral)**的,不是直接和安全有关;MITM
和MIN_16_DIGIT_PIN
都是加强认证强度,防止暴力破解或中间人攻击。
#define BTM_SEC_OUT_LEVEL4_FLAGS \
(BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_MITM | \
BTM_SEC_MODE4_LEVEL4)
#define BTM_SEC_IN_LEVEL4_FLAGS \
(BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_MITM | \
BTM_SEC_MODE4_LEVEL4)
// system/stack/btm/btm_sec.cc
static uint16_t btm_sec_set_serv_level4_flags(uint16_t cur_security,
bool is_originator) {
uint16_t sec_level4_flags =
is_originator ? BTM_SEC_OUT_LEVEL4_FLAGS : BTM_SEC_IN_LEVEL4_FLAGS;
return cur_security | sec_level4_flags;
}
项目 | 含义 | 为什么需要 | 备注 |
---|---|---|---|
BTM_SEC_OUT_AUTHENTICATE (或 BTM_SEC_IN_AUTHENTICATE ) | 需要进行认证(完成配对过程,建立可信连接) | Level 4 要求必须经过身份认证,不允许匿名连接 | 需要交换身份信息(如密钥、证书) |
BTM_SEC_OUT_ENCRYPT (或 BTM_SEC_IN_ENCRYPT ) | 需要链路加密(建立加密连接) | Level 4 要求链路必须加密,防止数据被窃听 | 通常在认证成功后自动加密 |
BTM_SEC_OUT_MITM (或 BTM_SEC_IN_MITM ) | 需要防中间人攻击的认证(MITM protection) | Level 4 要求配对过程中必须防止中间人攻击 | 常见方式:用户确认、数字比较、密钥输入 |
BTM_SEC_MODE4_LEVEL4 | 要求使用 Secure Connections Only 模式(基于 ECDH 的强安全配对) | 这是 Level 4 的核心要求:只允许 LE Secure Connections (LESC),不接受传统配对(Legacy Pairing) | 需要设备双方都支持 Bluetooth 4.2+ 的 LESC 特性 |
BTM_SEC_OUT_LEVEL4_FLAGS
👉 表示当主动发起连接时,要强制要求:
- 配对认证
- 加密链路
- 有中间人保护
- 使用 LE Secure Connections Only 模式
BTM_SEC_IN_LEVEL4_FLAGS
👉 表示当被连接时,同样要强制要求:
- 配对认证
- 加密链路
- 有中间人保护
- 使用 LE Secure Connections Only 模式
这两个宏分别用于不同方向的连接,但要求完全一样,都是 Level 4。
什么是 Bluetooth Secure Connections Only Mode Level 4?
等级 | 描述 | 特点 |
---|---|---|
Level 1 | 不要求认证也不要求加密 | 开放连接,最弱 |
Level 2 | 不要求认证但要求加密 | 加密但不认证 |
Level 3 | 需要认证和加密,但可以是传统配对(Legacy Pairing) | 有一定防护 |
Level 4 | 需要认证、加密,并且必须使用 LE Secure Connections(ECDH密钥交换) | 最高级别安全,防中间人攻击,适合敏感数据传输,比如支付、医疗、车载蓝牙连接 |
Level 4 是从 Bluetooth 4.2 标准开始要求的,需要设备硬件和软件都支持 Secure Connections (SC) 特性。
例子:
- 车机连接手机(比如 BMW 蓝牙连接 iPhone)
👉 必须使用 Secure Connections Only,避免有人中途劫持蓝牙连接控制车机 - 手机连接支付 POS 机
👉 必须要求认证、防中间人攻击、并且加密,保护交易安全 - 智能门锁连接手机
👉 不仅要认证,还要防止别人假冒设备或劫持指令
BTM_SEC_OUT_LEVEL4_FLAGS
和 BTM_SEC_IN_LEVEL4_FLAGS
是出站/入站连接时强制应用 Secure Connections Only Level 4 安全要求的一组标志,确保连接经过认证、加密、防中间人攻击,并使用最新的安全机制(LESC)。
btm_sec_l2cap_access_req_by_requirement
这个函数是 L2CAP 建立连接时,蓝牙安全管理模块(BTM)用来检查是否满足安全要求 的关键步骤。
如果安全性(比如加密、认证)不够,就会触发配对/加密流程。
入参:
bd_addr
:对方蓝牙设备地址。security_required
:希望达到的安全要求(比如必须认证、加密、Secure Connections等)。is_originator
:自己是发起方(true)还是接受方(false)。p_callback
:安全处理完成后的回调函数。p_ref_data
:回调时传递的用户自定义数据。
// system/stack/btm/btm_sec.cc
tBTM_STATUS btm_sec_l2cap_access_req_by_requirement(
const RawAddress& bd_addr, uint16_t security_required, bool is_originator,
tBTM_SEC_CALLBACK* p_callback, void* p_ref_data) {
// 记录当前请求的设备地址、请求的安全等级,以及我是发起方还是接受方。
LOG_INFO(
"Checking l2cap access requirements peer:%s security:0x%x "
"is_initiator:%s",
PRIVATE_ADDRESS(bd_addr), security_required,
logbool(is_originator).c_str());
// 1. 初始化变量
tBTM_STATUS rc = BTM_SUCCESS; // 初始设为成功
bool chk_acp_auth_done = false; // 标记是否需要检查认证状态
/* should check PSM range in LE connection oriented L2CAP connection */
constexpr tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; // 这里明确只针对 传统蓝牙(BR/EDR) 处理
/* Find or get oldest record */
// 2. 查找设备记录
tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(bd_addr); // 找到或创建一条安全记录(每个连接的设备在 BTM 都有一份安全记录)。
p_dev_rec->hci_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_BR_EDR); // 绑定 HCI 连接句柄。
// 3. 被动连接且要求 Secure Connections Level 4 的校验
if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4)) {
/*
如果我是接受方,且要求Mode 4 Level 4(即 Secure Connections 的最高等级),那么:
1. 检查本地和对方是否支持 Secure Connections。
2. 如果任何一方不支持,拒绝连接。
3. 比如要求超高安全性的支付终端,只接受支持 Secure Connections 的设备
*/
bool local_supports_sc =
controller_get_interface()->supports_secure_connections();
/* acceptor receives L2CAP Channel Connect Request for Secure Connections
* Only service */
if (!local_supports_sc || !p_dev_rec->SupportsSecureConnections()) {
LOG_WARN(
"Policy requires mode 4 level 4, but local_support_for_sc=%d, "
"rmt_support_for_sc=%s, failing connection",
local_supports_sc,
logbool(p_dev_rec->SupportsSecureConnections()).c_str());
if (p_callback) {
(*p_callback)(&bd_addr, transport, (void*)p_ref_data,
BTM_MODE4_LEVEL4_NOT_SUPPORTED);
}
return (BTM_MODE4_LEVEL4_NOT_SUPPORTED);
}
}
/* there are some devices (moto KRZR) which connects to several services at
* the same time */
/* we will process one after another */
// 4. 处理并发连接情况(多个安全过程同时进行)
if ((p_dev_rec->p_callback) ||
(btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)) {
/*
如果当前设备已经在处理其他安全过程(比如 pairing).
或全局 pairing 状态不是空闲,就要排队。
*/
LOG_INFO("security_flags:x%x, sec_flags:x%x", security_required,
p_dev_rec->sec_flags);
rc = BTM_CMD_STARTED; // 暂时返回命令已启动(不是立刻成功)。
// 判断是否满足 legacy 安全模式的要求
if ((btm_cb.security_mode == BTM_SEC_MODE_SERVICE) ||
(BTM_SM4_KNOWN == p_dev_rec->sm4) ||
(BTM_SEC_IS_SM4(p_dev_rec->sm4) &&
(!btm_sec_is_upgrade_possible(p_dev_rec, is_originator)))) {
/* legacy mode - local is legacy or local is lisbon/peer is legacy
* or SM4 with no possibility of link key upgrade */
/*
如果系统处于老版本(Legacy)安全模式或某些情况不能升级:
是否需要认证?是否需要加密?
如果需要 16位 PIN(强认证),也检查。
简单理解:
只要满足当前安全要求(加密/认证/16位PIN),就直接返回成功,否则需要等待后续处理。
*/
if (is_originator) {
if (((security_required & BTM_SEC_OUT_FLAGS) == 0) ||
((((security_required & BTM_SEC_OUT_FLAGS) ==
BTM_SEC_OUT_AUTHENTICATE) &&
btm_dev_authenticated(p_dev_rec))) ||
((((security_required & BTM_SEC_OUT_FLAGS) ==
(BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) &&
btm_dev_encrypted(p_dev_rec)))) {
rc = BTM_SUCCESS;
}
} else {
if (((security_required & BTM_SEC_IN_FLAGS) == 0) ||
(((security_required & BTM_SEC_IN_FLAGS) ==
BTM_SEC_IN_AUTHENTICATE) &&
btm_dev_authenticated(p_dev_rec)) ||
(((security_required & BTM_SEC_IN_FLAGS) ==
(BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) &&
btm_dev_encrypted(p_dev_rec))) {
// Check for 16 digits (or MITM)
if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) ||
(((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) ==
BTM_SEC_IN_MIN_16_DIGIT_PIN) &&
btm_dev_16_digit_authenticated(p_dev_rec))) {
rc = BTM_SUCCESS;
}
}
}
if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) &&
(p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
rc = BTM_CMD_STARTED;
}
if (rc == BTM_SUCCESS) {
// 特别注意:临时配对的限制
// 如果是临时绑定状态(例如没保存的配对信息),禁止访问高安全性服务。
if (access_secure_service_from_temp_bond(p_dev_rec, is_originator, security_required)) {
LOG_ERROR("Trying to access a secure service from a temp bonding, rejecting");
rc = BTM_FAILED_ON_SECURITY;
}
if (p_callback)
(*p_callback)(&bd_addr, transport, (void*)p_ref_data, rc);
return (rc);
}
}
btm_cb.sec_req_pending = true;
return (BTM_CMD_STARTED);
}
// 记录安全请求
// 如果需要等待进一步配对、加密,保存相关信息:
/* Save the security requirements in case a pairing is needed */
p_dev_rec->required_security_flags_for_pairing = security_required;
// 根据不同的安全模式,动态调整安全要求(比如一定要加密):
/* Modify security_required in btm_sec_l2cap_access_req for Lisbon */
if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
btm_cb.security_mode == BTM_SEC_MODE_SC) {
if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
if (is_originator) {
/* SM4 to SM4 -> always encrypt */
security_required |= BTM_SEC_OUT_ENCRYPT;
} else /* acceptor */
{
/* SM4 to SM4: the acceptor needs to make sure the authentication is
* already done */
chk_acp_auth_done = true;
/* SM4 to SM4 -> always encrypt */
security_required |= BTM_SEC_IN_ENCRYPT;
}
} else if (!(BTM_SM4_KNOWN & p_dev_rec->sm4)) { // 如果对方特性未知,先等待特性交换
/* the remote features are not known yet */
LOG_INFO(
"Remote features have not yet been received sec_flags:0x%02x %s",
p_dev_rec->sec_flags, (is_originator) ? "initiator" : "acceptor");
p_dev_rec->sm4 |= BTM_SM4_REQ_PEND;// 如果对方设备特性(比如是否支持 SC)还不知道,先标记 pending,稍后再处理。
return (BTM_CMD_STARTED);
}
}
BTM_TRACE_DEBUG(
"%s() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", __func__,
p_dev_rec->sm4, p_dev_rec->sec_flags, security_required,
chk_acp_auth_done);
// 更新设备安全记录
// 保存更新后的安全要求、回调、发起方信息:
p_dev_rec->security_required = security_required;
p_dev_rec->p_ref_data = p_ref_data;
p_dev_rec->is_originator = is_originator;
if (chk_acp_auth_done) {
BTM_TRACE_DEBUG(
"(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: "
"x%x",
(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED),
(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED));
/* SM4, but we do not know for sure which level of security we need.
* as long as we have a link key, it's OK */
if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) ||
(0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) {
rc = BTM_DELAY_CHECK;
/*
2046 may report HCI_Encryption_Change and L2C Connection Request out of
sequence
because of data path issues. Delay this disconnect a little bit
*/
LOG_INFO(
"%s peer should have initiated security process by now (SM4 to SM4)",
__func__);
p_dev_rec->p_callback = p_callback;
p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC;
(*p_callback)(&bd_addr, transport, p_ref_data, rc);
return BTM_SUCCESS;
}
}
p_dev_rec->p_callback = p_callback;
// 进一步针对 Secure Connections 校验
if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
(p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
/* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case
*/
if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) {
p_dev_rec->sm4 |= BTM_SM4_UPGRADE; // 如果要求 Mode4 Level4,但 link key 不够强(不是 P-256),就标记要升级:
}
p_dev_rec->sec_flags &=
~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
BTM_SEC_AUTHENTICATED);
BTM_TRACE_DEBUG("%s: sec_flags:0x%x", __func__, p_dev_rec->sec_flags);
// 如果 link key 已经有但不够强,就清掉以前的信息,强制重新认证。
} else {
/* If we already have a link key to the connected peer, is it secure
* enough? */
btm_sec_check_upgrade(p_dev_rec, is_originator);
}
}
// 启动安全处理流程,比如配对、加密协商等。
rc = btm_sec_execute_procedure(p_dev_rec); // 在我们分析的这个案例中,在这里返回了 BTM_SUCCESS
if (rc != BTM_CMD_STARTED) {
// 如果不是 BTM_CMD_STARTED(比如失败了),立即回调上层。
BTM_TRACE_DEBUG("%s: p_dev_rec=%p, clearing callback. old p_callback=%p",
__func__, p_dev_rec, p_dev_rec->p_callback);
p_dev_rec->p_callback = NULL;
(*p_callback)(&bd_addr, transport, p_dev_rec->p_ref_data, rc); // 在这里触发了 l2c_link_sec_comp 调用
}
// 最后根据执行结果返回。
return (rc);
}
这个函数做的事情可以简单理解为:
步骤 | 目的 |
---|---|
找到设备记录 | 了解设备当前的安全状态 |
检查本地和远端是否支持需要的特性 | 比如 Secure Connections |
判断现有链接是否已经足够安全 | 是否需要加密/认证/重新配对 |
根据需要触发配对、加密 | 启动安全过程或者直接回调成功 |
使用场景:
- 你手机连耳机,如果耳机要求安全连接但你手机不支持,就直接拒绝。
- 你手机和支付终端连,要求用 P-256 密钥,如果之前是旧版配对,就重新触发安全升级。
- 多个蓝牙服务(比如耳机音频+电话)同时要求连接,按顺序一个个处理安全性。
btm_sec_execute_procedure
- 传入参数是一个指向设备安全记录(
p_dev_rec
)的指针。 - 返回类型是
tBTM_STATUS
,代表操作结果,比如成功、失败、启动中等。
// system/stack/btm/btm_sec.cc
tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec) {
CHECK(p_dev_rec != nullptr);
// 把当前设备需要的安全要求、已有的安全状态、以及安全过程状态打日志。
// 方便调试:比如知道当前是否已经加密、认证了。
LOG_INFO(
"security_required:0x%x security_flags:0x%x security_state:%s[%hhu]",
p_dev_rec->security_required, p_dev_rec->sec_flags,
security_state_text(static_cast<tSECURITY_STATE>(p_dev_rec->sec_state))
.c_str(),
p_dev_rec->sec_state);
// 1. 如果设备当前正在进行安全流程,不重复发起
if (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE) {
// 如果设备不是空闲状态(比如正在认证或加密中),就直接返回,说明流程已经在跑了。
LOG_INFO(
"Security state is idle indicating remote name request is outstanding");
return (BTM_CMD_STARTED);
}
/*
2. 如果设备名字未知,先发起远程名字请求
1. 为什么要名字?:很多安全策略需要知道对方设备名字(比如判断是已知设备还是陌生设备)。
2. 如果名字没拿到,先请求获取远程设备名字。
*/
/* If any security is required, get the name first */
if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) &&
(p_dev_rec->hci_handle != HCI_INVALID_HANDLE)) {
LOG_INFO("Security Manager: Start get name");
if (!btm_sec_start_get_name(p_dev_rec)) {
LOG_WARN("Unable to start remote name request");
return (BTM_NO_RESOURCES);
}
return (BTM_CMD_STARTED);
}
/* If connection is not authenticated and authentication is required */
/* start authentication and return PENDING to the caller */
// 3. 检查是否需要认证(配对)
if (p_dev_rec->hci_handle != HCI_INVALID_HANDLE) {
// 连接已经建立(hci_handle有效),进入检查阶段。
bool start_auth = false;
// Check link status of BR/EDR
// 3.1 出站 or 入站认证需求判断
// 如果还没认证,就根据连接方向(自己发起or对方发起)判断要不要启动认证。
// 出站连接看 BTM_SEC_OUT_AUTHENTICATE
// 入站连接看 BTM_SEC_IN_AUTHENTICATE
if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) {
if (p_dev_rec->IsLocallyInitiated()) {
if (p_dev_rec->security_required &
(BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) {
LOG_INFO("Outgoing authentication/encryption Required");
start_auth = true;
}
} else {
if (p_dev_rec->security_required &
(BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) {
LOG_INFO("Incoming authentication/encryption Required");
start_auth = true;
}
}
}
// 3.2 检查是否需要16位PIN码
if (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED)) {
/*
* We rely on BTM_SEC_16_DIGIT_PIN_AUTHED being set if MITM is in use,
* as 16 DIGIT is only needed if MITM is not used. Unfortunately, the
* BTM_SEC_AUTHENTICATED is used for both MITM and non-MITM
* authenticated connections, hence we cannot distinguish here.
*/
// 如果没有16位PIN码认证,但要求了 BTM_SEC_IN_MIN_16_DIGIT_PIN,则也要认证,这个通常用于防止弱PIN码攻击。
if (!p_dev_rec->IsLocallyInitiated()) {
if (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) {
LOG_INFO("BTM_SEC_IN_MIN_16_DIGIT_PIN Required");
start_auth = true;
}
}
}
// 3.3 如果需要认证,则启动认证流程
if (start_auth) {
LOG_INFO("Security Manager: Start authentication");
/*
* If we do have a link-key, but we end up here because we need an
* upgrade, then clear the link-key known and authenticated flag before
* restarting authentication.
* WARNING: If the controller has link-key, it is optional and
* recommended for the controller to send a Link_Key_Request.
* In case we need an upgrade, the only alternative would be to delete
* the existing link-key. That could lead to very bad user experience
* or even IOP issues, if a reconnect causes a new connection that
* requires an upgrade.
*/
// 特殊情况:需要升级链接密钥
if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) &&
(!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) &&
(!p_dev_rec->IsLocallyInitiated() &&
(p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) {
// 如果已经有密钥,但不满足更高安全要求(比如16位PIN),就清除旧的密钥标记,强制重做配对。这是为了安全升级。
p_dev_rec->sec_flags &=
~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
BTM_SEC_AUTHENTICATED);
}
// 真正启动认证
btm_sec_wait_and_start_authentication(p_dev_rec); // 启动认证(配对)流程
return (BTM_CMD_STARTED); // 返回正在进行中
}
}
/* If connection is not encrypted and encryption is required */
/* start encryption and return PENDING to the caller */
// 4. 检查是否需要加密
if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) &&
((p_dev_rec->IsLocallyInitiated() &&
(p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT)) ||
(!p_dev_rec->IsLocallyInitiated() &&
(p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT))) &&
(p_dev_rec->hci_handle != HCI_INVALID_HANDLE)) {
// 如果还没有加密,且上层要求了加密,则发起加密。
BTM_TRACE_EVENT("Security Manager: Start encryption");
btsnd_hcic_set_conn_encrypt(p_dev_rec->hci_handle, true); // 使用 HCI_Set_Connection_Encryption 指令。
p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; // 状态设为 ENCRYPTING,返回处理中
return (BTM_CMD_STARTED);
} else {
LOG_INFO("Encryption not required");
}
// 5. 检查是否符合 Level 4 安全要求(SC Only)
if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
(p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
// 如果要求 Level 4,但连接密钥不是 P-256 生成的(老的密钥),直接失败。
// 保护:防止旧的不安全链接偷渡。
BTM_TRACE_EVENT(
"%s: Security Manager: SC only service, but link key type is 0x%02x -",
"security failure", __func__, p_dev_rec->link_key_type);
return (BTM_FAILED_ON_SECURITY);
}
// 6. 检查是否是"临时绑定"设备在访问安全服务
if (access_secure_service_from_temp_bond(p_dev_rec,
p_dev_rec->IsLocallyInitiated(),
p_dev_rec->security_required)) {
LOG_ERROR("Trying to access a secure service from a temp bonding, rejecting");
// 如果是临时配对的(没正式保存密钥),而试图访问高安全服务,拒绝!
return (BTM_FAILED_ON_SECURITY);
}
/* All required security procedures already established */
// 所有检查通过,清理安全请求标志
p_dev_rec->security_required &=
~(BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT);
BTM_TRACE_EVENT("Security Manager: access granted");
// 安全检查通过,允许访问
return (BTM_SUCCESS); // 返回成功,可以正式使用连接了。
}
这段 btm_sec_execute_procedure
做了这些事情:
-
看状态:是不是空闲?是不是已经有名字?是不是认证了?是不是加密了?
-
做必要动作:如果没有名字就拿名字,没认证就认证,没加密就加密。
-
符合安全级别要求吗:比如 Level 4,SC Only。
-
临时配对限制:临时配对不能访问高安全服务。
-
最终决定:可以访问(成功)或安全失败。
它是蓝牙设备连接过程中,真正落地执行安全控制策略的地方!
5. 总结
车机接收到 手机侧的 avdtp 连接请求后:
- 通过 l2cu_find_rcb_by_psm 传入 psm, 找到对应的服务。 这里找到了我们之前蓝牙初始化注册进 l2cap 的 avdtp 服务。
- 通过 l2cu_allocate_ccb 分配 一个 Channel Control Block ,并且将 该通道控制块的状态设置为 CST_CLOSED。 这是该通道的初始状态。
- 通过 l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info), 触发状态机处理 L2CEVT_L2CAP_CONNECT_REQ 事件; p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; 将状态切换到 “等待安全完成”
- 本地开始对应的安全检查。
- 安全检查通过后,回调 l2c_link_sec_comp 函数。
2. 车机同意连接
1. 日志
1082 2025-04-24 15:56:22.505074 22:22:96:de:b1:39 (leo 8295 chan) vivoMobi_91:b0:62 (cbx) L2CAP 21 Sent Connection Response - Success (SCID: 0x0041, DCID: 0x0046)
Frame 1082: 21 bytes on wire (168 bits), 21 bytes captured (168 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 12
CID: L2CAP Signaling Channel (0x0001)
Command: Connection Response
Command Code: Connection Response (0x03)
Command Identifier: 0x11
Command Length: 8
Destination CID: Dynamically Allocated Channel (0x0046)
Source CID: Dynamically Allocated Channel (0x0041)
Result: Successful (0x0000)
Status: No further information available (0x0000)
# 安全模块 检查完毕
04-24 15:56:22.504876 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: Security Manager: access granted
04-24 15:56:22.504883 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_sec_l2cap_access_req_by_requirement: p_dev_rec=0xb800004b18420208, clearing callback. old p_callback=0x7a3fcaf9cc
# 回调 l2c_link_sec_comp
04-24 15:56:22.504893 6130 6190 I l2c_link: packages/modules/Bluetooth/system/stack/l2cap/l2c_link.cc:284 l2c_link_sec_comp2: btm_status=BTM_SUCCESS, BD_ADDR=xx:xx:xx:xx:b0:62, transport=BT_TRANSPORT_BR_EDR
04-24 15:56:22.504901 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_TERM_W4_SEC_COMP [2], event=SECURITY_COMPLETE [7]
我们从 回调 l2c_link_sec_comp 开始梳理。
2. 源码分析
1. l2c_link_sec_comp2
l2c_link_sec_comp2
函数是 L2CAP 层在接收到 Security Manager(安全管理模块) 回调(Security Completion)之后,根据安全认证结果,继续推进连接流程的一个重要处理函数
参数:
-
p_bda:远端设备的蓝牙地址(谁的安全过程完成了)
-
transport:传输类型(BR/EDR 或 LE);这里没用到(标记
UNUSED_ATTR
),但是为了接口统一保留 -
p_ref_data:指向发起安全检查请求时附带的指针,这里是指向对应的 L2CAP CCB (Channel Control Block)
-
status:安全检查(如鉴权、加密等)完成后的结果,比如
BTM_SUCCESS
、BTM_AUTH_FAILURE
等
// system/stack/l2cap/l2c_link.cc
void l2c_link_sec_comp(const RawAddress* p_bda,
UNUSED_ATTR tBT_TRANSPORT transport, void* p_ref_data,
tBTM_STATUS status) {
l2c_link_sec_comp2(*p_bda, transport, p_ref_data, status);
}
void l2c_link_sec_comp2(const RawAddress& p_bda,
UNUSED_ATTR tBT_TRANSPORT transport, void* p_ref_data,
tBTM_STATUS status) {
tL2C_CONN_INFO ci; // 连接信息结构体,用于后续封装事件参数。
tL2C_LCB* p_lcb; // Link Control Block,连接控制块,代表一条逻辑连接。
tL2C_CCB* p_ccb; // Channel Control Block,信道控制块,代表一条具体的信道。
tL2C_CCB* p_next_ccb; // Channel Control Block,信道控制块,代表一条具体的信道。
// 打印日志,显示安全过程返回的状态(status)、设备地址(p_bda)、传输类型(BR/EDR or LE)。
LOG_INFO("btm_status=%s, BD_ADDR=%s, transport=%s",
btm_status_text(status).c_str(), PRIVATE_ADDRESS(p_bda),
bt_transport_text(transport).c_str());
// 特殊处理:
if (status == BTM_SUCCESS_NO_SECURITY) {
// 如果安全管理器返回 BTM_SUCCESS_NO_SECURITY(即“不需要安全”也算通过),直接将其视为普通的 BTM_SUCCESS
// 统一后续处理逻辑
status = BTM_SUCCESS;
}
// 把当前设备地址和安全检查结果打包到 ci,准备给后续状态机(l2c_csm_execute)使用
/* Save the parameters */
ci.status = status;
ci.bd_addr = p_bda;
// 根据设备地址和传输类型,找到对应的 连接控制块(LCB), LCB 代表当前设备和对方设备之间的“链接”
p_lcb = l2cu_find_lcb_by_bd_addr(p_bda, transport);
/* If we don't have one, this is an error */
if (!p_lcb) {
// 如果找不到对应的 LCB
// 说明出现了异常,比如链接已经断了,但安全过程回调还到达了;
LOG_WARN("L2CAP got sec_comp for unknown BD_ADDR");// 打印警告日志,直接返回,不做任何处理。
return;
}
/* Match p_ccb with p_ref_data returned by sec manager */
for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) {
// 遍历当前连接(LCB)上挂着的所有 CCB(信道)。
// 每一个 CCB 对应一个 L2CAP Channel(如 SDP、RFCOMM、AVCTP、A2DP 等通道)
p_next_ccb = p_ccb->p_next_ccb;// p_next_ccb 保存下一个,防止遍历时链表出错。
// 找到和回调参数 p_ref_data 匹配的 CCB。
// 这里要求精确匹配指针,所以安全回调回来时能直接定位到是哪一个信道触发的安全流程。
if (p_ccb == p_ref_data) {
switch (status) {
// 如果安全认证成功:
case BTM_SUCCESS:
// 触发 CCB 的状态机,发送 L2CEVT_SEC_COMP 事件,带着连接信息(ci)
l2c_csm_execute(p_ccb, L2CEVT_SEC_COMP, &ci); //
break;
case BTM_DELAY_CHECK:
// 意思是要延迟一段时间再确认安全性(SM4 协议相关,旧版蓝牙的一种安全模式)
/* start a timer - encryption change not received before L2CAP connect
* req */
// 启动一个定时器,等超时后重新处理。
alarm_set_on_mloop(p_ccb->l2c_ccb_timer,
L2CAP_DELAY_CHECK_SM4_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb);
return; // 注意这里直接 return,不继续遍历
default:
// 其他情况(比如认证失败、加密失败等)
// 触发 CCB 状态机,发送 L2CEVT_SEC_COMP_NEG 事件
// 通常最终导致连接失败或断开
l2c_csm_execute(p_ccb, L2CEVT_SEC_COMP_NEG, &ci);
break;
}
break; // 处理完对应的 CCB后,直接退出循环,不再继续找其他信道
}
}
}
- L2CAP在安全认证(比如加密或鉴权)完成后,根据认证结果,通知对应的信道状态机继续连接流程或者终止连接。
如果认证成功,就继续发起连接;如果失败,就中断连接。 - 正常情况下, 会返回 BTM_SUCCESS
- l2c_csm_execute(p_ccb, L2CEVT_SEC_COMP, &ci)
我们继续分析 l2c_csm_execute 的执行:
- 此时 p_ccb->chnl_state = CST_TERM_W4_SEC_COMP
- event = L2CEVT_SEC_COMP
04-24 15:56:22.504901 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_TERM_W4_SEC_COMP [2], event=SECURITY_COMPLETE [7]
void l2c_csm_execute(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
if (p_ccb == nullptr) {
LOG_WARN("CCB is null for event (%d)", event);
return;
}
LOG_INFO("Entry chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
switch (p_ccb->chnl_state) {
case CST_TERM_W4_SEC_COMP:
l2c_csm_term_w4_sec_comp(p_ccb, event, p_data);
break;
}
}
2. l2c_csm_term_w4_sec_comp
这段代码是 L2CAP CCB(Channel Control Block)状态机中,在"终止中等待安全认证完成"状态(TERM_W4_SEC_COMP)下处理各种事件的核心逻辑。
- 函数定义:处理 L2CAP CCB 当前处于 CST_TERM_W4_SEC_COMP 状态时收到的各种事件。
p_ccb
:对应的 Channel Control Block。event
:收到的事件类型(如连接请求、断开指示、安全认证完成等)。p_data
:事件附带的参数(类型根据不同事件不同)。
04-24 15:56:22.504907 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:481 l2c_csm_term_w4_sec_comp: LCID: 0x0046 st: TERM_W4_SEC_COMP evt: SECURITY_COMPLETE
04-24 15:56:22.504913 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:497 l2c_csm_term_w4_sec_comp: Not waiting for info response, sending connect response
04-24 15:56:22.504922 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:503 l2c_csm_term_w4_sec_comp: Not LE connection, sending configure request
static void l2c_csm_term_w4_sec_comp(tL2C_CCB* p_ccb, tL2CEVT event,
void* p_data) {
// 打日志:记录当前 Local CID、本状态名、接收到的事件名,便于调试。
LOG_INFO("LCID: 0x%04x st: TERM_W4_SEC_COMP evt: %s", p_ccb->local_cid,
l2c_csm_get_event_name(event));
switch (event) {
case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
// 链路断开通知(Link Layer)。链路断了,当前还在等待安全认证,就没必要继续了。
/* Tell security manager to abort */
btm_sec_abort_access_req(p_ccb->p_lcb->remote_bd_addr); // 通知 Security Manager 中止本次安全请求。
l2cu_release_ccb(p_ccb); // 释放掉当前的 Channel Control Block(内存、资源等都回收)
break;
case L2CEVT_SEC_COMP:
// 安全认证完成并且成功。
p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP; // 把 Channel 状态转为 等待上层(L2CA)回应连接。
/* Wait for the info resp in next state before sending connect ind (if
* needed) */
if (!p_ccb->p_lcb->w4_info_rsp) {
// 如果 不需要等待 Info Response,直接继续连接流程
// Info Response 是标准 L2CAP连接时用来了解对方特性的一步。
LOG_INFO("Not waiting for info response, sending connect response");
/* Don't need to get info from peer or already retrieved so continue */
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CONNECT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 开启连接超时定时器
if (p_ccb->p_lcb->transport != BT_TRANSPORT_LE) {
// 如果是经典蓝牙(BR/EDR)连接:
LOG_INFO("Not LE connection, sending configure request");
l2c_csm_send_connect_rsp(p_ccb); // 发送 Connect Response
l2c_csm_send_config_req(p_ccb); // 发送 Configuration Request(即开始配置通道参数)
} else {
// 如果是 BLE 连接(Bluetooth Low Energy)
if (p_ccb->ecoc) { // 是 Enhanced Credit Based Connection (ECoC)
// 使用 Credit-based 多通道连接(LE ECoC),一次可以连多个 CID
/* Handle Credit Based Connection */
LOG_INFO("Calling CreditBasedConnect_Ind_Cb(), num of cids: %d",
p_ccb->p_lcb->pending_ecoc_conn_cnt);
std::vector<uint16_t> pending_cids;
for (int i = 0; i < p_ccb->p_lcb->pending_ecoc_conn_cnt; i++) {
// 把要建立连接的所有 pending CIDs 收集起来。
uint16_t cid = p_ccb->p_lcb->pending_ecoc_connection_cids[i];
if (cid != 0) pending_cids.push_back(cid);
}
// 通知上层 Profile,有多个 CID 等待建立。
(*p_ccb->p_rcb->api.pL2CA_CreditBasedConnectInd_Cb)(
p_ccb->p_lcb->remote_bd_addr, pending_cids, p_ccb->p_rcb->psm,
p_ccb->peer_conn_cfg.mtu, p_ccb->remote_id);
} else {
// 否则是传统 BLE CoC, 使用传统 BLE CoC(Credit-Based Connection,单一通道)。
/* Handle BLE CoC */
LOG_INFO("Calling Connect_Ind_Cb(), CID: 0x%04x",
p_ccb->local_cid);
l2c_csm_send_connect_rsp(p_ccb); // 回复 connect rsp。
l2c_csm_indicate_connection_open(p_ccb); // 告知上层连接已经建立成功。
}
}
} else { // 如果还在等 Info Response
// 等待对方的 Info Response,比如设备特性等。
// 注意到这里讲了蓝牙兼容性问题(Bluesoleil响应慢的问题)。
/*
** L2CAP Connect Response will be sent out by 3 sec timer expiration
** because Bluesoleil doesn't respond to L2CAP Information Request.
** Bluesoleil seems to disconnect ACL link as failure case, because
** it takes too long (4~7secs) to get response.
** product version : Bluesoleil 2.1.1.0 EDR Release 060123
** stack version : 05.04.11.20060119
*/
/* Cancel ccb timer as security complete. waiting for w4_info_rsp
** once info rsp received, connection rsp timer will be started
** while sending connection ind to profiles
*/
alarm_cancel(p_ccb->l2c_ccb_timer); // 取消之前的定时器。
/* Waiting for the info resp, tell the peer to set a longer timer */
LOG_INFO("Waiting for info response, sending connect pending");
l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0); // 向对方回复 "连接还在处理中"(PENDING 状态),以防超时。
}
break;
case L2CEVT_SEC_COMP_NEG:
// 安全认证失败
if (((tL2C_CONN_INFO*)p_data)->status == BTM_DELAY_CHECK) {
// 特殊情况 BTM_DELAY_CHECK
// 如果失败原因是 "需要延迟检查"(通常是SM4安全模式下的一种处理)。
/* start a timer - encryption change not received before L2CAP connect
* req */
alarm_set_on_mloop(p_ccb->l2c_ccb_timer,
L2CAP_DELAY_CHECK_SM4_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 启动延迟定时器,后续补发认证。
} else {// 其他认证失败情况
if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE)
l2cu_reject_ble_connection(
p_ccb, p_ccb->remote_id,
L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION); // 如果是 BLE,发 BLE 的拒绝连接(错误码:认证失败)。
else
l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_SECURITY_BLOCK, 0); // 如果是 BR/EDR,发送 L2CAP 层的拒绝(SECURITY BLOCK)。
l2cu_release_ccb(p_ccb); // 最后释放掉这个 CCB。
}
break;
case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
// 有数据写入或收到数据,但当前正在断开中,丢弃数据并释放。
osi_free(p_data);
break;
case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
// 上层主动要求断开,直接释放通道。
l2cu_release_ccb(p_ccb);
break;
case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
// 对方发起断开。
l2cu_send_peer_disc_rsp(p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid,
p_ccb->remote_cid); // 回复 disconnect response。
/* Tell security manager to abort */
btm_sec_abort_access_req(p_ccb->p_lcb->remote_bd_addr); //通知 Security Manager 取消认证
l2cu_release_ccb(p_ccb); // 释放通道
break;
case L2CEVT_TIMEOUT:
// 超时,比如认证超时了,主动断 ACL 链路。
/* SM4 related. */
acl_disconnect_from_handle(
p_ccb->p_lcb->Handle(), HCI_ERR_AUTH_FAILURE,
"stack::l2cap::l2c_csm::l2c_csm_term_w4_sec_comp Event timeout");
break;
case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
// 安全模块通知可以重新发起安全请求,比如密钥刷新之类的。
btm_sec_l2cap_access_req(p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
false, &l2c_link_sec_comp, p_ccb); // 再次请求访问权限。
break;
default:
// 遇到未处理的事件,打印错误日志。
LOG_ERROR("Handling unexpected event:%s", l2c_csm_get_event_name(event));
}
LOG_INFO("Exit chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
}
这段 l2c_csm_term_w4_sec_comp
的本质是:
- 在等待安全认证的时候,处理各种意外情况(断链、认证失败、认证成功、超时等)。
- 保证:
- 正常认证成功时,能继续连接流程;
- 认证失败或断链时,能正确清理资源;
- 防止泄露或死锁。
目前我们只需要关注 L2CEVT_SEC_COMP认证成功的即可
- p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP : 把 Channel 状态转为 等待上层(L2CA)回应连接
- l2c_csm_send_connect_rsp(p_ccb) :发送 Connect Response
- l2c_csm_send_config_req(p_ccb) : 发送 Configuration Request(即开始配置通道参数)
3. l2c_csm_send_connect_rsp
04-24 15:56:22.504928 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_W4_L2CA_CONNECT_RSP [4], event=UPPER_LAYER_CONNECT_RSP [22]
04-24 15:56:22.504935 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:810 l2c_csm_w4_l2ca_connect_rsp: LCID: 0x0046 st: W4_L2CA_CON_RSP evt: UPPER_LAYER_CONNECT_RSP
04-24 15:56:22.504941 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:870 l2c_csm_w4_l2ca_connect_rsp: Sending connection ok for BR_EDR
static void l2c_csm_send_connect_rsp(tL2C_CCB* p_ccb) {
l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); // 发送 L2CEVT_L2CA_CONNECT_RSP 事件
}
void l2c_csm_execute(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
if (p_ccb == nullptr) {
LOG_WARN("CCB is null for event (%d)", event);
return;
}
LOG_INFO("Entry chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
switch (p_ccb->chnl_state) {
case CST_W4_L2CA_CONNECT_RSP:
l2c_csm_w4_l2ca_connect_rsp(p_ccb, event, p_data);
break;
break;
}
}
4. l2c_csm_w4_l2ca_connect_rsp
-
专门处理 CCB(Channel Control Block)在等待上层回应连接请求(Connect Response)阶段时发生的各种事件
-
p_ccb
:当前信道的控制块(Channel Control Block),管理一个逻辑信道(LCID)。 -
event
:本次触发状态机的事件类型(枚举)。 -
p_data
:附加数据(根据不同事件类型,结构体不同,比如tL2C_CONN_INFO
等)。
// system/stack/l2cap/l2c_csm.cc
static void l2c_csm_w4_l2ca_connect_rsp(tL2C_CCB* p_ccb, tL2CEVT event,
void* p_data) {
tL2C_CONN_INFO* p_ci; // 指向连接信息,后面处理连接响应用。
tL2C_LCB* p_lcb = p_ccb->p_lcb; // 指向对应的 LCB(Link Control Block,表示物理链接,例如 BR/EDR 或 LE 链路)。
tL2CA_DISCONNECT_IND_CB* disconnect_ind =
p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; // 上层注册的断开连接回调函数。
uint16_t local_cid = p_ccb->local_cid; // 本地的 CID(Channel ID),用于标识逻辑信道
LOG_INFO("LCID: 0x%04x st: W4_L2CA_CON_RSP evt: %s", p_ccb->local_cid,
l2c_csm_get_event_name(event)); // 日志,打印当前 Local CID、状态(等待上层连接响应 W4_L2CA_CON_RSP)和触发事件名。
switch (event) {
// 1. 链路层断开通知
case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
// 链路断开了(可能是对端断开或者链路异常中断)。
LOG_INFO("Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb); // 释放 p_ccb
(*disconnect_ind)(local_cid, false); // 调用上层回调 disconnect_ind() 通知上层,不需要等待 Disconnect Confirm
break;
case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP: // CREDIT_BASED 连接回应
// 收到上层对 Credit-Based 连接请求的响应(通常是 LE 上的信用基连接)。
p_ci = (tL2C_CONN_INFO*)p_data;
if ((p_lcb == nullptr) || (p_lcb && p_lcb->transport != BT_TRANSPORT_LE)) {// 检查链路是否存在且是 LE 链路。
LOG_WARN("LE link doesn't exist");
return;
}
l2cu_send_peer_credit_based_conn_res(p_ccb, p_ci->lcids,
p_ci->l2cap_result); // 回复对端连接结果
alarm_cancel(p_ccb->l2c_ccb_timer); // 停止 CCB 上的连接超时定时器
// 遍历 pending_ecoc_connection_cids 数组,设置对应 CCB 的状态:
for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
if (cid == 0) {
LOG_WARN("pending_ecoc_connection_cids[%d] is %d", i, cid);
continue;
}
tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
if (temp_p_ccb) {
auto it = std::find(p_ci->lcids.begin(), p_ci->lcids.end(), cid);
if (it != p_ci->lcids.end()) {
// 成功的 CID,状态设为 CST_OPEN(已连接)
temp_p_ccb->chnl_state = CST_OPEN;
} else {
l2cu_release_ccb(temp_p_ccb); // 失败的,释放 CCB。
}
}
else {
LOG_WARN("temp_p_ccb is NULL, pending_ecoc_connection_cids[%d] is %d", i, cid);
}
}
// 清空 pending_ecoc_connection_cids 相关字段
p_lcb->pending_ecoc_conn_cnt = 0;
memset(p_lcb->pending_ecoc_connection_cids, 0,
L2CAP_CREDIT_BASED_MAX_CIDS);
break;
// 经典连接回应
case L2CEVT_L2CA_CONNECT_RSP:
// 收到上层对传统连接请求(BR/EDR 或 LE)的响应。
p_ci = (tL2C_CONN_INFO*)p_data;
if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) {
// LE (信用基连接)
/* Result should be OK or Reject */
if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK)) {
// 如果上层同意(l2cap_result == L2CAP_CONN_OK),发送 OK 响应,状态设 CST_OPEN
l2cble_credit_based_conn_res(p_ccb, L2CAP_CONN_OK);
p_ccb->chnl_state = CST_OPEN;
alarm_cancel(p_ccb->l2c_ccb_timer);
} else {
// 否则发送拒绝并释放 CCB。
l2cble_credit_based_conn_res(p_ccb, p_ci->l2cap_result);
l2cu_release_ccb(p_ccb);
}
} else {
// BR/EDR (传统)
/* Result should be OK or PENDING */
if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK)) {
// 上层同意(l2cap_result == OK),发送 OK,转到 CST_CONFIG(配置阶段),开启配置超时定时器。
LOG_INFO("Sending connection ok for BR_EDR");
l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_OK, 0);
p_ccb->chnl_state = CST_CONFIG;
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CFG_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb);
} else {
// 上层暂时挂起(Pending),发送 PENDING,开启扩展连接超时定时器。
/* If pending, stay in same state and start extended timer */
LOG_INFO("Sending connection result %d and status %d",
p_ci->l2cap_result, p_ci->l2cap_status);
l2cu_send_peer_connect_rsp(p_ccb, p_ci->l2cap_result,
p_ci->l2cap_status);
alarm_set_on_mloop(p_ccb->l2c_ccb_timer,
L2CAP_CHNL_CONNECT_EXT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb);
}
}
break;
case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG:
// 上层拒绝 Credit-Based 连接请求。
p_ci = (tL2C_CONN_INFO*)p_data;
alarm_cancel(p_ccb->l2c_ccb_timer); // 停掉定时器。
if (p_lcb != nullptr) {
if (p_lcb->transport == BT_TRANSPORT_LE) {
// 如果链路存在且是 LE,发送拒绝回应
l2cu_send_peer_credit_based_conn_res(p_ccb, p_ci->lcids,
p_ci->l2cap_result);
}
for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
l2cu_release_ccb(temp_p_ccb); // 释放所有 pending CCB。
}
p_lcb->pending_ecoc_conn_cnt = 0;
memset(p_lcb->pending_ecoc_connection_cids, 0,
L2CAP_CREDIT_BASED_MAX_CIDS);
}
break;
case L2CEVT_L2CA_CONNECT_RSP_NEG:
// 传统连接拒绝
p_ci = (tL2C_CONN_INFO*)p_data;
// 根据链路类型(LE or BR/EDR)发送拒绝。
if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE)
l2cble_credit_based_conn_res(p_ccb, p_ci->l2cap_result);
else
l2cu_send_peer_connect_rsp(p_ccb, p_ci->l2cap_result,
p_ci->l2cap_status);
l2cu_release_ccb(p_ccb); // 然后释放 CCB。
break;
// 超时事件
case L2CEVT_TIMEOUT:
// 在等待上层响应期间超时。
l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_NO_PSM, 0); // 向对端回复 NO_PSM 错误(表明不支持)。
LOG_INFO("Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb); // 释放 CCB。
(*disconnect_ind)(local_cid, false); // 调用上层断开通知(不需要等待确认)
break;
// 数据相关事件
case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
osi_free(p_data); // 在连接未完成阶段,不应该有数据收发,所以这里直接 free 掉。
break;
// 上层请求主动断开
case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
l2cu_send_peer_disc_req(p_ccb); // 发送 Disconnect Request 给对端, 状态机切换到 CST_W4_L2CAP_DISCONNECT_RSP(等待断开回应)。
p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_DISCONNECT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 开启断开超时定时器
break;
case L2CEVT_L2CAP_INFO_RSP: // 收到信息回应
// 收到 L2CAP 信息响应(比如询问对端的特性)。
/* We have feature info, so now give the upper layer connect IND */
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CONNECT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 重新开启连接超时定时器。
LOG_INFO("Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid);
l2c_csm_send_connect_rsp(p_ccb); // 调用 l2c_csm_send_connect_rsp(p_ccb) 给上层
l2c_csm_send_config_req(p_ccb); // 调用 l2c_csm_send_config_req(p_ccb) 向对端发配置请求。
break;
default:
// 收到不符合当前状态的事件,打错误日志。
LOG_ERROR("Handling unexpected event:%s", l2c_csm_get_event_name(event));
}
LOG_INFO("Exit chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
}
主要事件 | 主要处理逻辑 |
---|---|
链路断开 | 释放 CCB,通知上层。 |
上层 Credit-based Connect 回复 | 回复对端,调整信道状态。 |
上层传统 Connect 回复 | 回复对端,切换到配置阶段或超时。 |
上层拒绝连接 | 回复对端,释放资源。 |
超时 | 回复对端错误,释放资源,通知上层。 |
数据写或收 | 丢弃。 |
上层请求断开 | 发送断开请求,等待响应。 |
收到信息回应 | 继续完成连接建立。 |
我们这里只需要关注 L2CEVT_L2CA_CONNECT_RSP 事件的处理:
- l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_OK, 0);
- p_ccb->chnl_state = CST_CONFIG;
04-24 15:56:22.504928 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_W4_L2CA_CONNECT_RSP [4], event=UPPER_LAYER_CONNECT_RSP [22]
04-24 15:56:22.504935 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:810 l2c_csm_w4_l2ca_connect_rsp: LCID: 0x0046 st: W4_L2CA_CON_RSP evt: UPPER_LAYER_CONNECT_RSP
04-24 15:56:22.504941 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:870 l2c_csm_w4_l2ca_connect_rsp: Sending connection ok for BR_EDR
这里就是 本节 车机回复同意连接 ok , 的地方。 我们可以对照 btsnoop 日志看一下。
1082 2025-04-24 15:56:22.505074 22:22:96:de:b1:39 (leo 8295 chan) vivoMobi_91:b0:62 (cbx) L2CAP 21 Sent Connection Response - Success (SCID: 0x0041, DCID: 0x0046)
Frame 1082: 21 bytes on wire (168 bits), 21 bytes captured (168 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 12
CID: L2CAP Signaling Channel (0x0001)
Command: Connection Response
Command Code: Connection Response (0x03)
Command Identifier: 0x11
Command Length: 8
Destination CID: Dynamically Allocated Channel (0x0046)
Source CID: Dynamically Allocated Channel (0x0041)
Result: Successful (0x0000)
Status: No further information available (0x0000)
3. 车机发起配置请求
1083 2025-04-24 15:56:22.505308 22:22:96:de:b1:39 (leo 8295 chan) vivoMobi_91:b0:62 (cbx) L2CAP 21 Sent Configure Request (DCID: 0x0041)
Frame 1083: 21 bytes on wire (168 bits), 21 bytes captured (168 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 12
CID: L2CAP Signaling Channel (0x0001)
Command: Configure Request
Command Code: Configure Request (0x04)
Command Identifier: 0x0e
Command Length: 8
Destination CID: Dynamically Allocated Channel (0x0041)
0000 0000 0000 000. = Reserved: 0x0000
.... .... .... ...0 = Continuation Flag: False
Option: MTU
在 l2c_csm_term_w4_sec_comp 函数的分析中:
- p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP : 把 Channel 状态转为 等待上层(L2CA)回应连接
- l2c_csm_send_connect_rsp(p_ccb) :发送 Connect Response
- p_ccb->chnl_state = CST_CONFIG;
- l2c_csm_send_config_req(p_ccb) : 发送 Configuration Request(即开始配置通道参数)
触发了 l2c_csm_send_config_req, 本节就来分析这个。
1. l2c_csm_send_config_req
static void l2c_csm_send_config_req(tL2C_CCB* p_ccb) {
tL2CAP_CFG_INFO config{};
config.mtu_present = true;
config.mtu = p_ccb->p_rcb->my_mtu;
p_ccb->max_rx_mtu = config.mtu;
if (p_ccb->p_rcb->ertm_info.preferred_mode != L2CAP_FCR_BASIC_MODE) {
config.fcr_present = true;
config.fcr = kDefaultErtmOptions;
}
p_ccb->our_cfg = config;
l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONFIG_REQ, &config); // 这里发送了 L2CEVT_L2CA_CONFIG_REQ 消息
}
void l2c_csm_execute(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
if (p_ccb == nullptr) {
LOG_WARN("CCB is null for event (%d)", event);
return;
}
LOG_INFO("Entry chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
switch (p_ccb->chnl_state) {
case CST_CONFIG:
l2c_csm_config(p_ccb, event, p_data);
break;
break;
}
}
04-24 15:56:22.505097 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CONFIG [5], event=UPPER_LAYER_CONFIG_REQ [24]
04-24 15:56:22.505104 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:975 l2c_csm_config: LCID: 0x0046 st: CONFIG evt: UPPER_LAYER_CONFIG_REQ
04-24 15:56:22.505267 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:1235 l2c_csm_config: Exit chnl_state=CST_CONFIG [5], event=UPPER_LAYER_CONFIG_REQ [24]
2. l2c_csm_config
- 信道已经进入"正在配置"阶段(CONFIG状态),不同的事件触发不同的逻辑处理。
static void l2c_csm_config(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
tL2CAP_CFG_INFO* p_cfg = (tL2CAP_CFG_INFO*)p_data; // 把传进来的 p_data 强制转换成普通L2CAP配置结构体。
tL2CA_DISCONNECT_IND_CB* disconnect_ind =
p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; // 保存上层应用注册的“断开指示”回调函数指针,后面如果要通知上层断开连接,就直接用这个回调。
uint16_t local_cid = p_ccb->local_cid; // 本地通道号保存一份副本,后续打印日志或传递参数方便。
uint8_t cfg_result; // 用于保存peer配置请求处理后的结果,比如"成功"、"需要断开"、"需要重新配置"。
tL2C_LCB* p_lcb = p_ccb->p_lcb; // 链接控制块(LCB),描述物理连接(ACL link)状态。
tL2C_CCB* temp_p_ccb; // 临时的CCB指针,目前还没用到。
tL2CAP_LE_CFG_INFO* p_le_cfg = (tL2CAP_LE_CFG_INFO*)p_data; // 如果是LE credit-based flow,会用到这个类型(区别于经典的L2CAP)。 处理LE Credit-Based的Reconfig消息
LOG_INFO("LCID: 0x%04x st: CONFIG evt: %s", p_ccb->local_cid,
l2c_csm_get_event_name(event)); // 打印出当前信道ID、当前状态(CONFIG)、接收到的事件名。
switch (event) {
case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
// 链路层提示说物理链路断开了(ACL link down了)。 比如手机蓝牙突然关闭,车机收到链路断开的通知,就走这里
LOG_INFO("Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb); // 释放CCB(信道控制块),因为连接没了;
(*disconnect_ind)(local_cid, false); // 通知上层连接断开,参数 false 表示“不需要上层回复Confirm”。
break;
case L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_REQ:
// 接收到Peer设备发来的LE Credit-Based重配置请求。比如耳机重协商MTU大小时
/* For ecoc reconfig is handled below in l2c_ble. In case of success
* let us notify upper layer about the reconfig
*/
LOG_INFO("Calling LeReconfigCompleted_Cb(), CID: 0x%04x",
p_ccb->local_cid);
// 调用上层注册的Reconfig完成回调,告诉上层“Peer发起了重配置,处理结果是成功的”。
(*p_ccb->p_rcb->api.pL2CA_CreditBasedReconfigCompleted_Cb)(
p_lcb->remote_bd_addr, p_ccb->local_cid, false, p_le_cfg);
break;
case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
// 对端发来配置请求
// 调用底层函数解析对端发来的配置请求内容。
cfg_result = l2cu_process_peer_cfg_req(p_ccb, p_cfg);
if (cfg_result == L2CAP_PEER_CFG_OK) { // 对端配置没问题。
LOG_INFO("Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d",
p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT));
l2c_csm_send_config_rsp_ok(p_ccb); // 发送Config Response(OK)给对端。
if (p_ccb->config_done & OB_CFG_DONE) { // 如果自己也已经完成对对方的配置(即 OB_CFG_DONE),检查是否能进入 OPEN 状态
if (p_ccb->remote_config_rsp_result == L2CAP_CFG_OK) {
l2c_csm_indicate_connection_open(p_ccb); // 如果对端返回OK,那就打开连接;
} else { // 否则如果我是发起方,本地上报错误。
if (p_ccb->connection_initiator == L2CAP_INITIATOR_LOCAL) {
(*p_ccb->p_rcb->api.pL2CA_Error_Cb)(p_ccb->local_cid,
L2CAP_CFG_FAILED_NO_REASON);
bluetooth::shim::CountCounterMetrics(
android::bluetooth::CodePathCounterKeyEnum::
L2CAP_CONFIG_REQ_FAILURE,
1);
}
}
}
} else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT) { // 配置严重不兼容,需要断开
/* Disconnect if channels are incompatible */
LOG_INFO("incompatible configurations disconnect");
l2cu_disconnect_chnl(p_ccb); //断开连接。
} else /* Return error to peer so it can renegotiate if possible */
{ // 配置部分不兼容,需要协商:
LOG_INFO("incompatible configurations trying reconfig");
l2cu_send_peer_config_rsp(p_ccb, p_cfg); // 发送配置响应,表示部分参数不接受,等对端重新发新的配置。
}
break;
case L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_RSP:
// 收到 BLE Credit-Based 连接重新配置的响应。
// 更新状态:配置完成,连接状态改为 OPEN,取消超时定时器。
p_ccb->config_done |= OB_CFG_DONE;
p_ccb->config_done |= RECONFIG_FLAG;
p_ccb->chnl_state = CST_OPEN;
alarm_cancel(p_ccb->l2c_ccb_timer);
LOG_INFO("Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid);
// 通知上层,Credit-Based重配置成功完成(`true` 表示成功)。
p_ccb->p_rcb->api.pL2CA_CreditBasedReconfigCompleted_Cb(
p_lcb->remote_bd_addr, p_ccb->local_cid, true, p_le_cfg);
break;
case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */
// 对端发来了配置响应
l2cu_process_peer_cfg_rsp(p_ccb, p_cfg); // 处理对端的配置响应,把对端配置更新到本地控制块 p_ccb->peer_cfg
/* TBD: When config options grow beyong minimum MTU (48 bytes)
* logic needs to be added to handle responses with
* continuation bit set in flags field.
* 1. Send additional config request out until C-bit is cleared in
* response
*/
p_ccb->config_done |= OB_CFG_DONE; // 标记自己发出去的配置已经完成了,OB_CFG_DONE(OutBound Config Done)。
if (p_ccb->config_done & IB_CFG_DONE) { // 如果对端的配置也完成了(InBound Config Done),说明双方配置都搞定了,可以考虑继续后续操作。
/* Verify two sides are in compatible modes before continuing */
if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
// 检查自己和对端的FCR模式(Flow Control and Retransmission mode,比如 Basic/ERTM/Streaming)是否一致。 如果不一致,无法通信,需要断开
l2cu_send_peer_disc_req(p_ccb); // 发送 Disconnect Request 给对方。
LOG_WARN(
"Calling Disconnect_Ind_Cb(Incompatible CFG), CID: "
"0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb); // 释放 CCB 资源,通知上层断开连接(false 表示无须上层回复)。
(*disconnect_ind)(local_cid, false);
break;
}
// 如果配置兼容:
p_ccb->config_done |= RECONFIG_FLAG; // 标记为已经重新配置(即配置完成后还可以后续改配)。
p_ccb->chnl_state = CST_OPEN; // 把信道状态切换为 CST_OPEN,进入数据传输阶段。
l2c_link_adjust_chnl_allocation(); // 调整物理链路资源分配。
alarm_cancel(p_ccb->l2c_ccb_timer); // 取消配置超时定时器
/* If using eRTM and waiting for an ACK, restart the ACK timer */
if (p_ccb->fcrb.wait_ack) l2c_fcr_start_timer(p_ccb); // 如果在用增强模式(eRTM)且在等ACK包,则重启ACK定时器
/*
** check p_ccb->our_cfg.fcr.mon_tout and
*p_ccb->our_cfg.fcr.rtrans_tout
** we may set them to zero when sending config request during
*renegotiation
*/
if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) &&
((p_ccb->our_cfg.fcr.mon_tout == 0) ||
(p_ccb->our_cfg.fcr.rtrans_tout))) {
// 如果使用 eRTM 且 monitor timeout / retransmission timeout 不合理(=0),调整一下参数。
l2c_fcr_adj_monitor_retran_timeout(p_ccb);
}
/* See if we can forward anything on the hold queue */
if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
// 检查有没有挂起的待发数据,有的话现在可以发了!
l2c_link_check_send_pkts(p_ccb->p_lcb, 0, NULL);
}
}
LOG_INFO("Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid); // 打日志,表明调用了配置完成回调
p_ccb->remote_config_rsp_result = p_cfg->result; // 记录对端回复的配置结果(比如成功/失败码)。
if (p_ccb->config_done & IB_CFG_DONE) {
l2c_csm_indicate_connection_open(p_ccb); // 如果对端配置也完成,通知上层连接正式建立
}
break;
case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer config error rsp */
// 对端配置失败回复
/* Disable the Timer */
alarm_cancel(p_ccb->l2c_ccb_timer); // 停掉配置超时定时器。
/* If failure was channel mode try to renegotiate */
if (!l2c_fcr_renegotiate_chan(p_ccb, p_cfg)) { // 如果不能重新协商频道(比如因为模式不兼容),执行失败处理
LOG_INFO("Calling Config_Rsp_Cb(), CID: 0x%04x, Failure: %d",
p_ccb->local_cid, p_cfg->result);
if (p_ccb->connection_initiator == L2CAP_INITIATOR_LOCAL) {
// 如果是本地发起连接的角色,通知上层出错
(*p_ccb->p_rcb->api.pL2CA_Error_Cb)(p_ccb->local_cid,
L2CAP_CFG_FAILED_NO_REASON);
bluetooth::shim::CountCounterMetrics(
android::bluetooth::CodePathCounterKeyEnum::L2CAP_CONFIG_RSP_NEG,
1);
}
}
break;
// 对端主动断开请求
case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_DISCONNECT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 设置断开流程的超时器
p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; // 切换到等待上层应用回复 L2CA_DisconnectRsp 的状态。
LOG_INFO("Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed",
p_ccb->local_cid);
(*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, true); // 通知上层:对端请求断开连接(需要应用层回复)。
l2c_csm_send_disconnect_rsp(p_ccb); // 同时主动发出 Disconnect Response。
break;
case L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ:
// 本地发起credit-based重新配置请求
l2cu_send_credit_based_reconfig_req(p_ccb, (tL2CAP_LE_CFG_INFO*)p_data); // 发出 Credit Based Channel 的 Reconfiguration Request
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CFG_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 设置 Reconfig 超时定时器。
break;
case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
// 本地应用层发来配置请求
l2cu_process_our_cfg_req(p_ccb, p_cfg); // 处理自己配置请求内容,发给对端。
l2cu_send_peer_config_req(p_ccb, p_cfg);
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CFG_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 设置配置超时定时器。
break;
case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */
// 本地应用层收到对端的配置响应
l2cu_process_our_cfg_rsp(p_ccb, p_cfg); // 处理对端返回的响应并标记 IB_CFG_DONE。
p_ccb->config_done |= IB_CFG_DONE;
// 后续逻辑跟前面的 CONFIG_RSP 类似,不再赘述。
if (p_ccb->config_done & OB_CFG_DONE) {
/* Verify two sides are in compatible modes before continuing */
if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
l2cu_send_peer_disc_req(p_ccb);
LOG_WARN(
"Calling Disconnect_Ind_Cb(Incompatible CFG), CID: "
"0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb);
(*disconnect_ind)(local_cid, false);
break;
}
p_ccb->config_done |= RECONFIG_FLAG;
p_ccb->chnl_state = CST_OPEN;
l2c_link_adjust_chnl_allocation();
alarm_cancel(p_ccb->l2c_ccb_timer);
}
l2cu_send_peer_config_rsp(p_ccb, p_cfg);
/* If using eRTM and waiting for an ACK, restart the ACK timer */
if (p_ccb->fcrb.wait_ack) l2c_fcr_start_timer(p_ccb);
/* See if we can forward anything on the hold queue */
if ((p_ccb->chnl_state == CST_OPEN) &&
(!fixed_queue_is_empty(p_ccb->xmit_hold_q))) {
l2c_link_check_send_pkts(p_ccb->p_lcb, 0, NULL);
}
break;
case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
// 上层要求断开连接
// 发送 Disconnect Request,切换到等待响应状态。
l2cu_send_peer_disc_req(p_ccb);
p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_DISCONNECT_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 设置断开超时器。
break;
case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
// 收到对端的数据包
LOG_INFO("Calling DataInd_Cb(), CID: 0x%04x", p_ccb->local_cid);
// 判断是否是固定信道:
if (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL &&
p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL) {
if (p_ccb->local_cid < L2CAP_BASE_APPL_CID) {
// 如果是且注册了固定信道的回调,则直接调用。
if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL]
.pL2CA_FixedData_Cb != nullptr) {
p_ccb->metrics.rx(static_cast<BT_HDR*>(p_data)->len);
(*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL]
.pL2CA_FixedData_Cb)(p_ccb->local_cid,
p_ccb->p_lcb->remote_bd_addr,
(BT_HDR*)p_data);
} else {
if (p_data != nullptr) osi_free_and_reset(&p_data);
}
break;
}
}
// 否则调用普通动态信道的回调 pL2CA_DataInd_Cb。
if (p_data) p_ccb->metrics.rx(static_cast<BT_HDR*>(p_data)->len);
(*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR*)p_data);
break;
case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
// 本地应用层发数据
if (p_ccb->config_done & OB_CFG_DONE)
// 如果自己发出去的配置完成了,排入发送队列,否则丢弃数据
l2c_enqueue_peer_data(p_ccb, (BT_HDR*)p_data);
else
osi_free(p_data);
break;
case L2CEVT_TIMEOUT: // 配置或连接过程超时
if (p_ccb->ecoc) {
// 如果是 Credit Based ECOC 连接,且处于 Reconfig 中,遍历所有 CCB,强制断开。
for (temp_p_ccb = p_lcb->ccb_queue.p_first_ccb; temp_p_ccb;
temp_p_ccb = temp_p_ccb->p_next_ccb) {
if ((temp_p_ccb->in_use) && (temp_p_ccb->reconfig_started)) {
(*temp_p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(
temp_p_ccb->local_cid, false);
l2cu_release_ccb(temp_p_ccb);
}
}
acl_disconnect_from_handle(
p_ccb->p_lcb->Handle(), HCI_ERR_CONN_CAUSE_LOCAL_HOST,
"stack::l2cap::l2c_csm::l2c_csm_config timeout");
return;
}
// 否则发送 Disconnect Request 断开连接。
l2cu_send_peer_disc_req(p_ccb);
LOG_INFO("Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb);
(*disconnect_ind)(local_cid, false);
break;
default:
LOG_ERROR("Handling unexpected event:%s", l2c_csm_get_event_name(event));
}
LOG_INFO("Exit chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
}
目前 这个阶段,我们只需要关注:
case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
// 本地应用层发来配置请求
l2cu_process_our_cfg_req(p_ccb, p_cfg); // 处理自己配置请求内容,发给对端。
l2cu_send_peer_config_req(p_ccb, p_cfg);
alarm_set_on_mloop(p_ccb->l2c_ccb_timer, L2CAP_CHNL_CFG_TIMEOUT_MS,
l2c_ccb_timer_timeout, p_ccb); // 设置配置超时定时器。
break;
- l2cu_send_peer_config_req(p_ccb, p_cfg); 向对端发送 配置请求。
- 此时并没有切状态
1083 2025-04-24 15:56:22.505308 22:22:96:de:b1:39 (leo 8295 chan) vivoMobi_91:b0:62 (cbx) L2CAP 21 Sent Configure Request (DCID: 0x0041)
Frame 1083: 21 bytes on wire (168 bits), 21 bytes captured (168 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 12
CID: L2CAP Signaling Channel (0x0001)
Command: Configure Request
Command Code: Configure Request (0x04)
Command Identifier: 0x0e
Command Length: 8
Destination CID: Dynamically Allocated Channel (0x0041)
0000 0000 0000 000. = Reserved: 0x0000
.... .... .... ...0 = Continuation Flag: False
Option: MTU
04-24 15:56:22.505097 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CONFIG [5], event=UPPER_LAYER_CONFIG_REQ [24]
04-24 15:56:22.505104 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:975 l2c_csm_config: LCID: 0x0046 st: CONFIG evt: UPPER_LAYER_CONFIG_REQ
04-24 15:56:22.505267 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:1235 l2c_csm_config: Exit chnl_state=CST_CONFIG [5], event=UPPER_LAYER_CONFIG_REQ [24]
4. 接收到手机的配置请求
1089 2025-04-24 15:56:22.598410 vivoMobi_91:b0:62 (cbx) 22:22:96:de:b1:39 (leo 8295 chan) L2CAP 21 Rcvd Configure Request (DCID: 0x0046)
Frame 1089: 21 bytes on wire (168 bits), 21 bytes captured (168 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 12
CID: L2CAP Signaling Channel (0x0001)
Command: Configure Request
Command Code: Configure Request (0x04)
Command Identifier: 0x12
Command Length: 8
Destination CID: Dynamically Allocated Channel (0x0046)
0000 0000 0000 000. = Reserved: 0x0000
.... .... .... ...0 = Continuation Flag: False
Option: MTU
04-24 15:56:22.598728 6130 6190 I bt_l2c_main: packages/modules/Bluetooth/system/stack/l2cap/l2c_main.cc:310 process_l2cap_cmd: cmd_code: 4, id:18, cmd_len:8
04-24 15:56:22.598769 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CONFIG [5], event=PEER_CONFIG_REQ [14]
04-24 15:56:22.598780 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:975 l2c_csm_config: LCID: 0x0046 st: CONFIG evt: PEER_CONFIG_REQ
04-24 15:56:22.598793 6130 6190 I bt_l2cap: packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: l2c_fcr_process_peer_cfg_req() CFG fcr_present:0 fcr.mode:0 CCB FCR mode:0 preferred: 0
04-24 15:56:22.598803 6130 6190 I bt_l2cap: packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: l2cu_adjust_out_mps use 0 Based on peer_cfg.fcr.mps: 0 packet_size: 1011
04-24 15:56:22.598812 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:999 l2c_csm_config: Calling Config_Req_Cb(), CID: 0x0046, C-bit 0
- 此时我们收到了手机 对我们发起的 配置请求, 我们看一下该如何处理。
#define L2CAP_CMD_CONFIG_REQ 0x04
// system/stack/l2cap/l2c_main.cc process_l2cap_cmd 中对于 cmd_code: 4 的处理
case L2CAP_CMD_CONFIG_REQ: {
uint8_t* p_cfg_end = p + cmd_len; // 记录配置请求的结尾位置,用于控制循环边界。
// 标记是否发现不支持或格式错误的配置项,并统计被拒绝的长度(用于稍后构造拒绝响应)。
bool cfg_rej = false;
uint16_t cfg_rej_len = 0;
// 每个配置请求头部需要 4 字节(LCID + FLAGS),数据不够就返回。
uint16_t lcid;
if (p + 4 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CMD_CONFIG_REQ");
return;
}
STREAM_TO_UINT16(lcid, p); // lcid:本地通道号,表示配置哪个通道。
STREAM_TO_UINT16(cfg_info.flags, p); // 如 Continuation 标志(如果选项太长需要分段发送)。
uint8_t* p_cfg_start = p; // 记录配置选项的起始位置,用于可能的拒绝响应。
// 清除所有配置标志位(标记哪些选项被包含)。
cfg_info.flush_to_present = cfg_info.mtu_present =
cfg_info.qos_present = cfg_info.fcr_present = cfg_info.fcs_present =
false;
// 开始解析每个配置选项
while (p < p_cfg_end) {
// 进入配置选项循环,直到处理完所有内容。
// 每个选项结构:code (1B) + len (1B) + data (len B)
uint8_t cfg_code, cfg_len;
if (p + 2 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CMD_CONFIG_REQ sub_event");
return;
}
STREAM_TO_UINT8(cfg_code, p);
STREAM_TO_UINT8(cfg_len, p);
// 处理每种配置类型
switch (cfg_code & 0x7F) {
case L2CAP_CFG_TYPE_MTU: // MTU 设置
cfg_info.mtu_present = true;
if (cfg_len != 2) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT16(cfg_info.mtu, p);
break;
// Flush Timeout 设置
case L2CAP_CFG_TYPE_FLUSH_TOUT:
cfg_info.flush_to_present = true;
if (cfg_len != 2) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT16(cfg_info.flush_to, p);
break;
// QoS 设置(14 字节)
case L2CAP_CFG_TYPE_QOS:
cfg_info.qos_present = true;
if (cfg_len != 2 + 5 * 4) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
STREAM_TO_UINT8(cfg_info.qos.service_type, p);
STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
STREAM_TO_UINT32(cfg_info.qos.token_bucket_size, p);
STREAM_TO_UINT32(cfg_info.qos.peak_bandwidth, p);
STREAM_TO_UINT32(cfg_info.qos.latency, p);
STREAM_TO_UINT32(cfg_info.qos.delay_variation, p);
break;
// FCR(流控制与重传)设置(9 字节)
case L2CAP_CFG_TYPE_FCR:
cfg_info.fcr_present = true;
if (cfg_len != 3 + 3 * 2) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT8(cfg_info.fcr.mode, p);
STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
STREAM_TO_UINT16(cfg_info.fcr.rtrans_tout, p);
STREAM_TO_UINT16(cfg_info.fcr.mon_tout, p);
STREAM_TO_UINT16(cfg_info.fcr.mps, p);
break;
// FCS 设置(Frame Check Sequence)
case L2CAP_CFG_TYPE_FCS:
cfg_info.fcs_present = true;
if (cfg_len != 1) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT8(cfg_info.fcs, p);
break;
// 扩展流控设置(EXT FLOW)
case L2CAP_CFG_TYPE_EXT_FLOW:
cfg_info.ext_flow_spec_present = true;
if (cfg_len != 2 + 2 + 3 * 4) {
return;
}
if (p + cfg_len > p_next_cmd) {
return;
}
STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.sdu_inter_time, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.access_latency, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.flush_timeout, p);
break;
// 处理未知或格式错误的选项
default:
/* sanity check option length */
if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len) {
if (p + cfg_len > p_next_cmd) return;
p += cfg_len;
// 注:cfg_code 第 7 位为 1 表示此选项“不可拒绝”
if ((cfg_code & 0x80) == 0) { // // “可拒绝”位没设置
cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
cfg_rej = true;
}
}
/* bad length; force loop exit */
else {
p = p_cfg_end;
cfg_rej = true;
}
break;
}
}
// 查找此配置请求对应的通道 CCB.
tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
if (p_ccb) {
// 如果 CCB 存在:
p_ccb->remote_id = id;
if (cfg_rej) {
// 若发现不合法配置项,则发送 Config Reject。
l2cu_send_peer_config_rej(
p_ccb, p_cfg_start, (uint16_t)(cmd_len - L2CAP_CONFIG_REQ_LEN),
cfg_rej_len);
} else {
// 否则将 cfg_info 交给状态机处理配置请求。
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info);
}
} else {
// 如果 CCB 不存在:
// 通道无效,发送 Command Reject(CID 无效)响应。
/* updated spec says send command reject on invalid cid */
l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0);
}
break;
}
- 将 cfg_info 交给状态机处理配置请求。
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info);
这里发送了 L2CEVT_L2CAP_CONFIG_REQ 消息给状态机
L2CEVT_L2CAP_CONFIG_REQ = 14, /* request */
此时 p_ccb->chnl_state 依然是 CST_CONFIG 状态,
void l2c_csm_execute(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
if (p_ccb == nullptr) {
LOG_WARN("CCB is null for event (%d)", event);
return;
}
LOG_INFO("Entry chnl_state=%s [%d], event=%s [%d]",
channel_state_text(p_ccb->chnl_state).c_str(), p_ccb->chnl_state,
l2c_csm_get_event_name(event), event);
switch (p_ccb->chnl_state) {
case CST_CONFIG:
l2c_csm_config(p_ccb, event, p_data);
break;
break;
}
}
// 下面是 l2c_csm_config 函数中对于 L2CEVT_L2CAP_CONFIG_REQ 的处理
case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
// 对端发来配置请求
// 调用底层函数解析对端发来的配置请求内容。
cfg_result = l2cu_process_peer_cfg_req(p_ccb, p_cfg);
if (cfg_result == L2CAP_PEER_CFG_OK) { // 对端配置没问题。
LOG_INFO("Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d",
p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT));
l2c_csm_send_config_rsp_ok(p_ccb); // 发送Config Response(OK)给对端。
if (p_ccb->config_done & OB_CFG_DONE) { // 如果自己也已经完成对对方的配置(即 OB_CFG_DONE),检查是否能进入 OPEN 状态
if (p_ccb->remote_config_rsp_result == L2CAP_CFG_OK) {
l2c_csm_indicate_connection_open(p_ccb); // 如果对端返回OK,那就打开连接;
} else { // 否则如果我是发起方,本地上报错误。
if (p_ccb->connection_initiator == L2CAP_INITIATOR_LOCAL) {
(*p_ccb->p_rcb->api.pL2CA_Error_Cb)(p_ccb->local_cid,
L2CAP_CFG_FAILED_NO_REASON);
bluetooth::shim::CountCounterMetrics(
android::bluetooth::CodePathCounterKeyEnum::
L2CAP_CONFIG_REQ_FAILURE,
1);
}
}
}
} else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT) { // 配置严重不兼容,需要断开
/* Disconnect if channels are incompatible */
LOG_INFO("incompatible configurations disconnect");
l2cu_disconnect_chnl(p_ccb); //断开连接。
} else /* Return error to peer so it can renegotiate if possible */
{ // 配置部分不兼容,需要协商:
LOG_INFO("incompatible configurations trying reconfig");
l2cu_send_peer_config_rsp(p_ccb, p_cfg); // 发送配置响应,表示部分参数不接受,等对端重新发新的配置。
}
break;
- l2c_csm_send_config_rsp_ok : 向手机 侧发送了 配置应答, 状态没有变化还是 CST_CONFIG
5. 向手机发送配置应答
1090 2025-04-24 15:56:22.598958 22:22:96:de:b1:39 (leo 8295 chan) vivoMobi_91:b0:62 (cbx) L2CAP 19 Sent Configure Response - Success (SCID: 0x0041)
Frame 1090: 19 bytes on wire (152 bits), 19 bytes captured (152 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 10
CID: L2CAP Signaling Channel (0x0001)
Command: Configure Response
Command Code: Configure Response (0x05)
Command Identifier: 0x12
Command Length: 6
Source CID: Dynamically Allocated Channel (0x0041)
0000 0000 0000 000. = Reserved: 0x0000
.... .... .... ...0 = Continuation Flag: False
Result: Success (0x0000)
04-24 15:56:22.598819 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CONFIG [5], event=UPPER_LAYER_CONFIG_RSP [25]
04-24 15:56:22.598826 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:975 l2c_csm_config: LCID: 0x0046 st: CONFIG evt: UPPER_LAYER_CONFIG_RSP
- 调用 l2c_csm_send_config_rsp_ok 来处理 向手机发送配置应答的逻辑。
static void l2c_csm_send_config_rsp_ok(tL2C_CCB* p_ccb) {
tL2CAP_CFG_INFO config{};
config.result = L2CAP_CFG_OK;
l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONFIG_RSP, &config);
}
- 发现这里有触发了 L2CEVT_L2CA_CONFIG_RSP 事件
// 此时状态还是 CST_CONFIG, 所以 l2c_csm_execute 继续调用 l2c_csm_config 函数
// 下面是 l2c_csm_config 函数中对于 L2CEVT_L2CA_CONFIG_RSP 的处理
L2CEVT_L2CA_CONFIG_RSP = 25, /* config response */
case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */
// 本地应用层收到对端的配置响应
l2cu_process_our_cfg_rsp(p_ccb, p_cfg); // 处理对端返回的响应并标记 IB_CFG_DONE。
p_ccb->config_done |= IB_CFG_DONE;
// 后续逻辑跟前面的 CONFIG_RSP 类似,不再赘述。
if (p_ccb->config_done & OB_CFG_DONE) {
/* Verify two sides are in compatible modes before continuing */
if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
l2cu_send_peer_disc_req(p_ccb);
LOG_WARN(
"Calling Disconnect_Ind_Cb(Incompatible CFG), CID: "
"0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb);
(*disconnect_ind)(local_cid, false);
break;
}
p_ccb->config_done |= RECONFIG_FLAG;
p_ccb->chnl_state = CST_OPEN;
l2c_link_adjust_chnl_allocation();
alarm_cancel(p_ccb->l2c_ccb_timer);
}
l2cu_send_peer_config_rsp(p_ccb, p_cfg); // 这里是向手机 发送配置响应的地方。
/* If using eRTM and waiting for an ACK, restart the ACK timer */
if (p_ccb->fcrb.wait_ack) l2c_fcr_start_timer(p_ccb);
/* See if we can forward anything on the hold queue */
if ((p_ccb->chnl_state == CST_OPEN) &&
(!fixed_queue_is_empty(p_ccb->xmit_hold_q))) {
l2c_link_check_send_pkts(p_ccb->p_lcb, 0, NULL);
}
break;
- l2cu_send_peer_config_rsp(p_ccb, p_cfg): 这里是向手机 发送配置响应的地方。
6. 接收到手机的配置应答
1091 2025-04-24 15:56:22.599401 vivoMobi_91:b0:62 (cbx) 22:22:96:de:b1:39 (leo 8295 chan) L2CAP 19 Rcvd Configure Response - Success (SCID: 0x0046)
Frame 1091: 19 bytes on wire (152 bits), 19 bytes captured (152 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Length: 10
CID: L2CAP Signaling Channel (0x0001)
Command: Configure Response
Command Code: Configure Response (0x05)
Command Identifier: 0x0e
Command Length: 6
Source CID: Dynamically Allocated Channel (0x0046)
0000 0000 0000 000. = Reserved: 0x0000
.... .... .... ...0 = Continuation Flag: False
Result: Success (0x0000)
04-24 15:56:22.599604 6130 6190 I bt_l2c_main: packages/modules/Bluetooth/system/stack/l2cap/l2c_main.cc:310 process_l2cap_cmd: cmd_code: 5, id:14, cmd_len:6
04-24 15:56:22.599636 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:129 l2c_csm_execute: Entry chnl_state=CST_CONFIG [5], event=PEER_CONFIG_RSP [15]
04-24 15:56:22.599646 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:975 l2c_csm_config: LCID: 0x0046 st: CONFIG evt: PEER_CONFIG_RSP
04-24 15:56:22.599658 6130 6190 I l2c_link: packages/modules/Bluetooth/system/stack/l2cap/l2c_link.cc:739 l2c_link_adjust_chnl_allocation: CID:0x0042 FCR Mode:0 Priority:2 TxDataRate:1 RxDataRate:1 Quota:200
04-24 15:56:22.599667 6130 6190 I l2c_link: packages/modules/Bluetooth/system/stack/l2cap/l2c_link.cc:739 l2c_link_adjust_chnl_allocation: CID:0x0046 FCR Mode:0 Priority:2 TxDataRate:1 RxDataRate:1 Quota:200
04-24 15:56:22.599681 6130 6190 I l2c_csm : packages/modules/Bluetooth/system/stack/l2cap/l2c_csm.cc:1088 l2c_csm_config: Calling Config_Rsp_Cb(), CID: 0x0046
- 在 2025-04-24 15:56:22.599401 收到了 手机 对车机的配置应答。 我们来看一下是如何响应处理的
- process_l2cap_cmd: cmd_code: 5
#define L2CAP_CMD_CONFIG_RSP 0x05
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
case L2CAP_CMD_CONFIG_RSP: {
/*
计算当前命令数据的结束地址,用于限制 while (p < p_cfg_end) 的解析范围。
p 是当前命令起始指针,cmd_len 是该命令的数据长度。
*/
uint8_t* p_cfg_end = p + cmd_len;
uint16_t lcid; // 本地通道 ID,用于查找连接控制块(CCB)。
// 确保当前命令数据至少有 6 字节可读:LCID (2) + Flags (2) + Result (2)。
if (p + 6 > p_next_cmd) {
// 如果不满足,表示数据不完整,记录警告日志并返回。
LOG_WARN("Not enough data for L2CAP_CMD_CONFIG_RSP");
return;
}
/*
从数据流中按顺序解析:
lcid:本地通道 ID
cfg_info.flags:配置标志(是否继续配置等)
cfg_info.result:配置结果,例如 L2CAP_CFG_OK
*/
STREAM_TO_UINT16(lcid, p);
STREAM_TO_UINT16(cfg_info.flags, p);
STREAM_TO_UINT16(cfg_info.result, p);
/*
清空所有配置字段的标志位,准备重新填充配置项。
cfg_info 是一个结构体,保存了本次解析得到的配置信息。
*/
cfg_info.flush_to_present = cfg_info.mtu_present =
cfg_info.qos_present = cfg_info.fcr_present = cfg_info.fcs_present =
false;
// 开始循环解析各个配置选项:
while (p < p_cfg_end) {
// 遍历整个配置项字段,直到到达当前命令末尾。
uint8_t cfg_code, cfg_len;
if (p + 2 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CMD_CONFIG_RSP sub_event");
return;
}
// 获取当前配置项的类型(如 MTU、FCR 等)和长度。
STREAM_TO_UINT8(cfg_code, p);
STREAM_TO_UINT8(cfg_len, p);
switch (cfg_code & 0x7F) { // cfg_code 高位第 7 位用于“是否是可选项”,低 7 位才是实际类型。
case L2CAP_CFG_TYPE_MTU: // MTU 大小
cfg_info.mtu_present = true; // 标记为已解析。
if (p + 2 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_MTU");
return;
}
STREAM_TO_UINT16(cfg_info.mtu, p); // 读取 2 字节 MTU 数值
break;
case L2CAP_CFG_TYPE_FLUSH_TOUT: // Flush Timeout
cfg_info.flush_to_present = true;
if (p + 2 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_FLUSH_TOUT");
return;
}
STREAM_TO_UINT16(cfg_info.flush_to, p);
break;
case L2CAP_CFG_TYPE_QOS:// 服务质量配置
cfg_info.qos_present = true;
if (p + 2 + 5 * 4 > p_next_cmd) { // 一共 22 字节:1 + 1 + 5 * 4
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_QOS");
return;
}
// 包括 token rate、带宽、延迟等。
STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
STREAM_TO_UINT8(cfg_info.qos.service_type, p);
STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
STREAM_TO_UINT32(cfg_info.qos.token_bucket_size, p);
STREAM_TO_UINT32(cfg_info.qos.peak_bandwidth, p);
STREAM_TO_UINT32(cfg_info.qos.latency, p);
STREAM_TO_UINT32(cfg_info.qos.delay_variation, p);
break;
case L2CAP_CFG_TYPE_FCR: // 流控/重传配置
cfg_info.fcr_present = true;
if (p + 3 + 3 * 2 > p_next_cmd) { // 一共 9 字节:3 + 3 * 2
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_FCR");
return;
}
// 包括模式、窗口大小、超时值等。
STREAM_TO_UINT8(cfg_info.fcr.mode, p);
STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
STREAM_TO_UINT16(cfg_info.fcr.rtrans_tout, p);
STREAM_TO_UINT16(cfg_info.fcr.mon_tout, p);
STREAM_TO_UINT16(cfg_info.fcr.mps, p);
break;
case L2CAP_CFG_TYPE_FCS: // 帧校验
cfg_info.fcs_present = true;
if (p + 1 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_FCS");
return;
}
// 读取 1 字节的 fcs 校验类型。
STREAM_TO_UINT8(cfg_info.fcs, p);
break;
case L2CAP_CFG_TYPE_EXT_FLOW:// 扩展流控制
cfg_info.ext_flow_spec_present = true;
if (p + 2 + 2 + 3 * 4 > p_next_cmd) {
LOG_WARN("Not enough data for L2CAP_CFG_TYPE_EXT_FLOW");
return;
}
// 读取 ID、服务类型、SDU 尺寸、延迟等。
STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.sdu_inter_time, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.access_latency, p);
STREAM_TO_UINT32(cfg_info.ext_flow_spec.flush_timeout, p);
break;
}
}
// 根据 lcid 查找对应的连接控制块(CCB)。
tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
if (p_ccb) {
if (p_ccb->local_id != id) {
// 检查这条响应命令的标识符 id 是否与原请求一致,不一致可能是延迟或错误响应。
LOG_WARN("cfg rsp - bad ID. Exp: %d Got: %d", p_ccb->local_id, id);
break;
}
// 成功响应与否:
if (cfg_info.result == L2CAP_CFG_OK) { // 表示配置接受
// 使用状态机将配置结果作为事件投递到对应通道的状态机中处理。
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info);
} else {
// 否则认为配置失败,走 L2CEVT_L2CAP_CONFIG_RSP_NEG
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info);
}
} else {
// 找不到则丢弃
// 输出日志说明:收到一个对未知通道的配置响应,可能是协议异常或状态不同步。
LOG_WARN("Rcvd cfg rsp for unknown CID: 0x%04x", lcid);
}
break;
}
}
- l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info);
- 最终向 状态机 触发了 L2CEVT_L2CAP_CONFIG_RSP 消息
- L2CEVT_L2CAP_CONFIG_RSP = 15, /* response */
当前状态机还是 处于 CST_CONFIG
// 此时状态还是 CST_CONFIG, 所以 l2c_csm_execute 继续调用 l2c_csm_config 函数
// 下面是 l2c_csm_config 函数中对于 L2CEVT_L2CAP_CONFIG_RSP 的处理
L2CEVT_L2CAP_CONFIG_RSP = 15, /* response */
case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */
// 对端发来了配置响应
l2cu_process_peer_cfg_rsp(p_ccb, p_cfg); // 处理对端的配置响应,把对端配置更新到本地控制块 p_ccb->peer_cfg
/* TBD: When config options grow beyong minimum MTU (48 bytes)
* logic needs to be added to handle responses with
* continuation bit set in flags field.
* 1. Send additional config request out until C-bit is cleared in
* response
*/
p_ccb->config_done |= OB_CFG_DONE; // 标记自己发出去的配置已经完成了,OB_CFG_DONE(OutBound Config Done)。
if (p_ccb->config_done & IB_CFG_DONE) { // 如果对端的配置也完成了(InBound Config Done),说明双方配置都搞定了,可以考虑继续后续操作。
/* Verify two sides are in compatible modes before continuing */
if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
// 检查自己和对端的FCR模式(Flow Control and Retransmission mode,比如 Basic/ERTM/Streaming)是否一致。 如果不一致,无法通信,需要断开
l2cu_send_peer_disc_req(p_ccb); // 发送 Disconnect Request 给对方。
LOG_WARN(
"Calling Disconnect_Ind_Cb(Incompatible CFG), CID: "
"0x%04x No Conf Needed",
p_ccb->local_cid);
l2cu_release_ccb(p_ccb); // 释放 CCB 资源,通知上层断开连接(false 表示无须上层回复)。
(*disconnect_ind)(local_cid, false);
break;
}
// 如果配置兼容:
p_ccb->config_done |= RECONFIG_FLAG; // 标记为已经重新配置(即配置完成后还可以后续改配)。
p_ccb->chnl_state = CST_OPEN; // 把信道状态切换为 CST_OPEN,进入数据传输阶段。
l2c_link_adjust_chnl_allocation(); // 调整物理链路资源分配。
alarm_cancel(p_ccb->l2c_ccb_timer); // 取消配置超时定时器
/* If using eRTM and waiting for an ACK, restart the ACK timer */
if (p_ccb->fcrb.wait_ack) l2c_fcr_start_timer(p_ccb); // 如果在用增强模式(eRTM)且在等ACK包,则重启ACK定时器
/*
** check p_ccb->our_cfg.fcr.mon_tout and
*p_ccb->our_cfg.fcr.rtrans_tout
** we may set them to zero when sending config request during
*renegotiation
*/
if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) &&
((p_ccb->our_cfg.fcr.mon_tout == 0) ||
(p_ccb->our_cfg.fcr.rtrans_tout))) {
// 如果使用 eRTM 且 monitor timeout / retransmission timeout 不合理(=0),调整一下参数。
l2c_fcr_adj_monitor_retran_timeout(p_ccb);
}
/* See if we can forward anything on the hold queue */
if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
// 检查有没有挂起的待发数据,有的话现在可以发了!
l2c_link_check_send_pkts(p_ccb->p_lcb, 0, NULL);
}
}
LOG_INFO("Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid); // 打日志,表明调用了配置完成回调
p_ccb->remote_config_rsp_result = p_cfg->result; // 记录对端回复的配置结果(比如成功/失败码)。
if (p_ccb->config_done & IB_CFG_DONE) {
l2c_csm_indicate_connection_open(p_ccb); // 如果对端配置也完成,通知上层连接正式建立
}
break;
- 手机发起的配置请求, 我们已经响应了, 所以 p_ccb->config_done & IB_CFG_DONE 条件成立
- p_ccb->chnl_state = CST_OPEN;
- 此时会调用 l2c_csm_indicate_connection_open(p_ccb)
7. l2c_csm_indicate_connection_open
我们专门看一下这个函数的实现
// system/stack/l2cap/l2c_csm.cc
static void l2c_csm_indicate_connection_open(tL2C_CCB* p_ccb) {
if (p_ccb->connection_initiator == L2CAP_INITIATOR_LOCAL) { // 如果是发起方,就会回调如下
(*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(p_ccb->local_cid, L2CAP_CONN_OK);
} else {
// 在这个场景中 车机并不是发起者,所以会调用如下
(*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb)(
p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid, p_ccb->p_rcb->psm,
p_ccb->remote_id);
}
if (p_ccb->chnl_state == CST_OPEN && !p_ccb->p_lcb->is_transport_ble()) {
(*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(
p_ccb->local_cid, p_ccb->connection_initiator, &p_ccb->peer_cfg);
}
}
// system/stack/include/l2c_api.h
typedef struct {
tL2CA_CONNECT_IND_CB* pL2CA_ConnectInd_Cb;
tL2CA_CONNECT_CFM_CB* pL2CA_ConnectCfm_Cb;
tL2CA_CONFIG_IND_CB* pL2CA_ConfigInd_Cb;
tL2CA_CONFIG_CFM_CB* pL2CA_ConfigCfm_Cb;
tL2CA_DISCONNECT_IND_CB* pL2CA_DisconnectInd_Cb;
tL2CA_DISCONNECT_CFM_CB* pL2CA_DisconnectCfm_Cb;
tL2CA_DATA_IND_CB* pL2CA_DataInd_Cb;
tL2CA_CONGESTION_STATUS_CB* pL2CA_CongestionStatus_Cb;
tL2CA_TX_COMPLETE_CB* pL2CA_TxComplete_Cb;
tL2CA_ERROR_CB* pL2CA_Error_Cb;
tL2CA_CREDIT_BASED_CONNECT_IND_CB* pL2CA_CreditBasedConnectInd_Cb;
tL2CA_CREDIT_BASED_CONNECT_CFM_CB* pL2CA_CreditBasedConnectCfm_Cb;
tL2CA_CREDIT_BASED_RECONFIG_COMPLETED_CB*
pL2CA_CreditBasedReconfigCompleted_Cb;
tL2CA_CREDIT_BASED_COLLISION_IND_CB* pL2CA_CreditBasedCollisionInd_Cb;
} tL2CAP_APPL_INFO;
// system/stack/avdt/avdt_l2c.cc
const tL2CAP_APPL_INFO avdt_l2c_appl = {avdt_l2c_connect_ind_cback,
avdt_l2c_connect_cfm_cback,
avdt_l2c_config_ind_cback,
avdt_l2c_config_cfm_cback,
avdt_l2c_disconnect_ind_cback,
NULL,
avdt_l2c_data_ind_cback,
avdt_l2c_congestion_ind_cback,
NULL,
avdt_on_l2cap_error,
NULL,
NULL,
NULL,
NULL};
-
这个函数会 触发 avdtp 对应的 pL2CA_ConnectInd_Cb 和 pL2CA_ConfigCfm_Cb 调用
-
所以此时会触发 avdt_l2c_connect_ind_cback 和 avdt_l2c_config_cfm_cback
04-24 15:56:22.599692 6130 6190 I bt_avp : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: trace_avdt avdt_l2c_connect_ind_cback 143 ->
04-24 15:56:22.599759 6130 6190 I bt_avp : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: avdt_ccb_alloc_by_channel_index: allocated (index 0) peer=70:8f:47:91:b0:62 p_ccb=0x7a407afc28
04-24 15:56:22.599782 6130 6190 I bt_avp : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: avdt_ad_tc_tbl_to_idx: 0
04-24 15:56:22.599793 6130 6190 I bt_avp : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: trace_avdt avdt_l2c_config_cfm_cback 306 ->
04-24 15:56:22.599801 6130 6190 I bt_avp : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: trace_avdt avdt_l2c_config_ind_cback 339 ->
- 至于 avdtp 如何初始化, 不再本节的分析之内, 会在 avdtp 章节中,专门分享。