【Bluedroid】HFP连接流程源码分析(一)

embedded/2025/1/13 13:11:49/

Bluedroid 蓝牙 HFP 连接流程涵盖多个环节,从前期准备到连接建立、状态管理以及维护与断开,各环节紧密相扣,确保蓝牙免提连接稳定可靠。

一、概述

1.1. 连接前准备

  • 用户操作:用户需在 Android 设备上开启蓝牙功能。同时,目标蓝牙设备(如车载蓝牙)要进入配对模式,Android 设备通过搜索发现目标设备并完成配对,此过程可能需用户输入 PIN 码或确认配对请求 。
  • 协议栈初始化:Bluedroid 蓝牙协议栈要进行初始化,加载必要模块、配置蓝牙芯片。上层协议(如 HFP)向 L2CAP 注册服务,通过发送 L2CA_ConnectReq 请求完成;同时利用 SDP 服务搜索目标设备上的 HFP 服务,通过发送和接收 SDP 搜索请求与响应来实现。

1.2. 连接建立

  • 发起连接请求:SDP 服务搜索成功后,Android 设备向目标设备发起 HFP 连接请求,涉及 L2CAP、RFCOMM 等底层协议栈层次。
  • 建立 RFCOMM 连接:在 L2CAP 链路信道基础上建立 RFCOMM Session,模拟串口通信。此过程包括设置 DLCI、信号状态等步骤,成功建立后上层应用可通过模拟串口通信。
  • 服务级连接:RFCOMM 连接建立后,通过发送和接收 AT 指令进行服务级连接。AG 和 HF 交互如 BRSF、BAC、CIND、CMER 等 AT 指令,直至连接成功。

1.3. 连接状态管理与反馈

  • 状态上报:HFP 连接成功建立后,Bluedroid 蓝牙协议栈通过回调函数,经底层协议栈、JNI 层传递,向上层应用报告连接状态变化。
  • 应用响应:上层应用(如蓝牙电话应用)接收通知后,按预设逻辑处理。连接成功则可能启动音频传输服务,准备收发蓝牙音频数据。

1.4. 与其他蓝牙组件的交互

  • 与蓝牙协议栈的协同工作:整个 HFP 连接流程紧密依赖蓝牙协议栈。在连接请求的传输和处理过程中,严格遵循蓝牙协议规定的通信规范和数据格式。从链路层的物理连接建立到高层的服务发现和连接管理,各个层次之间相互协作,确保数据的可靠传输和连接的稳定建立。
  • 与音频系统的整合:当 HFP 连接成功后,还需要与音频系统进行整合。蓝牙设备通常用于音频传输,因此需要将蓝牙音频流与设备的音频播放或录制组件进行对接。涉及到音频数据的格式转换、音频通道的配置等操作,以确保音频数据能够在设备上正确地播放或录制,为用户提供高质量的蓝牙音频体验。

1.5. 连接维护与断开

  • 连接维护:连接建立后,系统持续监控连接状态,定期发送心跳包验证连接存活,处理因设备移动或信号干扰导致的连接中断。
  • 连接断开:用户不再需要服务时,上层应用发起断开请求,通过底层协议栈发送相应 AT 指令断开 HFP 连接。

Bluedroid蓝牙HFP连接流程涉及多个层次和组件的协同工作。通过严格的初始化、精确的连接请求处理、有效的状态监控和与其他组件的紧密整合,实现了可靠的蓝牙免提连接。该流程遵循蓝牙标准和规范,充分利用Bluedroid蓝牙协议栈的功能,以及上层应用的逻辑处理,满足了用户在蓝牙音频通信方面的需求。

二、源码分析

connect

/packages/modules/Bluetooth/system/btif/src/btif_hf_client.cc
static bt_status_t connect(const RawAddress* bd_addr) {log::verbose("HFP Client version is  {}", btif_hf_client_version);CHECK_BTHF_CLIENT_INIT(); // 检查HFP客户端是否已初始化return btif_queue_connect(UUID_SERVCLASS_HF_HANDSFREE, bd_addr, connect_int);
}

