欢迎光临
我们一直在努力

【Bluedroid】Bluetooth GATT 客户端特征值读取流程剖析(GATT_READ_BY_TYPE/GATT_READ_BY_HANDLE)

博主简介

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, &current_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



    赞(0)
    未经允许不得转载:171主机测评 » 【Bluedroid】Bluetooth GATT 客户端特征值读取流程剖析(GATT_READ_BY_TYPE/GATT_READ_BY_HANDLE)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址