博主简介
byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发,深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域。乐于技术分享与交流,欢迎关注互动!
📌 主页与联系方式
-
CSDN:https://blog.csdn.net/weixin_37800531
-
知乎:https://www.zhihu.com/people/38-72-36-20-51
-
微信公众号:嵌入式硬核研究所
-
邮箱:byteqqb@163.com(技术咨询或合作请备注需求)
⚠️ 版权声明
本文为原创内容,未经授权禁止转载。商业合作或内容授权请联系邮箱并备注来意。
本文基于 Android AOSP 蓝牙源码,深入剖析 GATT 客户端两种核心特征值读取方式(按句柄 GATT_READ_BY_HANDLE、按 UUID GATT_READ_BY_TYPE)的全链路执行流程,涵盖从 BTIF 层 API 调用、BTA 层消息分发与状态机处理、Stack 层安全校验,到 JNI 层回调的完整逻辑,通过时序图梳理关键函数调用关系,为蓝牙开发与问题定位提供核心参考。
一、概述
Android 蓝牙 GATT(Generic Attribute Profile)是 BLE 设备通信的核心模块,本文聚焦 AOSP 源码中的 btif、bta、stack 三层代码,重点分析 GATT 客户端特征值读取的核心流程:
-
上层通过btif_gattc_read_char(按句柄)/btif_gattc_read_using_char_uuid(按 UUID)发起读取请求;
-
请求经 JNI 线程调度后封装为BTA_GATTC_API_READ_EVT消息,通过bta_sys_sendmsg分发到主线程;
-
BTA 层通过状态机(bta_gattc_sm_execute)执行bta_gattc_read动作,区分两种读取方式调用GATTC_Read完成底层协议交互;
-
流程中包含请求队列管理(bta_gattc_enqueue)、安全检查(gatt_security_check_start)、操作完成回调(bta_gattc_read_cmpl)等关键环节;
-
最终通过read_char_cb回调到 JNI 层btgattc_read_characteristic_cb,完成对 Java 层的结果反馈。
二、源码剖析
btif_gattc_read_char
// packages/modules/Bluetooth/system/btif/src/btif_gatt_client.cc
static bt_status_t btif_gattc_read_char(int conn_id, uint16_t handle, int auth_req) {
CHECK_BTGATT_INIT();
// 1:线程切换 – 从任意调用线程转到JNI线程执行BTA层调用
// :回调传递 – read_char_cb作为读取结果回调传入BTA层
return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharacteristic, static_cast<tCONN_ID>(conn_id),
handle, auth_req, read_char_cb, nullptr));
}
// packages/modules/Bluetooth/system/bta/gatt/bta_gattc_api.cc
void BTA_GATTC_ReadCharacteristic(tCONN_ID conn_id, uint16_t handle, tGATT_AUTH_REQ auth_req,
GATT_READ_OP_CB callback, void* cb_data) {
// 3:消息封装 – 将读取请求封装为BTA_GATTC_API_READ_EVT事件
tBTA_GATTC_API_READ* p_buf = (tBTA_GATTC_API_READ*)osi_calloc(sizeof(tBTA_GATTC_API_READ));
p_buf->hdr.event = BTA_GATTC_API_READ_EVT; // 事件类型标识
p_buf->hdr.layer_specific = static_cast<uint16_t>(conn_id); // 连接ID存储位置
p_buf->is_multi_read = false; // 单特征读取标记
p_buf->auth_req = auth_req;
p_buf->handle = handle;
p_buf->read_cb = callback; // 4:回调函数传递到消息结构
p_buf->read_cb_data = cb_data;
// 5:异步消息发送 – 通过bta_sys_sendmsg投递到BTA主线程处理
bta_sys_sendmsg(p_buf);
}
Android蓝牙GATT客户端读取特征值的异步调用链。上层请求通过do_in_jni_thread切换到核心线程,然后封装为BTA_GATTC_API_READ_EVT消息发送到BTA事件队列,实现线程安全的异步处理。
btif_gattc_read_using_char_uuid
// packages/modules/Bluetooth/system/btif/src/btif_gatt_client.cc
static bt_status_t btif_gattc_read_using_char_uuid(int conn_id, const Uuid& uuid, uint16_t s_handle,
uint16_t e_handle, int auth_req) {
CHECK_BTGATT_INIT();
// 通过UUID读取 – 与通过句柄读取的API形成对比,这里使用UUID+句柄范围作为参数
return do_in_jni_thread(Bind(&BTA_GATTC_ReadUsingCharUuid, static_cast<tCONN_ID>(conn_id), uuid,
s_handle, e_handle, auth_req, read_using_char_uuid_cb, nullptr));
}
// packages/modules/Bluetooth/system/bta/gatt/bta_gattc_api.cc
/**
* This function is called to read a value of characteristic with uuid equal to
* |uuid|
*/
void BTA_GATTC_ReadUsingCharUuid(tCONN_ID conn_id, const Uuid& uuid, uint16_t s_handle,
uint16_t e_handle, tGATT_AUTH_REQ auth_req,
GATT_READ_OP_CB callback, void* cb_data) {
tBTA_GATTC_API_READ* p_buf = (tBTA_GATTC_API_READ*)osi_calloc(sizeof(tBTA_GATTC_API_READ));
p_buf->hdr.event = BTA_GATTC_API_READ_EVT; // 重用相同事件类型
p_buf->hdr.layer_specific = static_cast<uint16_t>(conn_id);
p_buf->is_multi_read = false;
p_buf->auth_req = auth_req;
p_buf->handle = 0; // 句柄设为0,表示使用UUID读取而非句柄读取
p_buf->uuid = uuid; // UUID参数被存入消息结构
p_buf->s_handle = s_handle; // 起始句柄范围
p_buf->e_handle = e_handle; // 结束句柄范围
p_buf->read_cb = callback; // 不同的回调函数 – read_using_char_uuid_cb
p_buf->read_cb_data = cb_data;
bta_sys_sendmsg(p_buf);
}
通过UUID读取蓝牙GATT特征值的功能。与直接通过句柄读取不同,此API允许在指定的句柄范围内(s_handle到e_handle)搜索匹配指定UUID的特征并进行读取。BTIF层通过相同的线程切换机制调用BTA层API,BTA层将请求封装为相同的BTA_GATTC_API_READ_EVT事件但填充了不同的参数(UUID和句柄范围)。
关键区别:
UUID搜索:使用uuid+s_handle+e_handle代替单一handle
相同事件类型:仍使用BTA_GATTC_API_READ_EVT,但通过handle=0标识UUID读取模式
范围限定:s_handle和e_handle限定搜索范围,提高效率
bta_sys_sendmsg(BTA_GATTC_API_READ_EVT)
packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/*******************************************************************************
*
* Function bta_sys_sendmsg
*
* Description Send a GKI message to BTA. This function is designed to
* optimize sending of messages to BTA. It is called by BTA
* API functions and call-in functions.
*
* TODO (apanicke): Add location object as parameter for easier
* future debugging when doing alarm refactor
*
*
* Returns void
*
******************************************************************************/
void bta_sys_sendmsg(void* p_msg) {
// 将消息通过main_thread主线程处理,确保线程安全
if (do_in_main_thread(base::BindOnce(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
BT_STATUS_SUCCESS) {
log::error("do_in_main_thread failed");
}
// 通过base::BindOnce绑定消息到主线程执行bta_sys_event
}
/*******************************************************************************
*
* Function bta_sys_event
*
* Description BTA event handler; called from task event handler.
*
*
* Returns void
*
******************************************************************************/
static void bta_sys_event(BT_HDR_RIGID* p_msg) {
bool freebuf = true; // 默认释放消息缓冲区
log::verbose("Event 0x{:x}", p_msg->event);
/* get subsystem id from event */
uint8_t id = (uint8_t)(p_msg->event >> 8); // 从事件号提取子系统ID
// 事件号高8位是子系统ID,低8位是具体事件
/* verify id and call subsystem event handler */
if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) {
// 子系统ID有效且已注册,调用对应的处理器
freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
} else {
log::info("Ignoring receipt of unregistered event id:{}[{}]",
BtaIdSysText(static_cast<tBTA_SYS_ID>(id)), id);
}
if (freebuf) {
osi_free(p_msg); // 释放消息内存
}
}
Android蓝牙BTA系统的核心消息分发机制。bta_sys_sendmsg负责将消息提交到主线程队列,bta_sys_event作为事件分发中心,根据消息中的子系统ID路由到对应的处理模块。
bta_gattc_hdl_event
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_main.cc
/*******************************************************************************
*
* Function bta_gattc_hdl_event
*
* Description GATT client main event handling function.
*
*
* Returns bool
*
******************************************************************************/
bool bta_gattc_hdl_event(const BT_HDR_RIGID* p_msg) {
tBTA_GATTC_CLCB* p_clcb = NULL; // 连接控制块指针
bool rt = true; // 返回值,true表示释放消息缓冲区
#if (BTA_GATT_DEBUG == TRUE)
log::verbose("Event:{}", gattc_evt_code(p_msg->event));
#endif
switch (p_msg->event) {
// 直接处理连接相关API事件
case BTA_GATTC_API_OPEN_EVT:
bta_gattc_process_api_open((tBTA_GATTC_DATA*)p_msg);
break;
case BTA_GATTC_API_CANCEL_OPEN_EVT:
bta_gattc_process_api_open_cancel((tBTA_GATTC_DATA*)p_msg);
break;
default: // 其他事件通过连接控制块的状态机处理
// 根据事件类型查找对应的连接控制块
if (p_msg->event == BTA_GATTC_INT_CONN_EVT) {
p_clcb = bta_gattc_find_int_conn_clcb((tBTA_GATTC_DATA*)p_msg);
} else if (p_msg->event == BTA_GATTC_INT_DISCONN_EVT) {
p_clcb = bta_gattc_find_int_disconn_clcb((tBTA_GATTC_DATA*)p_msg);
} else {
// 从layer_specific获取conn_id查找控制块
p_clcb = bta_gattc_find_clcb_by_conn_id(static_cast<tCONN_ID>(p_msg->layer_specific));
}
if (p_clcb != nullptr) {
// 执行状态机处理具体事件
rt = bta_gattc_sm_execute(p_clcb, p_msg->event, (const tBTA_GATTC_DATA*)p_msg);
} else {
log::error("Ignore unknown conn ID: {}", p_msg->layer_specific);
}
break;
}
return rt; // 返回是否释放消息缓冲区
}
GATT客户端的核心事件分发器,根据事件类型路由到不同的处理逻辑。连接相关API事件直接处理,其他事件(包括BTA_GATTC_API_READ_EVT)通过查找对应连接控制块,由状态机bta_gattc_sm_execute处理。
bta_gattc_sm_execute
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_main.cc
/*******************************************************************************
*
* Function bta_gattc_sm_execute
*
* Description State machine event handling function for GATTC
*
*
* Returns bool : true if queued client request buffer can be
* immediately released, else false
*
******************************************************************************/
bool bta_gattc_sm_execute(tBTA_GATTC_CLCB* p_clcb, uint16_t event, const tBTA_GATTC_DATA* p_data) {
tBTA_GATTC_ST_TBL state_table;
uint8_t action;
int i;
bool rt = true; // 默认释放消息缓冲区
tBTA_GATTC_STATE in_state = p_clcb->state; // 保存进入状态
uint16_t in_event = event; // 保存原始事件
#if (BTA_GATT_DEBUG == TRUE)
log::verbose("State 0x{:02x} [{}], Event 0x{:x}[{}]", in_state, gattc_state_code(in_state),
in_event, gattc_evt_code(in_event));
#else
log::verbose("State 0x{:02x}, Event 0x{:x}", in_state, in_event);
#endif
/* look up the state table for the current state */
state_table = bta_gattc_st_tbl[p_clcb->state]; // 根据当前状态获取状态表
event &= 0x00FF; // 移除高8位子系统ID,只保留低8位事件号
/* set next state */
p_clcb->state = (tBTA_GATTC_STATE)(state_table[event][BTA_GATTC_NEXT_STATE]); // 状态转移
/* execute action functions */
for (i = 0; i < BTA_GATTC_ACTIONS; i++) { // 遍历动作列表
action = state_table[event][i];
if (action != BTA_GATTC_IGNORE) { // 有动作需要执行
(*bta_gattc_action[action])(p_clcb, p_data); // 执行具体动作函数
if (bta_gattc_is_data_queued(p_clcb, p_data)) {
/* buffer is queued, don't free in the bta dispatcher.
* we free it ourselves when a completion event is received.
*/
rt = false; // 如果消息被排队,不立即释放缓冲区
}
} else {
break; // 遇到IGNORE表示后续无动作
}
}
#if (BTA_GATT_DEBUG == TRUE)
if (in_state != p_clcb->state) {
log::verbose("GATTC State Change: [{}] -> [{}] after Event [{}]", gattc_state_code(in_state),
gattc_state_code(p_clcb->state), gattc_evt_code(in_event));
}
#else
log::verbose("GATTC State Change: 0x{:02x} -> 0x{:02x} after Event 0x{:x}", in_state,
p_clcb->state, in_event);
#endif
return rt; // 返回是否释放消息缓冲区
}
/* state table */
const tBTA_GATTC_ST_TBL bta_gattc_st_tbl[] = {
bta_gattc_st_idle, /* BTA_GATTC_IDLE_ST */ // 空闲状态
bta_gattc_st_w4_conn, /* BTA_GATTC_W4_CONN_ST */ // 等待连接状态
bta_gattc_st_connected, /* BTA_GATTC_CONN_ST */ // 已连接状态
bta_gattc_st_discover /* BTA_GATTC_DISCOVER_ST */ // 发现服务状态
};
GATT客户端状态机的核心执行引擎,实现了基于状态表的状态驱动架构。根据当前状态和事件查找状态表,执行对应的动作函数并进行状态转移。当BTA_GATTC_API_READ_EVT发生时,在BTA_GATTC_CONN_ST状态表中查找对应的处理动作,执行读取操作,状态可能转移到等待响应状态。

当GATT客户端处于已连接状态时,收到BTA_GATTC_API_READ_EVT事件,状态机将:
执行bta_gattc_read()动作函数
保持BTA_GATTC_CONN_ST状态,等待操作完成
完成后通过BTA_GATTC_OP_CMPL_EVT事件返回结果
这种设计实现了非阻塞异步操作,允许在保持连接的同时处理多个GATT操作请求。
bta_gattc_read
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
/** Read an attribute */
void bta_gattc_read(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
// 尝试将读取请求入队,如果队列已满则延迟处理
if (bta_gattc_enqueue(p_clcb, p_data) == ENQUEUED_FOR_LATER) {
return; // 请求已排队,等待后续处理
}
tGATT_STATUS status;
// 判断读取方式:按句柄读取 或 按UUID读取
if (p_data->api_read.handle != 0) {
// 按句柄读取(直接读取)
tGATT_READ_PARAM read_param;
memset(&read_param, 0, sizeof(tGATT_READ_PARAM));
read_param.by_handle.handle = p_data->api_read.handle; // 特征句柄
read_param.by_handle.auth_req = p_data->api_read.auth_req; // 认证要求
status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_HANDLE, &read_param);
} else {
// 按UUID读取(在指定范围内搜索特征)
tGATT_READ_PARAM read_param;
memset(&read_param, 0, sizeof(tGATT_READ_BY_TYPE));
read_param.char_type.s_handle = p_data->api_read.s_handle; // 起始句柄
read_param.char_type.e_handle = p_data->api_read.e_handle; // 结束句柄
read_param.char_type.uuid = p_data->api_read.uuid; // 特征UUID
read_param.char_type.auth_req = p_data->api_read.auth_req; // 认证要求
status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_TYPE, &read_param);
}
/* read fail */
if (status != GATT_SUCCESS) {
/* Dequeue the data, if it was enqueued */
if (p_clcb->p_q_cmd == p_data) {
p_clcb->p_q_cmd = NULL; // 从队列中移除失败的命令
}
// 发送读取失败完成消息
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL);
bta_gattc_continue(p_clcb); // 继续处理队列中的下一个请求
}
// 读取成功时,后续通过BTA_GATTC_OP_CMPL_EVT事件处理
}
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_utils.cc
/*******************************************************************************
*
* Function bta_gattc_enqueue
*
* Description enqueue a client request in clcb.
*
* Returns BtaEnqueuedResult_t
*
******************************************************************************/
BtaEnqueuedResult_t bta_gattc_enqueue(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
// 检查当前是否有正在执行的命令
if (p_clcb->p_q_cmd == NULL) {
p_clcb->p_q_cmd = p_data; // 设置当前执行命令
return ENQUEUED_READY_TO_SEND; // 可以立即发送
}
// 已有命令在执行,将新请求加入等待队列
log::info("Already has a pending command to executer. Queuing for later {} conn id=0x{:04x}",
p_clcb->bda, p_clcb->bta_conn_id);
p_clcb->p_q_cmd_queue.push_back(p_data); // 添加到队列尾部
return ENQUEUED_FOR_LATER; // 请求被排队等待
}
GATT客户端读取操作的执行逻辑和请求队列管理。bta_gattc_read函数根据请求类型(按句柄或按UUID)发起读取操作,bta_gattc_enqueue函数负责管理请求队列,确保同一连接上GATT操作的有序执行。
1. 双模式读取:
-
按句柄读取:直接通过handle读取特定特征值
-
按UUID读取:在s_handle到e_handle范围内搜索指定uuid的特征
2. 请求队列管理:
-
p_clcb->p_q_cmd:当前正在执行的命令
-
p_clcb->p_q_cmd_queue:等待执行的命令队列
-
同一时间只处理一个命令,其他请求排队等待
3. 错误处理:读取失败时立即清理队列并通知上层,同时继续处理后续请求
对应状态表中BTA_GATTC_READ动作,当BTA_GATTC_API_READ_EVT事件发生时被调用,执行后保持BTA_GATTC_CONN_ST状态。
GATTC_Read
packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc
/*******************************************************************************
*
* Function GATTC_Read
*
* Description This function is called to read the value of an attribute
* from the server.
*
* Parameters conn_id: connection identifier.
* type – attribute read type.
* p_read – read operation parameters.
*
* Returns GATT_SUCCESS if command started successfully.
*
******************************************************************************/
tGATT_STATUS GATTC_Read(tCONN_ID conn_id, tGATT_READ_TYPE type, tGATT_READ_PARAM* p_read) {
tGATT_IF gatt_if = gatt_get_gatt_if(conn_id);
uint8_t tcb_idx = gatt_get_tcb_idx(conn_id);
tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx); // 获取连接控制块
tGATT_REG* p_reg = gatt_get_regcb(gatt_if); // 获取GATT注册信息
#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
static uint16_t cached_read_handle;
static int cached_tcb_idx = -1;
#endif
log::verbose("conn_id=0x{:x}, type=0x{:x}", conn_id, type);
// 参数验证
if ((p_tcb == NULL) || (p_reg == NULL) || (p_read == NULL) ||
((type >= GATT_READ_MAX) || (type == 0))) {
log::error("illegal param: conn_id=0x{:x}, type=0x{:x}", conn_id, type);
return GATT_ILLEGAL_PARAMETER;
}
// 分配命令控制块,用于跟踪读取操作
tGATT_CLCB* p_clcb = gatt_clcb_alloc(conn_id);
if (!p_clcb) {
return GATT_NO_RESOURCES;
}
p_clcb->operation = GATTC_OPTYPE_READ; // 操作类型:读取
p_clcb->op_subtype = type; // 读取子类型
p_clcb->auth_req = p_read->by_handle.auth_req; // 认证要求
p_clcb->counter = 0; // 用于分片读取的偏移量
p_clcb->read_req_current_mtu = gatt_tcb_get_payload_size(*p_tcb, p_clcb->cid); // 当前MTU
switch (type) {
case GATT_READ_BY_TYPE: // 按UUID读取
case GATT_READ_CHAR_VALUE: // 读取特征值
// 设置读取范围:起始句柄、结束句柄、UUID
p_clcb->s_handle = p_read->service.s_handle;
p_clcb->e_handle = p_read->service.e_handle;
p_clcb->uuid = p_read->service.uuid; // 目标特征UUID
break;
case GATT_READ_BY_HANDLE: // 按句柄读取
#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
// 测试模式特殊处理,正常使用时跳过
FALLTHROUGH_INTENDED;
#endif
case GATT_READ_PARTIAL: // 分片读取
p_clcb->uuid = Uuid::kEmpty; // 按句柄读取不需要UUID
p_clcb->s_handle = p_read->by_handle.handle; // 目标特征句柄
if (type == GATT_READ_PARTIAL) {
p_clcb->counter = p_read->partial.offset; // 分片读取偏移量
}
break;
// 其他读取类型省略…
}
/* start security check */
// 开始安全校验,如果需要加密则加入等待队列
if (gatt_security_check_start(p_clcb)) {
p_tcb->pending_enc_clcb.push_back(p_clcb);
}
return GATT_SUCCESS; // 命令已启动
}
GATT读取操作的底层实现,负责根据不同的读取类型(GATT_READ_BY_HANDLE或GATT_READ_BY_TYPE)设置相应的参数,并启动安全校验流程。
gatt_security_check_start
packages/modules/Bluetooth/system/stack/gatt/gatt_auth.cc
/** check link security, return true if p_clcb should be added back to queue */
bool gatt_security_check_start(tGATT_CLCB* p_clcb) {
tGATT_TCB* p_tcb = p_clcb->p_tcb; // 获取传输控制块
tGATT_SEC_ACTION sec_act_old = gatt_get_sec_act(p_tcb); // 获取当前安全动作
// 确定此读取操作需要的安全级别
tGATT_SEC_ACTION gatt_sec_act = gatt_determine_sec_act(p_clcb);
// 如果当前没有安全操作,设置新的安全动作
if (sec_act_old == GATT_SEC_NONE) {
gatt_set_sec_act(p_tcb, gatt_sec_act);
}
switch (gatt_sec_act) {
case GATT_SEC_SIGN_DATA: // 需要数据签名
log::verbose("Do data signing");
gatt_sign_data(p_clcb); // 执行数据签名
break;
case GATT_SEC_ENCRYPT: // 需要加密
case GATT_SEC_ENCRYPT_NO_MITM: // 需要加密但不需要MITM
case GATT_SEC_ENCRYPT_MITM: // 需要加密和MITM保护
if (sec_act_old < GATT_SEC_ENCRYPT) { // 当前安全级别不足
log::verbose("Encrypt now or key upgrade first");
tBTM_BLE_SEC_ACT btm_ble_sec_act;
gatt_convert_sec_action(gatt_sec_act, &btm_ble_sec_act);
// 启动加密过程
tBTM_STATUS btm_status = BTM_SetEncryption(p_tcb->peer_bda, p_tcb->transport,
gatt_enc_cmpl_cback, NULL, btm_ble_sec_act);
if ((btm_status != tBTM_STATUS::BTM_SUCCESS) &&
(btm_status != tBTM_STATUS::BTM_CMD_STARTED)) {
log::error("BTM_SetEncryption failed btm_status={}", btm_status);
gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
// 加密失败,立即结束操作
gatt_end_operation(p_clcb, GATT_INSUF_ENCRYPTION, NULL);
return false; // 无需重新排队
}
}
return true; // 需要等待加密完成,将p_clcb加入等待队列
case GATT_SEC_ENC_PENDING: // 加密已在进行中
/* wait for link encryption to finish */
return true; // 等待加密完成
default: // 不需要特殊安全处理(GATT_SEC_NONE等)
gatt_sec_check_complete(true, p_clcb, gatt_sec_act);
break;
}
return false; // 安全检查完成,无需排队
}
GATT读取操作前的安全检查入口,根据特征的安全要求和当前连接的安全状态,决定是否需要执行加密或签名操作。函数返回布尔值指示控制块p_clcb是否需要加入等待队列。
返回true时,控制块会被添加到p_tcb->pending_enc_clcb队列,待加密完成后继续处理读取操作。这是异步安全处理的关键设计。
-
在GATTC_Read中调用此函数
-
如果需要加密,读取操作会暂停,等待加密完成
-
加密完成后,队列中的读取操作会继续执行
关键决策逻辑:
| 安全级别 | 处理方式 | 返回结果 | 说明 |
| GATT_SEC_SIGN_DATA | 立即签名 | FALSE | 数据签名是同步操作 |
| GATT_SEC_ENCRYPT_* | 启动加密 | TRUE | 异步加密,需等待完成 |
| GATT_SEC_ENC_PENDING | 等待加密 | TRUE | 加密已在进行中 |
| 其他(默认) | 完成检查 | FALSE | 无需额外安全处理 |
bta_gattc_cmpl_sendmsg
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
/** client operation complete send message */
void bta_gattc_cmpl_sendmsg(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
tGATT_CL_COMPLETE* p_data) {
// 计算消息大小:基础结构 + 完成数据
const size_t len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE);
tBTA_GATTC_OP_CMPL* p_buf = (tBTA_GATTC_OP_CMPL*)osi_calloc(len); // 分配消息缓冲区
// 设置消息头
p_buf->hdr.event = BTA_GATTC_OP_CMPL_EVT; // 操作完成事件
p_buf->hdr.layer_specific = static_cast<uint16_t>(conn_id); // 保存连接ID
p_buf->status = status; // 操作状态(成功/失败)
p_buf->op_code = op; // 操作类型(读取/写入等)
// 如果有完成数据(如读取值),复制到消息中
if (p_data) {
p_buf->p_cmpl = (tGATT_CL_COMPLETE*)(p_buf + 1); // 指向缓冲区尾部
memcpy(p_buf->p_cmpl, p_data, sizeof(tGATT_CL_COMPLETE)); // 复制数据
}
bta_sys_sendmsg(p_buf); // 发送到主线程
}
GATT客户端操作完成后的统一通知机制,负责将操作结果(成功/失败)和返回数据封装成事件消息,通过bta_sys_sendmsg发送给上层BTA层。