connect函数的核心目标是发起一个连接到指定蓝牙设备,该设备支持蓝牙免提协议(HFP, Hands-Free Profile)的,通过调用 btif_queue_connect 来实际发起连接。btif_queue_connect 函数会将连接请求排队,等待蓝牙堆栈的适当时机来处理。

connect_int

/packages/modules/Bluetooth/system/btif/src/btif_hf_client.cc
/********************************************************************************* Function         connect** Description     connect to audio gateway** Returns         bt_status_t*******************************************************************************/
static bt_status_t connect_int(RawAddress* bd_addr, uint16_t uuid) {btif_hf_client_cb_t* cb = btif_hf_client_allocate_cb(); // 分配回调函数结构体if (cb == NULL) {log::error("could not allocate block!");return BT_STATUS_BUSY;}cb->peer_bda = *bd_addr; // 设置蓝牙设备地址//  检查是否已经与指定的蓝牙设备建立了连接if (is_connected(cb)) return BT_STATUS_BUSY; cb->state = BTHF_CLIENT_CONNECTION_STATE_CONNECTING; // 表示正在尝试连cb->peer_bda = *bd_addr;/* Open HF connection to remote device and get the relevant handle.* The handle is valid until we have called BTA_HfClientClose or the LL* has notified us of channel close due to remote closing, error etc.*/return BTA_HfClientOpen(cb->peer_bda, &cb->handle);
}

构建连接音频网关(Audio Gateway)的基础框架,包含了内存分配、错误处理、状态维护以及连接发起等。

BTA_HfClientOpen

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_api.cc
/********************************************************************************* Function         BTA_HfClientOpen** Description      Opens up a RF connection to the remote device and*                  subsequently set it up for a HF SLC** Returns          bt_status_t*******************************************************************************/
bt_status_t BTA_HfClientOpen(const RawAddress& bd_addr, uint16_t* p_handle) {log::verbose("");tBTA_HF_CLIENT_API_OPEN* p_buf =(tBTA_HF_CLIENT_API_OPEN*)osi_malloc(sizeof(tBTA_HF_CLIENT_API_OPEN));if (!bta_hf_client_allocate_handle(bd_addr, p_handle)) { // 尝试为新的连接分配一个句柄log::error("could not allocate handle");return BT_STATUS_FAIL;}p_buf->hdr.event = BTA_HF_CLIENT_API_OPEN_EVT; // 打开HF客户端连接的事件p_buf->hdr.layer_specific = *p_handle;p_buf->bd_addr = bd_addr;bta_sys_sendmsg(p_buf);return BT_STATUS_SUCCESS;
}

打开与远程设备的射频(RF)连接,并为蓝牙免提(Hands-Free,HF)语音链路控制(SLC)进行设置。

bta_sys_sendmsg(BTA_HF_CLIENT_API_OPEN_EVT)

来到了详细的消息发送事件,前面多次有展开过,不再详解。

HF Client使能的时候调用bta_sys_register注册其事件处理函数。

HF Client的事件最终都会调用到bta_hf_client_hdl_event进行处理。

bta_hf_client_hdl_event

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_main.cc
/********************************************************************************* Function         bta_hf_client_hdl_event** Description      Data HF Client main event handling function.*** Returns          bool*******************************************************************************/
bool bta_hf_client_hdl_event(const BT_HDR_RIGID* p_msg) {log::verbose("{} (0x{:x})", bta_hf_client_evt_str(p_msg->event),p_msg->event);bta_hf_client_sm_execute(p_msg->event, (tBTA_HF_CLIENT_DATA*)p_msg);return true;
}

将事件传递给状态机进行处理。

bta_hf_client_sm_execute

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_main.cc
/********************************************************************************* Function         bta_hf_client_sm_execute** Description      State machine event handling function for HF Client*** Returns          void*******************************************************************************/
void bta_hf_client_sm_execute(uint16_t event, tBTA_HF_CLIENT_DATA* p_data) {tBTA_HF_CLIENT_CB* client_cb =bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); // 查找对应的HF Client控制块if (client_cb == NULL) {log::error("cb not found for handle {}", p_data->hdr.layer_specific);return;}tBTA_HF_CLIENT_ST_TBL state_table;uint8_t action;int i;uint16_t in_event = event;uint8_t in_state = client_cb->state; // 从控制块中获取当前状态/* Ignore displaying of AT results when not connected (Ignored in state* machine) */if (client_cb->state == BTA_HF_CLIENT_OPEN_ST) {log::verbose("HF Client evt : State {} ({}), Event 0x{:04x} ({})",client_cb->state, bta_hf_client_state_str(client_cb->state),event, bta_hf_client_evt_str(event));}event &= 0x00FF; // 将事件值限制在有效范围内if (event >= (BTA_HF_CLIENT_MAX_EVT & 0x00FF)) {log::error("HF Client evt out of range, ignoring...");return;}/* look up the state table for the current state */state_table = bta_hf_client_st_tbl[client_cb->state]; // 查找状态表  /* set next state */client_cb->state = state_table[event][BTA_HF_CLIENT_NEXT_STATE]; // 设置下一个状态/* execute action functions */// 执行action函数for (i = 0; i < BTA_HF_CLIENT_ACTIONS; i++) {action = state_table[event][i];if (action != BTA_HF_CLIENT_IGNORE) {(*bta_hf_client_action[action])(p_data);} else {break;}}/* If the state has changed then notify the app of the corresponding change */// 根据新状态向应用程序发送相应的回调if (in_state != client_cb->state) {log::verbose("notifying state change to {} -> {} device {}", in_state,client_cb->state,ADDRESS_TO_LOGGABLE_STR(client_cb->peer_addr));tBTA_HF_CLIENT evt;memset(&evt, 0, sizeof(evt));evt.bd_addr = client_cb->peer_addr;if (client_cb->state == BTA_HF_CLIENT_INIT_ST) {bta_hf_client_app_callback(BTA_HF_CLIENT_CLOSE_EVT, &evt);log::verbose("marking CB handle {} to false", client_cb->handle);client_cb->is_allocated = false;} else if (client_cb->state == BTA_HF_CLIENT_OPEN_ST) {evt.open.handle = client_cb->handle;bta_hf_client_app_callback(BTA_HF_CLIENT_OPEN_EVT, &evt);}}log::verbose("device {} state change: [{}] -> [{}] after Event [{}]",ADDRESS_TO_LOGGABLE_STR(client_cb->peer_addr),bta_hf_client_state_str(in_state),bta_hf_client_state_str(client_cb->state),bta_hf_client_evt_str(in_event));
}

