【Bluedroid】蓝牙HID DEVICE断开连接流程源码分析
蓝牙HID(Human Interface Device)的断开连接流程涉及从应用层到协议栈的多层交互。本文通过剖析Android Bluetooth协议栈代码,梳理从上层调用disconnect()
到最终物理链路断开的完整流程,涵盖状态检查、消息传递、L2CAP通道关闭、资源清理及回调通知等关键环节。流程中通过事件驱动机制协调各模块,确保安全断开并通知应用层状态变化。
一、流程概述
1.1 应用层触发断开
-
入口函数:
btif_hd.cc
中的disconnect()
首先检查应用注册和设备状态。-
若条件不满足(如未注册或未启用),直接返回错误状态。
-
条件满足时调用
BTA_HdDisconnect()
发起断开请求。
-
1.2 BTA层消息传递
-
BTA_HdDisconnect通过
bta_sys_sendmsg()
发送BTA_HD_API_DISCONNECT_EVT
事件,触发状态机处理,而非直接操作硬件。
1.3 状态机处理断开事件
-
状态机
bta_hd_better_state_machine
根据当前状态(如BTA_HD_CONN_ST
)处理BTA_HD_API_DISCONNECT_EVT
,调用bta_hd_disconnect_act()
执行实际断开操作。
1.4 HID协议栈断开操作
-
HID_DevDisconnect()验证设备状态后,调用
hidd_conn_disconnect()
关闭L2CAP通道:-
清理待发送数据(
osi_free(hd_cb.pending_data)
)。 -
通过
L2CA_DisconnectReq()
依次断开控制通道(ctrl_cid
)和中断通道(intr_cid
)。 -
更新连接状态为
HID_CONN_STATE_DISCONNECTING
。
-
1.5 底层通道关闭与资源释放
-
hidd_l2cif_disconnect()处理L2CAP断开:
-
若所有通道关闭(
ctrl_cid
和intr_cid
均为0),更新设备状态为HIDD_DEV_NO_CONN
,并通过回调通知上层(HID_DHOST_EVT_CLOSE
或HID_DHOST_EVT_VC_UNPLUG
)。
-
1.6 回调通知与资源清理
-
BTA层回调:
bta_hd_cback()
根据事件类型(如HID_DHOST_EVT_CLOSE
)转发至btif_hd_upstreams_evt()
。 -
应用层处理:
-
正常断开:通知
BTHD_CONN_STATE_DISCONNECTED
,移除设备绑定信息(BTA_DmRemoveDevice()
或btif_hd_remove_device()
)。 -
虚拟线缆拔出:触发
vc_unplug_cb
,强制清理设备数据。
-
二、源码分析
disconnect
/packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function disconnect** Description Disconnects from host** Returns bt_status_t*******************************************************************************/
static bt_status_t disconnect(void) {log::verbose("");if (!btif_hd_cb.app_registered) {log::warn("application not yet registered");return BT_STATUS_NOT_READY;}if (btif_hd_cb.status != BTIF_HD_ENABLED) {log::warn("BT-HD not enabled, status={}", btif_hd_cb.status);return BT_STATUS_NOT_READY;}BTA_HdDisconnect();return BT_STATUS_SUCCESS;
}
断开与蓝牙主机的连接。在执行断开连接操作之前,先检查应用程序是否已经注册以及蓝牙 HID 设备(BT - HD)是否已经启用。若这两个条件都满足,会调用 BTA_HdDisconnect
函数来发起断开连接的请求;
BTA_HdDisconnect
/packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function BTA_HdDisconnect** Description This function is called when host shall be disconnected** Returns void*******************************************************************************/
void BTA_HdDisconnect(void) {log::verbose("");BT_HDR_RIGID* p_buf = (BT_HDR_RIGID*)osi_malloc(sizeof(BT_HDR_RIGID));p_buf->event = BTA_HD_API_DISCONNECT_EVT;bta_sys_sendmsg(p_buf);
}
该函数本身并不直接执行断开连接的实际动作,而是通过消息机制通知系统进行相应处理。
bta_hd_better_state_machine(BTA_HD_API_DISCONNECT_EVT)
packages/modules/Bluetooth/system/bta/hd/bta_hd_main.cc
static void bta_hd_better_state_machine(uint16_t event, tBTA_HD_DATA* p_data) {switch (get_state()) {case BTA_HD_INIT_ST:...break;case BTA_HD_IDLE_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_INIT_ST);bta_hd_unregister_act();break;case BTA_HD_API_CONNECT_EVT:bta_hd_connect_act(p_data);break;...case BTA_HD_CONN_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_TRANSIENT_TO_INIT_ST);bta_hd_disconnect_act();break;case BTA_HD_API_DISCONNECT_EVT:bta_hd_disconnect_act();break;...
bta_hd_disconnect_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_disconnect_act** Description Disconnect from device** Returns void*******************************************************************************/
void bta_hd_disconnect_act() {tHID_STATUS ret;tBTA_HD cback_data;log::verbose("");ret = HID_DevDisconnect();if (ret != HID_SUCCESS) {log::warn("HID_DevDisconnect returned {}", ret);return;}if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) {cback_data.conn.status = BTHD_CONN_STATE_DISCONNECTING;bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data);}
}
执行与蓝牙设备断开连接的操作。首先调用 HID_DevDisconnect
函数尝试断开连接,然后检查断开连接的结果。如果断开连接成功,会获取设备地址并设置连接状态为 “正在断开连接”,最后调用回调函数 bta_hd_cb.p_cback
通知上层连接状态的变化。
bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data)
btif_hd_upstreams_evt(BTA_HD_CONN_STATE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function btif_hd_upstreams_evt** Description Executes events in btif context** Returns void*******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {tBTA_HD* p_data = (tBTA_HD*)p_param;log::verbose("event={}", dump_hd_event(event));switch (event) {...case BTA_HD_CONN_STATE_EVT:HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda,(bthd_connection_state_t)p_data->conn.status);break;...
当接收到 BTA_HD_CONN_STATE_EVT
事件时,会调用回调函数来通知上层应用设备的连接状态发生了变化(BTHD_CONN_STATE_DISCONNECTING)。
HID_DevDisconnect
/packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function HID_DevDisconnect** Description Disconnects from device** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevDisconnect(void) {if (!hd_cb.reg_flag) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_DISCONNECT,1);return HID_ERR_NOT_REGISTERED;}if (!hd_cb.device.in_use) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DEVICE_NOT_IN_USE_AT_DISCONNECT,1);return HID_ERR_INVALID_PARAM;}if (hd_cb.device.state == HIDD_DEV_NO_CONN) {/* If we are still trying to connect, just close the connection. */if (hd_cb.device.conn.conn_state != HID_CONN_STATE_UNUSED) { // 说明设备正在尝试连接tHID_STATUS ret = hidd_conn_disconnect();hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE,HID_ERR_DISCONNECTING, NULL);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DISCONNECTING,1);return ret;}// 确实没有连接log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_CONNECTION_AT_DISCONNECT,1);return HID_ERR_NO_CONNECTION;}return hidd_conn_disconnect();
}
在执行断开连接操作之前,会对设备的注册状态、使用状态和连接状态进行检查。若检查通过,则调用 hidd_conn_disconnect
来实际执行断开连接操作。
hidd_conn_disconnect
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/********************************************************************************* Function hidd_conn_disconnect** Description Disconnects existing HID connection** Returns HID_SUCCESS*******************************************************************************/
tHID_STATUS hidd_conn_disconnect(void) {log::verbose("");// clean any outstanding data on intrif (hd_cb.pending_data) {osi_free(hd_cb.pending_data);hd_cb.pending_data = NULL;}// 获取连接信息指针tHID_CONN* p_hcon = &hd_cb.device.conn;// 判断连接状态并执行断开操作if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) {p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;/* Set l2cap idle timeout to 0 (so ACL link is disconnected* immediately after last channel is closed) */// 将 L2CAP 空闲超时时间设置为0,这样在最后一个通道关闭后,ACL 链路会立即断开L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR);if (p_hcon->intr_cid) {hidd_l2cif_disconnect(p_hcon->intr_cid);} else if (p_hcon->ctrl_cid) {hidd_l2cif_disconnect(p_hcon->ctrl_cid);}} else {log::warn("already disconnected");p_hcon->conn_state = HID_CONN_STATE_UNUSED;}return (HID_SUCCESS);
}
在执行断开操作前,会清理可能存在的待处理数据,然后根据连接的控制通道 ID(ctrl_cid
)和中断通道 ID(intr_cid
)的情况,执行相应的断开连接步骤,并更新连接状态。若连接已经断开,则更新状态。
hidd_l2cif_disconnect
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
static void hidd_l2cif_disconnect(uint16_t cid) {L2CA_DisconnectReq(cid);log::verbose("cid={:04x}", cid);tHID_CONN* p_hcon = &hd_cb.device.conn;if (p_hcon->conn_state == HID_CONN_STATE_UNUSED ||(p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {log::warn("unknown cid");return;}if (cid == p_hcon->ctrl_cid) {p_hcon->ctrl_cid = 0;} else {p_hcon->intr_cid = 0;// now disconnect CTRLL2CA_DisconnectReq(p_hcon->ctrl_cid);if (bluetooth::common::init_flags::clear_hidd_interrupt_cid_on_disconnect_is_enabled()) {p_hcon->ctrl_cid = 0;}}// 更新连接状态与回调通知if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {log::verbose("INTR and CTRL disconnected");hd_cb.device.state = HIDD_DEV_NO_CONN;p_hcon->conn_state = HID_CONN_STATE_UNUSED;if (hd_cb.pending_vc_unplug) {hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG,p_hcon->disc_reason, NULL);hd_cb.pending_vc_unplug = FALSE;} else {hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE,p_hcon->disc_reason, NULL);}}
}
断开指定的 L2CAP通道连接。先调用 L2CA_DisconnectReq
尝试断开连接,然后根据连接状态判断是否为未知通道 ID。若通道 ID 有效,根据通道类型(控制通道或中断通道)断开相应通道连接,并在两个通道都断开时更新设备连接状态,调用回调函数通知上层连接已断开。
情况1:hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG,p_hcon->disc_reason, NULL)
bta_hd_cback(HID_DHOST_EVT_VC_UNPLUG)
/********************************************************************************* Function bta_hd_cback** Description BTA HD callback function** Returns void*******************************************************************************/
static void bta_hd_cback(const RawAddress& bd_addr, uint8_t event,uint32_t data, BT_HDR* pdata) {tBTA_HD_CBACK_DATA* p_buf = NULL;uint16_t sm_event = BTA_HD_INVALID_EVT;log::verbose("event={}", event);switch (event) {...case HID_DHOST_EVT_CLOSE:sm_event = BTA_HD_INT_CLOSE_EVT;break;...case HID_DHOST_EVT_VC_UNPLUG:sm_event = BTA_HD_INT_VC_UNPLUG_EVT;break;...}if (sm_event != BTA_HD_INVALID_EVT &&(p_buf = (tBTA_HD_CBACK_DATA*)osi_malloc(sizeof(tBTA_HD_CBACK_DATA) +sizeof(BT_HDR))) != NULL) {p_buf->hdr.event = sm_event;p_buf->addr = bd_addr;p_buf->data = data;p_buf->p_data = pdata;bta_sys_sendmsg(p_buf);}
}
bta_hd_better_state_machine(BTA_HD_INT_VC_UNPLUG_EVT)
case BTA_HD_INT_VC_UNPLUG_EVT:set_state(BTA_HD_IDLE_ST);bta_hd_vc_unplug_done_act(p_data);break;
bta_hd_vc_unplug_done_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_vc_unplug_done_act** Description** Returns void*******************************************************************************/
void bta_hd_vc_unplug_done_act(tBTA_HD_DATA* p_data) {tBTA_HD_CBACK_DATA* p_cback = (tBTA_HD_CBACK_DATA*)p_data;tBTA_HD cback_data;log::verbose("");bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); // 关闭系统连接HID_DevUnplugDevice(p_cback->addr); // 设备拔出操作// 设置回调数据并调用回调函数cback_data.conn.bda = p_cback->addr;bta_hd_cb.bd_addr = p_cback->addr;(*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data);
}
处理 HID 设备虚拟线缆(VC)拔出完成后的操作。完成关闭系统连接、执行设备拔出操作,并通过回调函数通知上层设备虚拟线缆已拔出。
bta_sys_conn_close
packages/modules/Bluetooth/system/bta/sys/bta_sys_conn.cc
/********************************************************************************* Function bta_sys_conn_close** Description Called by BTA subsystems when a connection to the service* is closed*** Returns void*******************************************************************************/
void bta_sys_conn_close(tBTA_SYS_ID id, uint8_t app_id,const RawAddress& peer_addr) {if (bta_sys_cb.prm_cb) {bta_sys_cb.prm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr);}if (bta_sys_cb.ppm_cb) {bta_sys_cb.ppm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr);}
}
根据配置情况调用两个回调函数 bta_sys_cb.prm_cb
和 bta_sys_cb.ppm_cb
,向这些回调函数传递连接关闭的事件信息以及相关的参数,如系统 ID、应用 ID 和对端设备地址。
HID_DevUnplugDevice
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function HID_DevUnplugDevice** Description Unplugs virtual cable from given host** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevUnplugDevice(const RawAddress& addr) {if (hd_cb.device.addr == addr) {hd_cb.device.in_use = FALSE;hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;hd_cb.device.conn.ctrl_cid = 0;hd_cb.device.conn.intr_cid = 0;}return HID_SUCCESS;
}
从给定的主机上拔出虚拟线缆,通过检查设备地址,将设备的使用状态设置为未使用,并将连接状态相关的控制通道 ID 和中断通道 ID 设置为 0。
(*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data)
/********************************************************************************* Function btif_hd_upstreams_evt** Description Executes events in btif context** Returns void*******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {tBTA_HD* p_data = (tBTA_HD*)p_param;log::verbose("event={}", dump_hd_event(event));switch (event) {...case BTA_HD_VC_UNPLUG_EVT:// 检查连接情况并执行清理操作HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda, BTHD_CONN_STATE_DISCONNECTED);// 检查连接情况并执行清理操作if (bta_dm_check_if_only_hd_connected(p_data->conn.bda)) { // 检查指定地址的设备是否仅连接了 HID 配置文件log::verbose("Removing bonding as only HID profile connected");BTA_DmRemoveDevice(p_data->conn.bda);} else {RawAddress* bd_addr = (RawAddress*)&p_data->conn.bda;log::verbose("Only removing HID data as some other profiles connected");btif_hd_remove_device(*bd_addr);}// 调用虚拟线缆拔出回调函数HAL_CBACK(bt_hd_callbacks, vc_unplug_cb);break;...
BTA_HD_VC_UNPLUG_EVT
事件表示蓝牙 HID 设备的虚拟线缆(VC)已拔出。此事件处理主要包括更新连接状态、根据连接情况执行不同的清理操作,以及调用相应的回调函数通知上层。
btif_hd_remove_device
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function btif_hd_remove_device** Description Removes plugged device** Returns void*******************************************************************************/
void btif_hd_remove_device(RawAddress bd_addr) {BTA_HdRemoveDevice(bd_addr);btif_storage_remove_hidd(&bd_addr);
}
BTA_HdRemoveDevice
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function BTA_HdRemoveDevice** Description This function is called when a device is virtually uncabled** Returns void*******************************************************************************/
void BTA_HdRemoveDevice(const RawAddress& addr) {log::verbose("");tBTA_HD_DEVICE_CTRL* p_buf =(tBTA_HD_DEVICE_CTRL*)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL));p_buf->hdr.event = BTA_HD_API_REMOVE_DEVICE_EVT;p_buf->addr = addr;bta_sys_sendmsg(p_buf);
}
在设备的虚拟线缆断开(即设备被视为移除)时被调用。将该消息发送出去,以触发后续的设备移除操作。
bta_hd_better_state_machine(BTA_HD_API_REMOVE_DEVICE_EVT)
packages/modules/Bluetooth/system/bta/hd/bta_hd_main.cccase BTA_HD_CONN_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_TRANSIENT_TO_INIT_ST);bta_hd_disconnect_act();break;case BTA_HD_API_DISCONNECT_EVT:bta_hd_disconnect_act();break;case BTA_HD_API_ADD_DEVICE_EVT:bta_hd_add_device_act(p_data);break;case BTA_HD_API_REMOVE_DEVICE_EVT:bta_hd_remove_device_act(p_data);break;
bta_hd_remove_device_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_remove_device_act** Description** Returns void*******************************************************************************/
void bta_hd_remove_device_act(tBTA_HD_DATA* p_data) {tBTA_HD_DEVICE_CTRL* p_ctrl = (tBTA_HD_DEVICE_CTRL*)p_data;log::verbose("");HID_DevUnplugDevice(p_ctrl->addr);
}
处理移除蓝牙 HID 设备的操作。调用 HID_DevUnplugDevice
函数来执行设备的虚拟线缆拔出操作。HID_DevUnplugDevice前文有分析。
btif_storage_remove_hidd
packages/modules/Bluetooth/system/btif/src/btif_profile_storage.cc
/********************************************************************************* Function btif_storage_remove_hidd** Description Removes hidd bonded device info from nvram** Returns BT_STATUS_SUCCESS*******************************************************************************/
bt_status_t btif_storage_remove_hidd(RawAddress* remote_bd_addr) {btif_config_remove(remote_bd_addr->ToString(),BTIF_STORAGE_KEY_HID_DEVICE_CABLED);return BT_STATUS_SUCCESS;
}
从非易失性随机访问存储器(NVRAM)中移除与 HID已配对设备相关的信息。
情况2:hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL)
bta_hd_better_state_machine(BTA_HD_INT_CLOSE_EVT)
case BTA_HD_CONN_ST:switch (event) {...case BTA_HD_INT_CLOSE_EVT:set_state(BTA_HD_IDLE_ST);bta_hd_close_act(p_data);break;...
bta_hd_close_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_close_act** Description** Returns void*******************************************************************************/
void bta_hd_close_act(tBTA_HD_DATA* p_data) {tBTA_HD_CBACK_DATA* p_cback = (tBTA_HD_CBACK_DATA*)p_data;tBTA_HD cback_data;tBTA_HD_EVT cback_event = BTA_HD_CLOSE_EVT;log::verbose("");// 关闭系统连接bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); // 检查虚拟线缆拔出标志并执行相应操作if (bta_hd_cb.vc_unplug) {bta_hd_cb.vc_unplug = FALSE;HID_DevUnplugDevice(p_cback->addr);cback_event = BTA_HD_VC_UNPLUG_EVT;}// 设置回调数据并调用回调函数cback_data.conn.bda = p_cback->addr;bta_hd_cb.bd_addr = RawAddress::kEmpty;bta_hd_cb.p_cback(cback_event, &cback_data);
}
处理蓝牙 HID 设备的关闭操作。关闭系统连接,根据 bta_hd_cb.vc_unplug
标志决定是否执行设备的虚拟线缆拔出操作,最后调用回调函数通知上层设备关闭或虚拟线缆拔出事件。
bta_hd_cb.p_cback(BTA_HD_CLOSE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cccase BTA_HD_CLOSE_EVT:if (btif_hd_cb.forced_disc) { // 意味着设备是被强制断开连接的RawAddress* addr = (RawAddress*)&p_data->conn.bda;log::warn("remote device was forcefully disconnected");btif_hd_remove_device(*addr);btif_hd_cb.forced_disc = FALSE;break;}// 正常断开情况处理HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda, BTHD_CONN_STATE_DISCONNECTED);break;
BTA_HD_CLOSE_EVT
事件代表蓝牙 HID 设备关闭连接的事件。代码根据 btif_hd_cb.forced_disc
标志的状态,执行不同的操作,要么强制移除设备,要么通知上层设备连接已断开。
正常断开的情况会将设备地址和断开连接的状态 BTHD_CONN_STATE_DISCONNECTED
传递给该回调函数,从而通知上层设备连接已断开。