本篇我们通过分析一个具体的实例,来直观感受一下 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 ProtocolLength: 8CID: L2CAP Signaling Channel (0x0001)Command: Connection RequestCommand Code: Connection Request (0x02)Command Identifier: 0x11Command Length: 4PSM: 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:404-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 0x000004-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: 204-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:20004-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_REQ04-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=0x001904-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:false04-24 15:56:22.504822 6130 6190 I bt_btm : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: btm_find_or_alloc_dev04-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:104-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: x404-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: 0x40be04-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 granted04-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:AVDTPSTREAM_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 客户端数), 目前是 15for (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 的 CCBtL2C_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_lastl2cb.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; // 绑定 LCBp_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 activep_lcb->with_active_local_clients = true;}// 最后返回新分配的 CCBreturn 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.ccvoid 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 准备保存"断开连接回调函数"指针// 检查是否注册了对应的 RCBif (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_ccbtL2CAP_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 connectionconstexpr tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;// 打日志,记录当前是不是发起方 (is_originator),以及用的是什么 psmLOG_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 PSMtBTM_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 connectionif (!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.cctBTM_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 ofsequencebecause 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_SUCCESSif (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.cctBTM_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_AUTHENTICATEif (!(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 ProtocolLength: 12CID: L2CAP Signaling Channel (0x0001)Command: Connection ResponseCommand Code: Connection Response (0x03)Command Identifier: 0x11Command Length: 8Destination 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 granted04-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_COMPLETE04-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 response04-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 Responsel2c_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 的拒绝连接(错误码:认证失败)。elsel2cu_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_RSP04-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 Confirmbreak;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_OPENl2cble_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);elsel2cu_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_RSP04-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 ProtocolLength: 12CID: L2CAP Signaling Channel (0x0001)Command: Connection ResponseCommand Code: Connection Response (0x03)Command Identifier: 0x11Command Length: 8Destination 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 ProtocolLength: 12CID: L2CAP Signaling Channel (0x0001)Command: Configure RequestCommand Code: Configure Request (0x04)Command Identifier: 0x0eCommand Length: 8Destination CID: Dynamically Allocated Channel (0x0041)0000 0000 0000 000. = Reserved: 0x0000.... .... .... ...0 = Continuation Flag: FalseOption: 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_REQ04-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 Requestalarm_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);elseosi_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 ProtocolLength: 12CID: L2CAP Signaling Channel (0x0001)Command: Configure RequestCommand Code: Configure Request (0x04)Command Identifier: 0x0eCommand Length: 8Destination CID: Dynamically Allocated Channel (0x0041)0000 0000 0000 000. = Reserved: 0x0000.... .... .... ...0 = Continuation Flag: FalseOption: MTU04-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_REQ04-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 ProtocolLength: 12CID: L2CAP Signaling Channel (0x0001)Command: Configure RequestCommand Code: Configure Request (0x04)Command Identifier: 0x12Command Length: 8Destination CID: Dynamically Allocated Channel (0x0046)0000 0000 0000 000. = Reserved: 0x0000.... .... .... ...0 = Continuation Flag: FalseOption: MTU04-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:804-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_REQ04-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: 004-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: 101104-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 ProtocolLength: 10CID: L2CAP Signaling Channel (0x0001)Command: Configure ResponseCommand Code: Configure Response (0x05)Command Identifier: 0x12Command Length: 6Source CID: Dynamically Allocated Channel (0x0041)0000 0000 0000 000. = Reserved: 0x0000.... .... .... ...0 = Continuation Flag: FalseResult: 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 ProtocolLength: 10CID: L2CAP Signaling Channel (0x0001)Command: Configure ResponseCommand Code: Configure Response (0x05)Command Identifier: 0x0eCommand Length: 6Source CID: Dynamically Allocated Channel (0x0046)0000 0000 0000 000. = Reserved: 0x0000.... .... .... ...0 = Continuation Flag: FalseResult: 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:604-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_RSP04-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:20004-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:20004-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 0x05static 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:本地通道 IDcfg_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 Timeoutcfg_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 * 4LOG_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 * 2LOG_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_NEGl2c_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=0x7a407afc2804-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: 004-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 章节中,专门分享。