负责根据当前状态和接收到的事件来决定HF Client的下一步动作。包括查找对应的状态表、设置下一个状态、执行相应的动作函数,并在状态发生变化时通知应用程序。

bta_hf_client_st_init

进入到init状态下的HF Client状态表bta_hf_client_action

找到对应的处理函数bta_hf_client_start_open。

bta_hf_client_start_open

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_act.cc
/********************************************************************************* Function         bta_hf_client_start_open** Description      This starts an HF Client open.*** Returns          void*******************************************************************************/
void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA* p_data) {tBTA_HF_CLIENT_CB* client_cb =bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); // 查找对应的HF Client控制块if (client_cb == NULL) {log::error("wrong handle to control block {}", p_data->hdr.layer_specific);return;}/* store parameters */if (p_data) {client_cb->peer_addr = p_data->api_open.bd_addr;}/* Check if RFCOMM has any incoming connection to avoid collision. */RawAddress pending_bd_addr = RawAddress::kEmpty;if (PORT_IsOpening(&pending_bd_addr)) { // 检查是否有正在打开的RFCOMM连接/* Let the incoming connection goes through.                        *//* Issue collision for now.                                         *//* We will decide what to do when we find incoming connection later.*/bta_hf_client_collision_cback(BTA_SYS_CONN_OPEN, BTA_ID_HS, 0,client_cb->peer_addr);return;}/* set role */client_cb->role = BTA_HF_CLIENT_INT; // 明确客户端在此次连接中的角色/* do service search */bta_hf_client_do_disc(client_cb);
}

蓝牙HF Client打开流程的起始点,包含查找关键控制块、存储参数、规避RFCOMM连接冲突、设置角色以及启动服务搜索等步骤,为后续顺利建立蓝牙免提连接铺好道路。

