蓝牙 HID(Human Interface Device)协议中的虚拟拔插(Virtual UnPlug, VUP)是实现设备低功耗管理、状态重置的核心功能。本文基于 Android Bluedroid 源码,从上层应用触发virtual_unplug
接口开始,深入解析 VUP 的全流程实现,涵盖异步事件传递、设备能力适配(虚拟线缆支持与否)、定时器监控、协议栈消息封装、L2CAP 数据发送等关键环节,并对比不同设备的差异化处理策略(虚拟暂停 vs 物理断开),最后总结 VUP 设计的核心目标与工程实践要点。
虚拟拔插流程可分为 6大阶段,涉及BTIF、BTA、HID Host三层模块交互,完整调用链如下:
入口函数: virtual_unplug()
校验模块状态: 检查BTIF_HH_INIT
确保HID主机模块已初始化,拒绝BTIF_HH_DISABLED
状态。
设备查找: 通过btif_hh_find_dev_by_bda
验证目标设备是否存在且已连接。
异步事件投递: 调用btif_transfer_context
将BTIF_HH_VUP_REQ_EVT
事件抛至协议栈线程,确保非阻塞。
事件处理: btif_hh_handle_evt(BTIF_HH_VUP_REQ_EVT)
设备能力判断: 在btif_hh_virtual_unplug
中,根据设备的HID_VIRTUAL_CABLE
属性选择策略:
分支1(支持VUP):
启动3秒超时定时器(btif_hh_start_vup_timer
),防止设备无响应。
标记local_vup=true
,标识本地主动发起的VUP。
调用BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG)
发送控制命令。
分支2(不支持VUP): 直接触发物理断开(BTA_HhClose
),走标准断开流程。
BTA层转发: BTA_HhSendCtrl
→ bta_hh_snd_write_dev
消息封装: 构建tBTA_HH_CMD_DATA
结构,指定传输类型为HID_TRANS_CONTROL
,参数为BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG
。
协议栈投递: 通过bta_sys_sendmsg
将消息发送至BTA任务队列。
BTA任务处理: bta_hh_write_dev_act
参数转换: 将BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG
转换为HID协议定义的HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG
。
底层调用: 执行HID_HostWriteDev
,通过L2CAP通道发送控制数据包。
数据分段与发送: hidh_conn_snd_data
通道选择: 使用Control Channel(CID=0x11)传输VUP指令。
协议头构造: 生成HID_BUILD_HDR(HID_TRANS_CONTROL, HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG)
。
MTU适配: 若数据超MTU则分段发送,标记HID_TRANS_DATAC
后续包。
定时器回调: btif_hh_timer_timeout
超时处理: 3秒内未收到设备响应,强制标记连接断开,回调connection_state_cb
通知上层。
成功响应: 设备返回Handshake包后,更新设备状态为BTHH_CONN_STATE_DISCONNECTED
,清理定时器。
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/*******************************************************************************
*
* Function virtual_unplug
*
* Description Virtual UnPlug (VUP) the specified HID device.
*
* Returns bt_status_t
*
******************************************************************************/
static bt_status_t virtual_unplug(RawAddress* bd_addr) {
// 1. 初始化与模块状态检查
CHECK_BTHH_INIT();
log::verbose("BTHH");
btif_hh_device_t* p_dev;
tAclLinkSpec link_spec;
if (btif_hh_cb.status == BTIF_HH_DISABLED) {
log::error("Error, HH status = {}", btif_hh_cb.status);
return BT_STATUS_FAIL;
}
// 2. 设备查找参数准备
link_spec.addrt.bda = *bd_addr;
// Todo: fill with params received
link_spec.addrt.type = BLE_ADDR_PUBLIC;
link_spec.transport = BT_TRANSPORT_AUTO;
// 3. 设备存在性验证
p_dev = btif_hh_find_dev_by_bda(link_spec);
if (!p_dev) {
log::error("Error, device {} not opened.",
ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));
return BT_STATUS_FAIL;
}
// 4. 异步触发虚拟拔插事件
btif_transfer_context(btif_hh_handle_evt, BTIF_HH_VUP_REQ_EVT,
(char*)&link_spec, sizeof(tAclLinkSpec), NULL);
return BT_STATUS_SUCCESS;
}
验证模块状态和设备存在性后,异步触发虚拟拔插流程,确保操作的非阻塞性。
虚拟拔插(Virtual UnPlug, VUP) 是蓝牙蓝牙 HID(Human Interface Device) 协议中的关键功能,设计目的包括:
模拟物理断开:在不实际断开蓝牙底层连接(ACL 链路)的情况下,暂停 HID 设备的输入输出(如键盘停止发送按键数据)。
低功耗管理:临时停止 HID 数据传输,降低设备和主机的功耗。
状态重置:清除 HID 设备的临时状态(如报告描述符缓存),便于重新初始化。
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/*******************************************************************************
*
* Function btif_hh_handle_evt
*
* Description Switches context for immediate callback
*
* Returns void
*
******************************************************************************/
static void btif_hh_handle_evt(uint16_t event, char* p_param) {
CHECK(p_param != nullptr);
tAclLinkSpec* p_link_spec = (tAclLinkSpec*)p_param;
switch (event) {
case BTIF_HH_CONNECT_REQ_EVT: {
log::debug("Connect request received remote:{}",
ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));
if (btif_hh_connect(p_link_spec) == BT_STATUS_SUCCESS) {
HAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,
BTHH_CONN_STATE_CONNECTING);
} else
HAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,
BTHH_CONN_STATE_DISCONNECTED);
} break;
case BTIF_HH_DISCONNECT_REQ_EVT: {
log::debug("Disconnect request received remote:{}",
ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));
btif_hh_disconnect(p_link_spec);
HAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,
BTHH_CONN_STATE_DISCONNECTING);
} break;
case BTIF_HH_VUP_REQ_EVT: {
log::debug("Virtual unplug request received remote:{}",
ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));
if (btif_hh_virtual_unplug(p_link_spec) != BT_STATUS_SUCCESS) {
log::warn("Unable to virtual unplug device remote:{}",
ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));
}
} break;
default: {
log::warn("Unknown event received:{} remote:{}", event,
ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));
} break;
}
}
BTIF_HH_VUP_REQ_EVT
是蓝牙 HID 主机模块(BTHH)中处理虚拟拔插(VUP)请求的核心事件。其作用是触发实际的虚拟拔插操作,暂停 HID 设备的逻辑连接(保持底层蓝牙链路)。
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/*******************************************************************************
*
* Function btif_hh_virtual_unplug
*
* Description Virtual unplug initiated from the BTIF thread context
* Special handling for HID mouse-
*
* Returns void
*
******************************************************************************/
bt_status_t btif_hh_virtual_unplug(const tAclLinkSpec* link_spec) {
log::verbose("");
// 1. 设备查找与基础校验
btif_hh_device_t* p_dev;
p_dev = btif_hh_find_dev_by_bda(*link_spec);
// 2. 分支 1:支持虚拟线缆(HID_VIRTUAL_CABLE)的设备
if ((p_dev != NULL) && (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED) &&
(p_dev->attr_mask & HID_VIRTUAL_CABLE)) {
log::verbose("Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG for: {}",
ADDRESS_TO_LOGGABLE_CSTR(*link_spec));
/* start the timer */
btif_hh_start_vup_timer(link_spec); // 启动VUP定时器
p_dev->local_vup = true; // 标记本地发起的VUP
BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG); // 发送虚拟拔插控制命令
return BT_STATUS_SUCCESS;
}
// 3. 分支 2:不支持虚拟线缆的设备
else if ((p_dev != NULL) &&
(p_dev->dev_status == BTHH_CONN_STATE_CONNECTED)) {
log::error("Virtual unplug not supported, disconnecting device: {}",
ADDRESS_TO_LOGGABLE_CSTR(*link_spec));
/* start the timer */
btif_hh_start_vup_timer(link_spec);
p_dev->local_vup = true;
BTA_HhClose(p_dev->dev_handle); // 关闭设备连接(物理断开)
return BT_STATUS_SUCCESS;
}
// 4. 分支 3:设备未连接或不存在
else {
log::error("Error, device {} not opened, status = {}",
ADDRESS_TO_LOGGABLE_CSTR(*link_spec), btif_hh_cb.status);
if ((btif_hh_cb.pending_link_spec.addrt.bda == link_spec->addrt.bda) &&
(btif_hh_cb.status == BTIF_HH_DEV_CONNECTING)) {
btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED; // 更新全局状态为断开
btif_hh_cb.pending_link_spec = {}; // 清除挂起的连接参数
// 通知上层设备已断开(通过JNI线程)
/* need to notify up-layer device is disconnected to avoid
* state out of sync with up-layer */
do_in_jni_thread(base::Bind(
[](RawAddress bd_addrcb) {
HAL_CBACK(bt_hh_callbacks, connection_state_cb, &bd_addrcb,
BTHH_CONN_STATE_DISCONNECTED);
},
link_spec->addrt.bda));
}
return BT_STATUS_FAIL;
}
}
根据设备能力选择不同的拔插策略(虚拟暂停或物理断开),并维护状态同步。
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
#define BTIF_TIMEOUT_VUP_MS (3 * 1000)
/*******************************************************************************
*
* Function btif_hh_start_vup_timer
*
* Description start virtual unplug timer
*
* Returns void
******************************************************************************/
static void btif_hh_start_vup_timer(const tAclLinkSpec* link_spec) {
log::verbose("");
btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(*link_spec);
CHECK(p_dev != NULL);
alarm_free(p_dev->vup_timer);
p_dev->vup_timer = alarm_new("btif_hh.vup_timer"); // 创建新定时器实例
// 启动定时器并设置超时回调
alarm_set_on_mloop(p_dev->vup_timer, BTIF_TIMEOUT_VUP_MS,
btif_hh_timer_timeout, p_dev);
}
HID 虚拟拔插(VUP)流程中的超时监控模块,用于为虚拟拔插操作设置超时定时器,确保设备无响应时能及时触发异常处理。
将定时器添加到系统主事件循环(mloop
)中,启动计时。若设备在 3 秒内未完成虚拟拔插操作(如未响应控制命令),则触发超时回调(btif_hh_timer_timeout)。
核心流程可概括为 “设备查找→定时器清理→定时器创建→定时器启动”
packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/*******************************************************************************
*
* Function BTA_HhSendCtrl
*
* Description Send a control command to HID device.
*
* Returns void
*
******************************************************************************/
void BTA_HhSendCtrl(uint8_t dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type) {
bta_hh_snd_write_dev(dev_handle, HID_TRANS_CONTROL, (uint8_t)c_type, 0, 0,
NULL);
}
packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/*******************************************************************************
*
* Function bta_hh_snd_write_dev
*
******************************************************************************/
static void bta_hh_snd_write_dev(uint8_t dev_handle, uint8_t t_type,
uint8_t param, uint16_t data, uint8_t rpt_id,
BT_HDR* p_data) {
tBTA_HH_CMD_DATA* p_buf =
(tBTA_HH_CMD_DATA*)osi_calloc(sizeof(tBTA_HH_CMD_DATA));
p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT;
p_buf->hdr.layer_specific = (uint16_t)dev_handle;
p_buf->t_type = t_type; // 传输类型(如HID控制/中断传输)
p_buf->data = data;
p_buf->param = param;
p_buf->p_data = p_data;
p_buf->rpt_id = rpt_id;
bta_sys_sendmsg(p_buf);
}
将上层应用的写设备请求(如发送 HID 输出报告、控制命令)封装为内部消息。
通过协议栈的消息队列(bta_sys_sendmsg
)异步传递给处理线程,确保操作的非阻塞性。
packages/modules/Bluetooth/system/bta/hh/bta_hh_main.cc
...
case BTA_HH_CONN_ST:
switch (event) {
...
case BTA_HH_API_WRITE_DEV_EVT:
bta_hh_write_dev_act(p_cb, p_data);
break;
...
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/*******************************************************************************
*
* Function bta_hh_write_dev_act
*
* Description Write device action. can be SET/GET/DATA transaction.
*
* Returns void
*
******************************************************************************/
static uint8_t convert_api_sndcmd_param(const tBTA_HH_CMD_DATA& api_sndcmd) {
uint8_t api_sndcmd_param = api_sndcmd.param;
if (api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) {
api_sndcmd_param = (api_sndcmd.param == BTA_HH_PROTO_RPT_MODE)
? HID_PAR_PROTOCOL_REPORT
: HID_PAR_PROTOCOL_BOOT_MODE;
}
return api_sndcmd_param;
}
void bta_hh_write_dev_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
// 1. 事件类型计算
uint16_t event =
(p_data->api_sndcmd.t_type - HID_TRANS_GET_REPORT) + BTA_HH_GET_RPT_EVT;
// 2. LE 设备分支处理
if (p_cb->is_le_device)
bta_hh_le_write_dev_act(p_cb, p_data);
else {
// 3. 参数转换(适配 HID 协议)
/* match up BTE/BTA report/boot mode def */
const uint8_t api_sndcmd_param =
convert_api_sndcmd_param(p_data->api_sndcmd);
// 4. 执行 HID 写操作
tHID_STATUS status = HID_HostWriteDev(p_cb->hid_handle,
p_data->api_sndcmd.t_type,
api_sndcmd_param,
p_data->api_sndcmd.data,
p_data->api_sndcmd.rpt_id,
p_data->api_sndcmd.p_data);
// 5. 错误处理
if (status != HID_SUCCESS) {
log::error("HID_HostWriteDev Error, status:{}", status);
if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL &&
p_data->api_sndcmd.t_type != HID_TRANS_DATA) {
BT_HDR cbhdr = {
.event = BTA_HH_GET_RPT_EVT,
.len = 0,
.offset = 0,
.layer_specific = 0,
};
tBTA_HH cbdata = {
.hs_data = {
.status = BTA_HH_ERR,
.handle = p_cb->hid_handle,
.rsp_data = {
.p_rpt_data = &cbhdr,
},
},
};
(*bta_hh_cb.p_cback)(event, &cbdata);
} else if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) {
tBTA_HH cbdata = {
.dev_status = {
.status = BTA_HH_ERR,
.handle = p_cb->hid_handle,
},
};
(*bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, &cbdata);
} else {
log::error(
"skipped executing callback in hid host error handling. command "
"type:{}, param:{}",
p_data->api_sndcmd.t_type, p_data->api_sndcmd.param);
}
}
// 6. 成功处理
else {
switch (p_data->api_sndcmd.t_type) {
case HID_TRANS_SET_PROTOCOL:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_GET_REPORT:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_SET_REPORT:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_GET_PROTOCOL:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_GET_IDLE:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_SET_IDLE: /* set w4_handsk event name for callback
function use */
p_cb->w4_evt = event;
break;
case HID_TRANS_DATA: /* output report */
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case HID_TRANS_CONTROL:
/* no handshake event will be generated */
/* if VC_UNPLUG is issued, set flag */
if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG)
p_cb->vp = true;
break;
/* currently not expected */
case HID_TRANS_DATAC:
default:
log::verbose("cmd type={}", p_data->api_sndcmd.t_type);
break;
}
/* if not control type transaction, notify PM for energy control */
if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) {
/* inform PM for mode change */
bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);
bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);
} else if (api_sndcmd_param == BTA_HH_CTRL_SUSPEND) {
bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);
} else if (api_sndcmd_param == BTA_HH_CTRL_EXIT_SUSPEND) {
bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);
}
}
}
return;
}
负责处理 HID 设备的控制 / 数据传输(如设置协议模式、发送输出报告),并与上层应用及电源管理模块同步状态。整体流程可分为分支判断→参数转换→操作执行→结果处理四大阶段。
packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/*******************************************************************************
*
* Function HID_HostWriteDev
*
* Description This function is called when the host has a report to send.
*
* report_id: is only used on GET_REPORT transaction if is
* specified. only valid when it is non-zero.
*
* Returns void
*
******************************************************************************/
tHID_STATUS HID_HostWriteDev(uint8_t dev_handle, uint8_t t_type, uint8_t param,
uint16_t data, uint8_t report_id, BT_HDR* pbuf) {
tHID_STATUS status = HID_SUCCESS;
// 1. 模块注册状态校验
if (!hh_cb.reg_flag) {
log::error("HID_ERR_NOT_REGISTERED");
status = HID_ERR_NOT_REGISTERED;
}
// 2. 设备句柄有效性校验
if ((dev_handle >= HID_HOST_MAX_DEVICES) ||
(!hh_cb.devices[dev_handle].in_use)) {
log::error("HID_ERR_INVALID_PARAM");
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_INVALID_PARAM_AT_HOST_WRITE_DEV,
1);
status = HID_ERR_INVALID_PARAM;
}
// 3. 设备连接状态校验
else if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) {
log::error("HID_ERR_NO_CONNECTION dev_handle {}", dev_handle);
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_NO_CONNECTION_AT_HOST_WRITE_DEV,
1);
status = HID_ERR_NO_CONNECTION;
}
// 4. 数据发送与错误处理
if (status != HID_SUCCESS)
osi_free(pbuf);
else
status =
hidh_conn_snd_data(dev_handle, t_type, param, data, report_id, pbuf);
return status;
}
负责校验操作合法性(模块注册状态、设备有效性、连接状态),并最终通过底层通道发送数据。
packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
/*******************************************************************************
*
* Function hidh_conn_snd_data
*
* Description This function is sends out data.
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type,
uint8_t param, uint16_t data, uint8_t report_id,
BT_HDR* buf) {
tHID_CONN* p_hcon = &hh_cb.devices[dhandle].conn;
BT_HDR* p_buf;
uint8_t* p_out;
uint16_t bytes_copied;
bool seg_req = false;
uint16_t data_size;
uint16_t cid;
uint16_t buf_size;
uint8_t use_data = 0;
bool blank_datc = false;
// 1. 连接状态校验
// 校验ACL连接是否存在(传统蓝牙BR/EDR)
if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr,
BT_TRANSPORT_BR_EDR)) {
osi_free(buf);
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_NO_CONNECTION_AT_SEND_DATA,
1);
return HID_ERR_NO_CONNECTION;
}
// 校验连接是否拥塞(避免发送数据导致队列堆积)
if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {
osi_free(buf);
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_CONGESTED_AT_FLAG_CHECK,
1);
return HID_ERR_CONGESTED;
}
// 2. 传输类型路由
switch (trans_type) {
case HID_TRANS_CONTROL: // 控制传输(如设置协议)
case HID_TRANS_GET_REPORT: // 获取报告
case HID_TRANS_SET_REPORT:
case HID_TRANS_GET_PROTOCOL:
case HID_TRANS_SET_PROTOCOL:
case HID_TRANS_GET_IDLE:
case HID_TRANS_SET_IDLE:
cid = p_hcon->ctrl_cid;
buf_size = HID_CONTROL_BUF_SIZE;
break;
case HID_TRANS_DATA: // 中断传输(如输出报告)
cid = p_hcon->intr_cid;
buf_size = HID_INTERRUPT_BUF_SIZE;
break;
default:
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_INVALID_PARAM_AT_SEND_DATA,
1);
return (HID_ERR_INVALID_PARAM);
}
if (trans_type == HID_TRANS_SET_IDLE)
use_data = 1;
else if ((trans_type == HID_TRANS_GET_REPORT) && (param & 0x08))
use_data = 2;
// 3. 数据分段处理
do {
if (buf == NULL || blank_datc) {
p_buf = (BT_HDR*)osi_malloc(buf_size);
p_buf->offset = L2CAP_MIN_OFFSET;
seg_req = false;
data_size = 0;
bytes_copied = 0;
blank_datc = false;
} else if ((buf->len > (p_hcon->rem_mtu_size - 1))) { // 数据长度超过对端 MTU,需分段
p_buf = (BT_HDR*)osi_malloc(buf_size);
p_buf->offset = L2CAP_MIN_OFFSET;
seg_req = true;
data_size = buf->len;
bytes_copied = p_hcon->rem_mtu_size - 1;
} else {
p_buf = buf;
p_buf->offset -= 1;
seg_req = false;
data_size = buf->len;
bytes_copied = buf->len;
}
p_out = (uint8_t*)(p_buf + 1) + p_buf->offset;
*p_out++ = HID_BUILD_HDR(trans_type, param);
/* If report ID required for this device */
if ((trans_type == HID_TRANS_GET_REPORT) && (report_id != 0)) {
*p_out = report_id;
data_size = bytes_copied = 1;
}
if (seg_req) {
memcpy(p_out, (((uint8_t*)(buf + 1)) + buf->offset), bytes_copied);
buf->offset += bytes_copied;
buf->len -= bytes_copied;
} else if (use_data == 1) {
*(p_out + bytes_copied) = data & 0xff;
} else if (use_data == 2) {
*(p_out + bytes_copied) = data & 0xff;
*(p_out + bytes_copied + 1) = (data >> 8) & 0xff;
}
p_buf->len = bytes_copied + 1 + use_data;
data_size -= bytes_copied;
// 4. L2CAP 数据发送
/* Send the buffer through L2CAP */
if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) ||
(!L2CA_DataWrite(cid, p_buf))) {
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDH_ERR_CONGESTED_AT_SEND_DATA,
1);
return (HID_ERR_CONGESTED);
}
if (data_size)
trans_type = HID_TRANS_DATAC;
else if (bytes_copied == (p_hcon->rem_mtu_size - 1)) {
trans_type = HID_TRANS_DATAC;
blank_datc = true;
}
} while ((data_size != 0) || blank_datc);
return (HID_SUCCESS);
}
HID 主机协议栈中数据发送的核心执行单元,负责将 HID 消息(控制命令、报告数据等)通过 L2CAP 通道发送至设备。其核心逻辑是处理数据分段、构建 HID 协议头,并与底层 L2CAP 交互完成数据传输。
packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/*******************************************************************************
*
* Function BTA_HhClose
*
* Description Disconnect a connection.
*
* Returns void
*
******************************************************************************/
void BTA_HhClose(uint8_t dev_handle) {
BT_HDR* p_buf = (BT_HDR*)osi_calloc(sizeof(BT_HDR));
p_buf->event = BTA_HH_API_CLOSE_EVT;
p_buf->layer_specific = (uint16_t)dev_handle;
bta_sys_sendmsg(p_buf);
}
通过异步消息机制将 “关闭连接” 请求传递给协议栈后台线程处理。HID 主机模块(BTA_HH)主动断开设备连接的流程同【Bluedroid】蓝牙HID Host disconnect流程源码解析-CSDN博客
BTIF层:负责与Java层的JNI交互,处理异步事件转发。
BTA层:协议栈核心逻辑,处理设备状态机与控制命令封装。
HID Host层:实现HID协议细节,管理L2CAP通道与数据传输。
4.2 异步非阻塞设计
通过btif_transfer_context
与bta_sys_sendmsg
实现跨线程通信,避免阻塞UI或系统主线程。
对支持VUP的设备:仅断开逻辑连接,保持ACL链路,便于快速重连。
对老旧设备:降级为物理断开,牺牲效率保证功能可用性。
双保险机制:定时器兜底 + 设备响应确认,防止协议栈状态死锁。
拥塞控制:L2CAP层检查HID_CONN_FLAGS_CONGESTED
标志,避免数据堆积。
虚拟拔插后,HID主机与设备可进入Sniff或Hold模式,减少射频活动,延长电池寿命。