当 GATT 客户端处于已连接状态时,收到 BTA_GATTC_OP_CMPL_EVT事件,状态机将:
执行 bta_gattc_op_cmpl()动作函数,处理操作结果(成功或失败)
保持 BTA_GATTC_CONN_ST状态,以便继续处理其他请求
这种设计实现了非阻塞异步操作,允许在保持连接的同时处理多个 GATT 操作请求,并通过统一的操作完成事件处理所有操作的结果。
bta_gattc_op_cmpl
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
/** operation completed */
void bta_gattc_op_cmpl(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
// 检查当前是否有待处理的命令,没有则记录错误或处理MTU配置失败回调
if (p_clcb->p_q_cmd == NULL) {
if (com::android::bluetooth::flags::gatt_callback_on_failure() &&
p_data->op_cmpl.op_code == GATTC_OPTYPE_CONFIG) {
bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);
return;
}
log::error("No pending command gatt client command");
return;
}
const tGATTC_OPTYPE op = p_data->op_cmpl.op_code; // 获取操作类型
// 验证操作类型是否合法(只处理读取、写入、执行写入和配置)
switch (op) {
case GATTC_OPTYPE_READ:
case GATTC_OPTYPE_WRITE:
case GATTC_OPTYPE_EXE_WRITE:
case GATTC_OPTYPE_CONFIG:
break;
case GATTC_OPTYPE_NONE:
case GATTC_OPTYPE_DISCOVERY:
case GATTC_OPTYPE_NOTIFICATION:
case GATTC_OPTYPE_INDICATION:
default:
log::error("unexpected operation, ignored");
return;
}
// 验证接收到的完成事件与当前待处理命令是否匹配
// 使用映射表bta_gattc_opcode_to_int_evt将操作类型转换为内部事件
if (p_clcb->p_q_cmd->hdr.event != bta_gattc_opcode_to_int_evt[op – GATTC_OPTYPE_READ] &&
(p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_EVT || op != GATTC_OPTYPE_READ)) {
// 事件不匹配,记录错误
uint8_t mapped_op = p_clcb->p_q_cmd->hdr.event – BTA_GATTC_API_READ_EVT + GATTC_OPTYPE_READ;
if (mapped_op > GATTC_OPTYPE_INDICATION) {
mapped_op = 0;
}
log::error("expect op:({} :0x{:04x}), receive unexpected operation ({}).",
bta_gattc_op_code_name[mapped_op], p_clcb->p_q_cmd->hdr.event,
bta_gattc_op_code_name[op]);
return;
}
/* 如果收到服务变更指示,丢弃所有非配置操作的响应
* 因为服务句柄可能已改变,旧的响应无效
*/
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING && p_clcb->p_srcb->srvc_hdl_chg &&
op != GATTC_OPTYPE_CONFIG) {
log::verbose("Discard all responses when service change indication is received.");
const_cast<tBTA_GATTC_DATA*>(p_data)->op_cmpl.status = GATT_ERROR;
}
/* 根据操作类型调用相应的完成处理函数 */
if (op == GATTC_OPTYPE_READ) {
// 读取操作完成处理
bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl);
} else if (op == GATTC_OPTYPE_WRITE) {
bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);
} else if (op == GATTC_OPTYPE_EXE_WRITE) {
bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl);
} else if (op == GATTC_OPTYPE_CONFIG) {
bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);
/* 如果同一设备上有更多客户端在等待MTU结果,触发它们继续执行 */
auto outstanding_conn_ids = GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(p_clcb->bda);
for (auto conn_id : outstanding_conn_ids) {
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
log::debug("Continue MTU request clcb {}", std::format_ptr(p_clcb));
if (p_clcb) {
log::debug("Continue MTU request for client conn_id=0x{:04x}", conn_id);
bta_gattc_continue(p_clcb);
}
}
}
/* 收到数据库不同步错误时,立即触发服务重新发现 */
if (p_data->op_cmpl.status == GATT_DATABASE_OUT_OF_SYNC) {
log::info("DATABASE_OUT_OF_SYNC, re-discover service");
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
/* 先请求读取数据库哈希值 */
p_clcb->p_srcb->srvc_hdl_db_hash = true;
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
return;
}
/* 如果需要自动更新,触发服务发现 */
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
/* 先请求读取数据库哈希值 */
p_clcb->p_srcb->srvc_hdl_db_hash = true;
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
return;
}
/* 继续处理命令队列中的下一个请求 */
bta_gattc_continue(p_clcb);
}
GATT客户端操作完成的统一处理函数,对应状态表中的BTA_GATTC_OP_CMPL动作。当底层完成读取、写入等操作后,通过BTA_GATTC_OP_CMPL_EVT事件触发此函数,根据操作类型分发给具体的完成处理函数。
读取完成工作流程:
底层完成读取后发送BTA_GATTC_OP_CMPL_EVT事件
状态机执行BTA_GATTC_OP_CMPL动作,调用此函数
验证操作类型为GATTC_OPTYPE_READ
调用bta_gattc_read_cmpl处理读取值
通过回调函数通知上层读取结果
继续处理队列中的下一个命令
bta_gattc_read_cmpl
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
/** read complete */
static void bta_gattc_read_cmpl(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_OP_CMPL* p_data) {
void* my_cb_data; // 回调数据指针
// 判断是否为批量读取操作(多个特征值一次读取)
if (!p_clcb->p_q_cmd->api_read.is_multi_read) {
// 单次读取:获取上层设置的回调函数和回调数据
GATT_READ_OP_CB cb = p_clcb->p_q_cmd->api_read.read_cb;
my_cb_data = p_clcb->p_q_cmd->api_read.read_cb_data;
/* 确定返回给上层的句柄
* 1. 如果是按句柄读取,使用请求时的句柄
* 2. 如果是按UUID读取(handle==0),使用响应中返回的实际句柄
*/
uint16_t handle = p_clcb->p_q_cmd->api_read.handle; // 读取请求中的句柄
if (handle == 0) { // handle为0表示按UUID读取
handle = p_data->p_cmpl->att_value.handle; // 使用实际响应句柄
}
osi_free_and_reset((void**)&p_clcb->p_q_cmd); // 释放命令内存
// 调用上层回调函数,传递读取结果
if (cb) {
cb(p_clcb->bta_conn_id, // 连接ID
p_data->status, // 读取状态(成功/失败)
handle, // 特征句柄
p_data->p_cmpl->att_value.len, // 值长度
p_data->p_cmpl->att_value.value, // 值数据
my_cb_data); // 回调数据
}
} else {
// 批量读取:获取批量读取的回调函数和数据
GATT_READ_MULTI_OP_CB cb = p_clcb->p_q_cmd->api_read_multi.read_cb;
my_cb_data = p_clcb->p_q_cmd->api_read_multi.read_cb_data;
tBTA_GATTC_MULTI handles = p_clcb->p_q_cmd->api_read_multi.handles; // 多个句柄
osi_free_and_reset((void**)&p_clcb->p_q_cmd); // 释放命令内存
// 调用批量读取回调函数
if (cb) {
cb(p_clcb->bta_conn_id, // 连接ID
p_data->status, // 读取状态
handles, // 多个句柄的列表
p_data->p_cmpl->att_value.len, // 总数据长度
p_data->p_cmpl->att_value.value, // 合并的值数据
my_cb_data); // 回调数据
}
}
}
GATT客户端读取操作的完成处理函数,负责解析读取结果并通过回调函数通知上层应用。处理两种读取模式:单次读取(按句柄或按UUID)和批量读取(多个句柄)。
read_char_cb
packages/modules/Bluetooth/system/btif/src/btif_gatt_client.cc
void read_char_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
uint8_t* value, void* /* data */) {
// 构造读取结果参数结构体
btgatt_read_params_t params = {
.handle = handle, // 特征句柄
.value.len = len, // 读取值的长度
.value_type = 0x00, /* GATTC_READ_VALUE_TYPE_VALUE 值类型 */
.status = status, // 读取状态
};
// 安全断言:确保长度不超过GATT最大属性长度
log::assert_that(len <= GATT_MAX_ATTR_LEN, "assert failed: len <= GATT_MAX_ATTR_LEN");
if (len > 0) {
memcpy(params.value.value, value, len); // 复制读取的值到结果结构
}
// 通过JNI调用Java层回调函数
CLI_CBACK_IN_JNI(read_characteristic_cb, conn_id, status, params);
}
#define CLI_CBACK_IN_JNI(P_CBACK, …) \\
do { \\
auto callbacks = bt_gatt_callbacks; \\
if (callbacks && callbacks->client->P_CBACK) { \\
log::verbose("HAL bt_gatt_callbacks->client->{}", #P_CBACK); \\
do_in_jni_thread(Bind(callbacks->client->P_CBACK, __VA_ARGS__)); \\
} else { \\
ASSERTC(0, "Callback is NULL", 0); \\
} \\
} while (0)
GATT客户端读取操作的最终回调,在bta_gattc_read_cmpl中调用。负责将底层读取结果转换为HAL层结构,并通过JNI线程回调到Java层。
btgattc_read_characteristic_cb
packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_gatt.cpp
static void btgattc_read_characteristic_cb(int conn_id, int status,
const btgatt_read_params_t& p_data) {
// 获取回调锁,保护回调过程
std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
// 创建JNI回调环境
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || !mCallbacksObj) {
return; // 环境无效或回调对象为空,直接返回
}
// 创建Java字节数组,用于传递读取的值
ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), NULL);
if (status == 0) { // 读取成功(GATT_SUCCESS)
// 创建与读取值长度相同的字节数组
jb.reset(sCallbackEnv->NewByteArray(p_data.value.len));
// 将C++数据复制到Java字节数组中
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, p_data.value.len, (jbyte*)p_data.value.value);
} else {
// 读取失败,创建一个长度为1的字节数组(占位符)
uint8_t value = 0;
jb.reset(sCallbackEnv->NewByteArray(1));
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 1, (jbyte*)&value);
}
// 调用Java层的onReadCharacteristic回调方法
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadCharacteristic,
conn_id, status, p_data.handle, jb.get());
}
GATT客户端读取操作的JNI层回调函数,负责将C++层的读取结果转换为Java对象,并通过JNI调用Java层的回调方法。这是从Native代码到Java应用层的最终接口。
bta_gattc_continue
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_utils.cc
void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb) {
// 检查当前是否有命令正在执行
if (p_clcb->p_q_cmd != NULL) {
log::info("Already scheduled another request for conn_id = 0x{:04x}", p_clcb->bta_conn_id);
return; // 已有命令在执行,直接返回
}
// 循环处理命令队列,直到队列为空或找到一个可执行的命令
while (!p_clcb->p_q_cmd_queue.empty()) {
const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd_queue.front(); // 获取队列头部的命令
// MTU配置请求的特殊处理
if (p_q_cmd->hdr.event != BTA_GATTC_API_CFG_MTU_EVT) {
// 非MTU配置请求,从队列中移除并立即执行
p_clcb->p_q_cmd_queue.pop_front();
bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
return; // 命令已开始执行,返回
}
/* 当前命令是MTU请求事件,需要特殊处理 */
uint16_t current_mtu = 0;
// 尝试发送MTU请求,返回MTU交换状态
auto result = GATTC_TryMtuRequest(p_clcb->bda, p_clcb->transport,
p_clcb->bta_conn_id, ¤t_mtu);
switch (result) {
case MTU_EXCHANGE_DEVICE_DISCONNECTED: // 设备已断开
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG, GATT_NO_RESOURCES, NULL);
// 命令已处理,释放并继续处理队列中的下一个命令
break;
case MTU_EXCHANGE_NOT_ALLOWED: // MTU交换不允许
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG, GATT_ERR_UNLIKELY, NULL);
// 命令已处理,释放并继续处理队列中的下一个命令
break;
case MTU_EXCHANGE_ALREADY_DONE: // MTU交换已完成
bta_gattc_send_mtu_response(p_clcb, p_q_cmd, current_mtu);
// 命令已处理,释放并继续处理队列中的下一个命令
break;
case MTU_EXCHANGE_IN_PROGRESS: // MTU交换正在进行中
log::warn("Waiting p_clcb {}", std::format_ptr(p_clcb));
return; // 等待当前MTU交换完成,不处理队列中的其他命令
case MTU_EXCHANGE_NOT_DONE_YET: // MTU交换尚未进行
p_clcb->p_q_cmd_queue.pop_front();
bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
return; // 开始执行MTU交换
}
/* 当前MTU请求已被处理(上述前三种情况)
* 从队列中移除并释放该命令,然后继续处理队列中的下一个命令
*/
p_clcb->p_q_cmd_queue.pop_front();
osi_free_and_reset((void**)&p_q_cmd);
// 继续while循环,处理队列中的下一个命令
}
}
GATT客户端命令队列处理的核心,负责在执行完一个操作后继续处理队列中的下一个命令。实现了命令的流水线执行机制,并对MTU配置请求进行了特殊处理,避免了重复的MTU交换。
当读取操作完成时,bta_gattc_op_cmpl函数会调用bta_gattc_continue,触发以下流程:
检查当前连接控制块的命令队列
如果有待处理的命令,取出并执行下一个
实现了读取完成后的自动流水线处理
三、时序图