bta_hf_client_do_disc

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_sdp.cc
/********************************************************************************* Function         bta_hf_client_do_disc** Description      Do service discovery.*** Returns          void*******************************************************************************/
void bta_hf_client_do_disc(tBTA_HF_CLIENT_CB* client_cb) {Uuid uuid_list[1];uint16_t num_uuid = 1;uint16_t attr_list[4];uint8_t num_attr;bool db_inited = false;/* initiator; get proto list and features */// 根据HF Client的角色(发起者或接受者),设置要搜索的UUID(通用唯一标识符)列表和属性列表if (client_cb->role == BTA_HF_CLIENT_INT) {attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;attr_list[3] = ATTR_ID_SUPPORTED_FEATURES;num_attr = 4;uuid_list[0] = Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE); // 设置为HF服务的16位UUID}/* acceptor; get features */else {attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST;attr_list[2] = ATTR_ID_SUPPORTED_FEATURES;num_attr = 3;uuid_list[0] = Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);}/* allocate buffer for sdp database */client_cb->p_disc_db = (tSDP_DISCOVERY_DB*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);/* set up service discovery database; attr happens to be attr_list len */db_inited = get_legacy_stack_sdp_api()->service.SDP_InitDiscoveryDb(client_cb->p_disc_db, BT_DEFAULT_BUFFER_SIZE, num_uuid, uuid_list,num_attr, attr_list); // 初始化SDP数据库if (db_inited) {/*Service discovery not initiated */// 向远程设备发起服务搜索请求// 请求包括远程设备的蓝牙地址、SDP数据库指针、回调函数(bta_hf_client_sdp_cback)以及用户数据(client_cb指针的强制转换)db_inited =get_legacy_stack_sdp_api()->service.SDP_ServiceSearchAttributeRequest2(client_cb->peer_addr, client_cb->p_disc_db, bta_hf_client_sdp_cback,(void*)client_cb);}if (!db_inited) { // 数据库初始化失败或服务搜索请求未能发起/*free discover db */osi_free_and_reset((void**)&client_cb->p_disc_db);/* sent failed event */tBTA_HF_CLIENT_DATA msg;msg.hdr.layer_specific = client_cb->handle;bta_hf_client_sm_execute(BTA_HF_CLIENT_DISC_FAIL_EVT, &msg);}
}

执行服务发现过程以查找远程蓝牙设备上的Hands-Free (HF) 服务。

SDP_ServiceSearchAttributeRequest2

SDP(Service Discovery Protocol,服务发现协议)是蓝牙技术中用于发现可用服务和设备功能的一种机制。在蓝牙Hands-Free Audio Gateway(HF AG)和Hands-Free Unit(HFU,通常简称为HF)的交互中,SDP服务搜索属性请求是HF AG用于查找HF设备提供的服务和特性的关键步骤。

SDP的代码分析单独分析,这里直接看对应的HCI log:

SDP Service Search Attribute Request (Hands-Free Audio Gateway)

构建请求:客户端把目标服务的 UUID (Universally Unique Identifier,通用唯一识别码),与免提音频网关相关的 UUID,放入请求数据包中 。列举期望查询的属性 ID,常见属性诸如 ATTR_ID_SERVICE_CLASS_ID_LIST(服务类 ID 列表)、ATTR_ID_PROTOCOL_DESC_LIST(协议描述列表)、ATTR_ID_SUPPORTED_FEATURES(支持的特性)等,这些属性能够勾勒出服务的轮廓。

发送与接收:客户端将这个构建好的请求发送给目标免提音频网关设备。网关收到请求后,依据自身配置和支持的服务,在本地 SDP 数据库里检索对应的信息,把匹配的数据整理好,再回应给客户端。

请求过程

  1. 构建请求:HF AG使用SDP API构建服务搜索属性请求,指定目标设备地址、UUID列表和属性列表。

  2. 发送请求:HF AG通过蓝牙L2CAP(Logical Link Control and Adaptation Protocol Layer)通道将请求发送到HF设备。

  3. 处理响应:HF设备收到请求后,会检查其SDP数据库以找到匹配的服务和属性。然后,会构建一个响应消息,包含请求的属性值,并通过L2CAP通道发送回HF AG。

SDP Service Search Attribute Response (Hands-Free Audio Gateway Generic Audio)

  4. 解析响应:HF AG收到响应后,会解析消息以提取所需的服务和属性信息。这些信息通常用于后续的服务连接和配置过程。

