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 数据库里检索对应的信息,把匹配的数据整理好,再回应给客户端。
请求过程
-
构建请求:HF AG使用SDP API构建服务搜索属性请求,指定目标设备地址、UUID列表和属性列表。
-
发送请求:HF AG通过蓝牙L2CAP(Logical Link Control and Adaptation Protocol Layer)通道将请求发送到HF设备。
-
处理响应: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;
else
event = 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接下一篇分析。