四、测试
问题:Android 蓝牙 GATT 客户端中bta_gattc_enqueue和bta_gattc_continue的作用是什么?请结合源码说明。(2024 某头部手机厂商安卓蓝牙开发岗真题)
答案:
-
bta_gattc_enqueue:将 GATT 客户端请求入队到 clcb(连接控制块)队列,若当前无 pending 请求则设为p_q_cmd(返回ENQUEUED_READY_TO_SEND),若已有则加入p_q_cmd_queue(返回ENQUEUED_FOR_LATER),避免并发操作冲突;
-
bta_gattc_continue:操作完成后处理队列剩余请求,若p_q_cmd为空则从队列取出下一个请求执行状态机,针对 MTU 请求做特殊校验,确保批量请求有序执行。
问题:GATT_READ_BY_HANDLE 和 GATT_READ_BY_TYPE 在代码实现上的核心差异是什么?
答案:
参数封装:前者封装conn_id+handle,后者封装conn_id+uuid+s_handle/e_handle;
BTA 层:前者handle非 0、无 UUID / 起止句柄,后者handle为 0、填充 UUID 和起止句柄;
Stack 层:前者op_subtype为GATT_READ_BY_HANDLE仅记录句柄,后者为GATT_READ_BY_TYPE记录 UUID 和起止句柄。
问题:GATT 读取流程中gatt_security_check_start加密失败会触发什么逻辑?
答案:
若BTM_SetEncryption返回非BTM_SUCCESS/BTM_CMD_STARTED,会:
重置链路安全动作为GATT_SEC_NONE;
设置链路状态为GATT_CH_OPEN;
调用gatt_end_operation结束操作并返回GATT_INSUF_ENCRYPTION错误;
通过bta_gattc_cmpl_sendmsg发送失败状态,最终回调上层返回加密不足错误。
问题:Android Bluetooth协议栈中GATT读取操作的线程模型是怎样的?(Google面试真题改编)
答案:
Android Bluetooth协议栈采用三层线程模型:
JNI线程:处理Java层调用,通过do_in_jni_thread将任务转发到主线程
主线程(BTA线程):执行核心蓝牙逻辑,通过bta_sys_sendmsg接收消息
回调线程:通过do_in_jni_thread将结果回调到JNI线程再传回Java层 这种设计确保线程安全,避免竞态条件,但需要注意避免主线程阻塞。
问题:GATT客户端如何处理多个并发的读取请求?(小米面试真题)
答案:
通过请求队列机制处理:
bta_gattc_enqueue函数检查当前是否有正在执行的命令(p_q_cmd)
如果有,将新请求放入p_q_cmd_queue队列
当前命令完成后,bta_gattc_continue从队列中取出下一个请求执行
MTU配置请求(BTA_GATTC_API_CFG_MTU_EVT)有特殊处理逻辑,可能等待其他请求完成 这种设计确保请求顺序执行,避免ATT协议层冲突。
问题:GATT读取操作中的安全处理流程是怎样的?(华为面试真题)
答案: 安全处理通过gatt_security_check_start函数实现:
1. 根据操作类型和当前链路安全状态确定所需安全等级
2. 检查gatt_sec_act(安全动作),可能包括:
-
GATT_SEC_SIGN_DATA:数据签名
-
GATT_SEC_ENCRYPT:链路加密
-
GATT_SEC_ENCRYPT_MITM:加密带中间人防护
3. 如果需要加密,调用BTM_SetEncryption设置加密
4. 安全处理完成后才发送ATT读取请求
5. 如果安全处理失败,返回GATT_INSUF_ENCRYPTION错误
问题:Android Bluetooth协议栈中,GATT客户端读取操作为什么需要经过主线程切换?如何实现的?(Google面试题)
答案:
需要主线程切换主要是为了:
-
线程安全:避免多线程同时访问共享数据结构导致竞态条件
-
避免阻塞:将耗时操作(如加密协商)从调用线程移出
-
统一调度:通过消息队列机制保证操作有序执行
实现方式:
在btif_gattc_read_char中调用do_in_jni_thread
将函数调用封装为Bind(&BTA_GATTC_ReadCharacteristic, …)
通过bta_sys_sendmsg将消息投递到主线程消息队列
主线程的bta_sys_event处理消息并调用相应处理器
问题: GATT读取操作中,遇到GATT_DATABASE_OUT_OF_SYNC错误时,协议栈如何处理?(Qualcomm面试题)
答案:
当收到GATT_DATABASE_OUT_OF_SYNC错误码时:
在bta_gattc_op_cmpl函数中检查p_data->op_cmpl.status
将p_clcb->auto_update设置为BTA_GATTC_REQ_WAITING
设置p_clcb->p_srcb->srvc_hdl_db_hash = true标记需要读取DB哈希
触发BTA_GATTC_INT_DISCOVER_EVT事件,开始服务发现
服务发现完成后,数据库与服务句柄重新同步
问题:解释bta_gattc_enqueue函数的作用,并说明为什么需要命令队列机制?(华为面试题)
答案:
bta_gattc_enqueue函数的作用是管理GATT客户端命令的排队执行,主要解决:
串行化请求:GATT协议要求某些操作必须顺序执行,不能并发
避免竞争:防止多个操作同时修改连接状态
错误恢复:当某个操作失败时,能正确处理后续命令
实现机制:
-
如果p_clcb->p_q_cmd为空,直接设置为当前命令,返回ENQUEUED_READY_TO_SEND
-
如果不为空,将命令加入p_clcb->p_q_cmd_queue队列,返回ENQUEUED_FOR_LATER
-
当前命令完成后,bta_gattc_continue会从队列取出下一个命令执行
五、log关键字
btif_gattc_read_char|btif_gattc_read_using_char_uuid|BTA_GATTC_API_READ_EVT|bta_gattc_read|GATTC_Read|GATT_READ_BY_HANDLE|GATT_READ_BY_TYPE|bta_gattc_read_cmpl|read_char_cb|btgattc_read_characteristic_cb|GATT_SUCCESS|GATT_INSUF_ENCRYPTION|bta_gattc_enqueue|bta_gattc_continue






![[C++]算法双指针 复写0-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260910013601-6aa2098179e1b-220x150.png)