通过此请求,HF AG可以了解HF设备支持的功能和特性,从而确保它们能够正确地建立连接并进行通信。例如,HF AG可能会检查HF设备是否支持免提通话、语音识别、电话簿访问等功能。

注意事项

  • 兼容性:不同的HF设备可能支持不同的服务和特性。因此,HF AG需要确保它能够处理来自不同设备的各种响应。

  • 安全性:SDP请求和响应是通过蓝牙无线传输的,因此可能会受到窃听或篡改的风险。为了增强安全性,可以使用蓝牙安全特性(如加密和认证)来保护这些通信。

  • 性能:SDP请求可能会引入一定的延迟,特别是在搜索大量服务或属性时。因此,在设计系统时需要考虑这一点,并尽可能优化搜索过程以减少延迟。

SDP服务搜索属性请求在蓝牙Hands-Free Audio Gateway和Hands-Free Unit之间的交互中扮演着重要角色。它允许HF AG发现HF设备上的服务和特性,从而确保它们能够正确地建立连接并进行通信。

bta_hf_client_sdp_cback

SDP的结果会回调给bta_hf_client_sdp_cback。

/********************************************************************************* Function         bta_hf_client_sdp_cback** Description      SDP callback function.*** Returns          void*******************************************************************************/
static void bta_hf_client_sdp_cback(UNUSED_ATTR const RawAddress& bd_addr,tSDP_STATUS status, const void* data) {uint16_t event;tBTA_HF_CLIENT_DISC_RESULT* p_buf = (tBTA_HF_CLIENT_DISC_RESULT*)osi_malloc(sizeof(tBTA_HF_CLIENT_DISC_RESULT));log::verbose("bta_hf_client_sdp_cback status:0x{:x}", status);tBTA_HF_CLIENT_CB* client_cb = (tBTA_HF_CLIENT_CB*)data;/* set event according to int/acp */// 根据客户端的角色(主动发起方或接受方),设置要发送的事件类型if (client_cb->role == BTA_HF_CLIENT_ACP)event = BTA_HF_CLIENT_DISC_ACP_RES_EVT;elseevent = BTA_HF_CLIENT_DISC_INT_RES_EVT;// 填充事件消息p_buf->hdr.event = event;p_buf->hdr.layer_specific = client_cb->handle;p_buf->status = status;bta_sys_sendmsg(p_buf);
}

处理SDP查询的结果。根据SDP操作的状态和提供的回调数据,构造一个事件消息,并通过蓝牙系统的消息发送机制发送Event到bta_sys_event中进行处理。 最后还是回到bta_hf_client_sm_execute()方法中。

bta_hf_client_sm_execute

从API_OPEN 状态 BTA_HF_CLIENT_INIT_ST, 之后将状态迁移到 BTA_HF_CLIENT_OPENING_ST。对应的state_stable是 bta_hf_client_st_opening。

根据BTA_HF_CLIENT_DISC_INT_RES_EVT 事件再进行枚举

根据BTA_HF_CLIENT_DISC_INT_RES_EVT 的值。在bta_hf_client_st_opening找到下标为9那一列。

相应的action 值,以及下一个状态。 BTA_HF_CLIENT_DISC_INT_RES 为 13,下一个迁移状态仍然是 BTA_HF_CLIENT_OPENING_ST。

根据上面的action值,在bta_hf_client_action 函数指针数组中找到对应下标的函数。

最后找到bta_hf_client_disc_int_res。

bta_hf_client_disc_int_res

/packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_act.cc
/********************************************************************************* Function         bta_hf_client_disc_int_res** Description      This function handles a discovery result when initiator.*** Returns          void*******************************************************************************/
void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA* p_data) {uint16_t event = BTA_HF_CLIENT_DISC_FAIL_EVT;log::verbose("Status: {}", p_data->disc_result.status);tBTA_HF_CLIENT_CB* client_cb =bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); // 查找客户端控制块if (client_cb == NULL) {log::error("cb not found for handle {}", p_data->hdr.layer_specific);return;}/* if found service */if (p_data->disc_result.status == SDP_SUCCESS ||p_data->disc_result.status == SDP_DB_FULL) {/* get attributes */if (bta_hf_client_sdp_find_attr(client_cb)) { // 进一步尝试获取服务属性event = BTA_HF_CLIENT_DISC_OK_EVT;}}/* free discovery db */bta_hf_client_free_db(p_data);/* send ourselves sdp ok/fail event */// 将之前确定的事件(成功或失败)以及相关的数据发送给HF客户端的状态机,以便进行后续处理bta_hf_client_sm_execute(event, p_data);
}

处理作为服务发现发起者时的发现结果。根据服务发现的结果来决定后续的操作,包括是否成功找到服务、释放发现数据库以及发送相应的事件给状态机。

bta_hf_client_sdp_find_attr
packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_sdp.cc
/********************************************************************************* Function         bta_hf_client_sdp_find_attr** Description      Process SDP discovery results to find requested attribute*** Returns          true if results found, false otherwise.*******************************************************************************/
bool bta_hf_client_sdp_find_attr(tBTA_HF_CLIENT_CB* client_cb) {tSDP_DISC_REC* p_rec = NULL;tSDP_DISC_ATTR* p_attr;tSDP_PROTOCOL_ELEM pe;bool result = false;client_cb->peer_version = HFP_VERSION_1_1; /* Default version *//* loop through all records we found */while (true) { // 通过循环遍历所有发现的SDP记录/* get next record; if none found, we're done */p_rec = get_legacy_stack_sdp_api()->db.SDP_FindServiceInDb(client_cb->p_disc_db, UUID_SERVCLASS_AG_HANDSFREE, p_rec);if (p_rec == NULL) {break;}/* get scn from proto desc list if initiator */if (client_cb->role == BTA_HF_CLIENT_INT) {// 获取SCN(Service Channel Number,服务通道号)if (get_legacy_stack_sdp_api()->record.SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {client_cb->peer_scn = (uint8_t)pe.params[0];} else {continue;}}/* get profile version (if failure, version parameter is not updated) */get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_HF_HANDSFREE, &client_cb->peer_version); // 从记录中找到HFP的版本/* get features */// 获取特性p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);if (p_attr != NULL &&SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {/* Found attribute. Get value. *//* There might be race condition between SDP and BRSF.  *//* Do not update if we already received BRSF.           */if (client_cb->peer_features == 0) {client_cb->peer_features = p_attr->attr_value.v.u16;/* SDP and BRSF WBS bit are different, correct it if set */if (client_cb->peer_features & 0x0020) {client_cb->peer_features &= ~0x0020;client_cb->peer_features |= BTA_HF_CLIENT_PEER_CODEC;}/* get network for ability to reject calls */p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(p_rec, ATTR_ID_NETWORK);if (p_attr != NULL &&SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {if (p_attr->attr_value.v.u16 == 0x01) {client_cb->peer_features |= BTA_HF_CLIENT_PEER_REJECT;}}}}/* found what we needed */result = true;break;}log::verbose("peer_version=0x{:x} peer_features=0x{:x}",client_cb->peer_version, client_cb->peer_features);return result;
}

在 SDP 发现结果中深挖目标服务的关键信息,如版本、特性、协议元素等,为后续蓝牙免提连接流程的精准推进提供支撑。

bta_hf_client_sm_execute(BTA_HF_CLIENT_DISC_OK_EVT)

目前的状态是BTA_HF_CLIENT_OPENING_ST。对应的state_stable是 bta_hf_client_st_opening。

根据BTA_HF_CLIENT_DISC_OK_EVT事件再进行枚举,找相应的action 值,以及下一个状态。

找相应的action 值,以及下一个状态。下一个迁移状态仍然是 BTA_HF_CLIENT_OPENING_ST。

根据上面的action值,在bta_hf_client_action 函数指针数组中找到对应下标的函数。

最后找到bta_hf_client_rfc_do_open。

bta_hf_client_rfc_do_open

packages/modules/Bluetooth/system/bta/hf_client/bta_hf_client_rfc.cc
/********************************************************************************* Function         bta_hf_client_rfc_do_open** Description      Open an RFCOMM connection to the peer device.*** Returns          void*******************************************************************************/
void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA* p_data) {tBTA_HF_CLIENT_CB* client_cb =bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);if (client_cb == NULL) {log::error("cb not found for handle {}", p_data->hdr.layer_specific);return;}// 创建RFCOMM连接if (RFCOMM_CreateConnectionWithSecurity(UUID_SERVCLASS_HF_HANDSFREE, client_cb->peer_scn, false,BTA_HF_CLIENT_MTU, client_cb->peer_addr, &(client_cb->conn_handle),bta_hf_client_mgmt_cback,BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) == PORT_SUCCESS) {bta_hf_client_setup_port(client_cb->conn_handle); // 设置端口log::verbose("bta_hf_client_rfc_do_open : conn_handle = {}",client_cb->conn_handle);}/* RFCOMM create connection failed; send ourselves RFCOMM close event */else {bta_hf_client_sm_execute(BTA_HF_CLIENT_RFC_CLOSE_EVT, p_data);}
}

与对端设备之间建立RFCOMM(Radio FrequencyCOMMunication)连接。用于音频通信,如蓝牙耳机或其他蓝牙音频设备。

RFCOMM_CreateConnectionWithSecurity接下一篇分析。

三、关键字

HFP Client version is|connect_int|BTA_HfClientOpen|BTA_HF_CLIENT_API_OPEN_EVT|bta_hf_client_sdp_cback|BTA_HF_CLIENT_DISC_INT_RES_EVT|bta_hf_client_disc_int_res|bta_hf_client_sdp_find_attr|BTA_HF_CLIENT_DISC_OK_EVT|bta_hf_client_rfc_do_open


http://www.ppmy.cn/embedded/153573.html

相关文章

【蓝桥杯——物联网设计与开发】Part2:OLED

目录 一、OLED &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;驱动原理 &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、OLED接口函数封装 &#x1f7e1;️OLED_Wri…

2025域名出售交易平台PHP源码

源码介绍 2025域名出售交易平台PHP源码,搭建即可正常使用&#xff0c;后台功能测试正常&#xff0c;前台测试正常,无需到处找教程或修复&#xff0c;教程一次性到位 搭建教程 PHP必须是5.6的 导入数据库 数据库配置文件 config/config.php 后台 http://域名/ymadmin 用户&am…

#渗透测试#网络安全# 一文了解什么是跨域CROS!!!

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

简历整理YH

一&#xff0c;订单中心 1&#xff0c;调拨单 融通(Rocketmq)-订单中心&#xff1a;ECC_BMS123(已出单)&#xff0c;125(分配),127(发货),129(收货) 通过RocketMq接入多场景订单数据 2&#xff0c;销售单 sap&#xff08;FTP&#xff09;-订单中心&#xff0c;下发1002,1003,…

【Rust】错误处理机制

目录 思维导图 引言 一、错误处理的重要性 1.1 软件中的错误普遍存在 1.2 编译时错误处理要求 二、错误的分类 2.1 可恢复错误&#xff08;Recoverable Errors&#xff09; 2.2 不可恢复错误&#xff08;Unrecoverable Errors&#xff09; 三、Rust 的错误处理机制 3…

Python的循环

Python的循环 Python的循环有两种&#xff0c;分别是for…in循环和while循环。 for…in 循环 假设我们要循环输出一个列表里的元素&#xff1a; names [张三,李四,王五] for name in names:print(name)执行这段代码后&#xff0c;会依次打印names的每一个元素&#xff1a;…

css 布局及动画应用(flex+transform+transition+animation)

文章目录 css 布局及动画应用animationtransform&#xff0c;transition&#xff0c;animation 综合应用实例代码实例解释 css 布局及动画应用 Display用法 作用&#xff1a;用于控制元素的显示类型&#xff0c;如块级元素、内联元素、无显示等。常见属性值及示例&#xff1a;…

HTTP 入门:认识网络通信基础

一、引言&#xff1a;HTTP 在网络世界的基石地位 在当今数字化的时代&#xff0c;网络通信如同空气一般无处不在&#xff0c;而 HTTP 协议则是网络世界中最为重要的基石之一。无论是浏览网页、使用手机 APP&#xff0c;还是进行各种网络数据交互&#xff0c;HTTP 都在背后默默…