本文基于Android蓝牙协议栈代码,深度解析HID设备(如键盘、鼠标)从应用层发送输入报告到主机设备的完整流程,涵盖数据封装、通道选择、L2CAP传输、电源管理 四大核心模块。通过函数调用链(send_report
→ BTA_HdSendReport
→ HID_DevSendReport
→ hidd_conn_send_data
)的逐层分析,重点剖析以下机制:
-
中断通道与控制通道的动态选择策略
-
蓝牙协议栈异步消息队列与状态机协作
-
BUSY/IDLE状态下的电源模式动态调整
-
低功耗模式(Sniff/Hold/Park)的冲突仲裁机制
一、流程概述
1.1 数据发送主流程
①应用层触发
send_report()
:接收上层输入数据,校验HID服务状态(注册/启用),将BTHD_REPORT_TYPE_INTRDATA
强制映射为中断通道输入报告。
②协议栈封装
BTA_HdSendReport()
:检查MTU大小,通过osi_malloc
分配消息缓冲区,构建tBTA_HD_SEND_REPORT
结构体,拷贝数据并投递到系统消息队列。
③事件路由
bta_hd_hdl_event()
:通过BTA_HD_API_SEND_REPORT_EVT
事件触发状态机跳转至BTA_HD_IDLE_ST
,调用动作函数bta_hd_send_report_act
。
④通道决策
bta_hd_send_report_act()
:根据use_intr
标志选择通道(HID_CHANNEL_INTR
或HID_CHANNEL_CTRL
),处理报告ID兼容性(Boot Mode适配)。
⑤物理层传输
-
HID_DevSendReport()
:验证通道与报告类型合法性(中断通道仅允许输入报告),调用hidd_conn_send_data
。 -
hidd_conn_send_data()
:封装L2CAP数据包,处理拥塞检测(HID_CONN_FLAGS_CONGESTED
),动态分配缓冲区,通过L2CA_DataWrite
发送。
1.2 电源管理协同流程
①BUSY状态标记
bta_sys_busy()
:通知电源管理模块(PPM/PRM)连接进入繁忙状态,暂停低功耗模式计时器。
②动态策略调整
bta_dm_pm_cback()
:针对BTA_SYS_CONN_BUSY
状态:
-
为A2DP音频流设置专用SSR4参数(最大延迟优化)
-
更新服务状态为
BTA_DM_PM_NEW_REQ
,重置失败计数器 -
跳过常规SSR设置,维持活跃连接状态
③模式仲裁与切换
bta_dm_pm_set_mode()
:综合各服务的电源需求(allowed_modes
),通过优先级仲裁选择最终模式:
-
冲突解决:若嗅探模式(SNIFF)被部分服务禁止,回退至PARK或保持ACTIVE
-
定时器管理 :为延迟执行的模式设置分级超时(如
BTA_DM_PM_SNIFF_TIMER_3
)
④IDLE状态恢复
bta_sys_idle()
:通知系统恢复空闲状态,重新评估低功耗条件,可能触发BTA_DM_PM_SSR0
参数重置。
1.3 关键机制协作
-
音频优化:为音频子系统提供专用 SSR 参数,确保低延迟传输
-
冲突处理:当多服务对电源模式有冲突需求时,选择兼容性最高的模式
-
失败恢复:记录失败的电源模式尝试,避免重复失败
-
动态调整:根据实时连接状态和服务需求,动态调整电源策略
二、源码解析
send_report
cpp
/packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
*
* Function send_report
*
* Description Sends Reports to hid host
*
* Returns bt_status_t
*
******************************************************************************/
static bt_status_t send_report(bthd_report_type_t type, uint8_t id,
uint16_t len, uint8_t* p_data) {
tBTA_HD_REPORT report;
log::verbose("type={} id={} len={}", type, id, len);
// 状态检查
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;
}
// 报告类型处理
if (type == BTHD_REPORT_TYPE_INTRDATA) {
report.type = BTHD_REPORT_TYPE_INPUT;
report.use_intr = TRUE;
} else {
report.type = (type & 0x03);
report.use_intr = FALSE;
}
// 构建报告结构体
report.id = id;
report.len = len;
report.p_data = p_data;
// 调用底层发送函数
BTA_HdSendReport(&report);
return BT_STATUS_SUCCESS;
}
用于向 **HID (Human Interface Device)**主机发送报告,将上层输入数据封装成标准HID报告格式并通过蓝牙传输。
BTA_HdSendReport
cpp
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/*******************************************************************************
*
* Function BTA_HdSendReport
*
* Description This function is called when report is to be sent
*
* Returns void
*
******************************************************************************/
void BTA_HdSendReport(tBTA_HD_REPORT* p_report) {
log::verbose("");
if (p_report->len > BTA_HD_REPORT_LEN) {
log::warn(
"report len ({}) > MTU len ({}), can't send report. Increase value of "
"HID_DEV_MTU_SIZE to send larger reports",
p_report->len, BTA_HD_REPORT_LEN);
return;
}
tBTA_HD_SEND_REPORT* p_buf =
(tBTA_HD_SEND_REPORT*)osi_malloc(sizeof(tBTA_HD_SEND_REPORT));
p_buf->hdr.event = BTA_HD_API_SEND_REPORT_EVT;
p_buf->use_intr = p_report->use_intr;
p_buf->type = p_report->type;
p_buf->id = p_report->id;
p_buf->len = p_report->len;
memcpy(p_buf->data, p_report->p_data, p_report->len);
bta_sys_sendmsg(p_buf); // 发送消息到系统队列
}
将应用层的 HID 报告转换为蓝牙协议可传输的格式,并通过消息队列机制实现异步发送。

bta_hd_hdl_event(BTA_HD_API_SEND_REPORT_EVT)
cpp
packages/modules/Bluetooth/system/bta/hd/bta_hd_main.cc
/*******************************************************************************
*
* Function bta_hd_hdl_event
*
* Description HID device main event handling function.
*
* Returns void
*
******************************************************************************/
bool bta_hd_hdl_event(const BT_HDR_RIGID* p_msg) {
log::verbose("p_msg->event={}", p_msg->event);
switch (p_msg->event) {
case BTA_HD_API_ENABLE_EVT:
bta_hd_api_enable((tBTA_HD_DATA*)p_msg);
break;
case BTA_HD_API_DISABLE_EVT:
if (bta_hd_cb.state == BTA_HD_CONN_ST) {
log::warn("host connected, disconnect before disabling");
// unregister (and disconnect)
bta_hd_cb.disable_w4_close = TRUE;
bta_hd_better_state_machine(BTA_HD_API_UNREGISTER_APP_EVT,
(tBTA_HD_DATA*)p_msg);
} else {
bta_hd_api_disable();
}
break;
default:
bta_hd_better_state_machine(p_msg->event, (tBTA_HD_DATA*)p_msg);
}
return (TRUE);
}
事件被路由到 bta_hd_better_state_machine
处理。
bta_hd_better_state_machine(BTA_HD_API_SEND_REPORT_EVT)
cpp
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_IDLE_ST:
switch (event) {
...
case BTA_HD_API_SEND_REPORT_EVT:
bta_hd_send_report_act(p_data);
break;
...
bta_hd_send_report_act
cpp
/packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/*******************************************************************************
*
* Function bta_hd_send_report_act
*
* Description Sends report
*
* Returns void
*
******************************************************************************/
void bta_hd_send_report_act(tBTA_HD_DATA* p_data) {
tBTA_HD_SEND_REPORT* p_report = (tBTA_HD_SEND_REPORT*)p_data;
uint8_t channel;
uint8_t report_id;
log::verbose("");
// 通道选择
channel = p_report->use_intr ? HID_CHANNEL_INTR : HID_CHANNEL_CTRL;
report_id =
(bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) ? p_report->id : 0x00;
HID_DevSendReport(channel, p_report->type, report_id, p_report->len,
p_report->data);
/* trigger PM */
bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); // 通知系统蓝牙 HID 模块处于忙碌状态(发送数据)
bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); // 立即通知系统模块恢复空闲状态
}
负责实际发送 HID 报告。接收之前封装的报告数据,选择适当的通道,并调用底层 HID 驱动接口完成数据传输。
根据 use_intr
标志决定使用中断通道还是控制通道
-
中断通道 (
HID_CHANNEL_INTR
**):**用于实时数据传输(如键盘、鼠标输入) -
控制通道 (
HID_CHANNEL_CTRL
):用于非实时控制和配置数据
HID_DevSendReport
cpp
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/*******************************************************************************
*
* Function HID_DevSendReport
*
* Description Sends report
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id,
uint16_t len, uint8_t* p_data) {
log::verbose("channel={} type={} id={} len={}", channel, type, id, len);
if (channel == HID_CHANNEL_CTRL) {
// 控制通道可用于发送任何类型的 HID 报告(输入、输出、特征)
return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len,
p_data);
}
if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) {
// on INTR we can only send INPUT
// 中断通道仅允许发送输入报告,因为中断通道专为实时数据(如键盘、鼠标输入)设计
return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA,
HID_PAR_REP_TYPE_INPUT, id, len, p_data);
}
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_INVALID_PARAM_SEND_REPORT,
1);
return HID_ERR_INVALID_PARAM;
}
将 HID 报告通过指定通道发送到主机设备。
hidd_conn_send_data
cpp
/packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/*******************************************************************************
*
* Function hidd_conn_send_data
*
* Description Sends data to host
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type,
uint8_t param, uint8_t data, uint16_t len,
uint8_t* p_data) {
BT_HDR* p_buf;
uint8_t* p_out;
uint16_t cid;
uint16_t buf_size;
log::verbose("channel({}), msg_type({}), len({})", channel, msg_type, len);
tHID_CONN* p_hcon = &hd_cb.device.conn;
// 1. 拥塞检查
if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_CONGESTED_AT_FLAG_CHECK,
1);
return HID_ERR_CONGESTED;
}
// 2. CID 和缓冲区大小确定
// 根据消息类型和通道确定 L2CAP 通道 ID (CID)
// 为控制和中断通道分配不同的缓冲区大小
switch (msg_type) {
case HID_TRANS_HANDSHAKE:
case HID_TRANS_CONTROL:
cid = p_hcon->ctrl_cid;
buf_size = HID_CONTROL_BUF_SIZE;
break;
case HID_TRANS_DATA:
if (channel == HID_CHANNEL_CTRL) {
cid = p_hcon->ctrl_cid;
buf_size = HID_CONTROL_BUF_SIZE;
} else {
cid = p_hcon->intr_cid;
buf_size = HID_INTERRUPT_BUF_SIZE;
}
break;
default:
log_counter_metrics(
android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_INVALID_PARAM,
1);
return (HID_ERR_INVALID_PARAM);
}
// 3. 内存分配与数据封装
p_buf = (BT_HDR*)osi_malloc(buf_size);
if (p_buf == NULL) {
log_counter_metrics(
android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_RESOURCES, 1);
return (HID_ERR_NO_RESOURCES);
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_out = (uint8_t*)(p_buf + 1) + p_buf->offset;
*p_out = HID_BUILD_HDR(msg_type, param); // 构建 HID 头
p_out++;
p_buf->len = 1; // start with header only
// add report id prefix only if non-zero (which is reserved)
// 添加报告 ID(如果非零)
if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) {
*p_out = data; // report_id
p_out++;
p_buf->len++;
}
// 添加数据
if (len > 0 && p_data != NULL) {
memcpy(p_out, p_data, len);
p_buf->len += len;
}
// check if connected
// 4. 连接状态检查
if (hd_cb.device.state != HIDD_DEV_CONNECTED) {
// for DATA on intr we hold transfer and try to reconnect
// 如果未连接但发送的是中断通道数据,保存数据并尝试重新连接
if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) {
// drop previous data, we do not queue it for now
if (hd_cb.pending_data) {
osi_free(hd_cb.pending_data);
}
hd_cb.pending_data = p_buf;
if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) {
hidd_conn_initiate();
}
return HID_SUCCESS;
}
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_NO_CONNECTION_AT_SEND_DATA,
1);
return HID_ERR_NO_CONNECTION;
}
log::verbose("report sent");
// 5. L2CAP 数据发送
if (!L2CA_DataWrite(cid, p_buf)) {
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_CONGESTED_AT_DATA_WRITE,
1);
return (HID_ERR_CONGESTED);
}
return (HID_SUCCESS);
}
将 HID 数据封装为 L2CAP 数据包并发送到主机设备。
bta_sys_busy
cpp
/packages/modules/Bluetooth/system/bta/sys/bta_sys_conn.cc
/*******************************************************************************
*
* Function bta_sys_busy
*
* Description Called by BTA subsystems to indicate that the connection to
* peer device is busy
*
* Returns void
*
******************************************************************************/
void bta_sys_busy(tBTA_SYS_ID id, uint8_t app_id, const RawAddress& peer_addr) {
if (bta_sys_cb.prm_cb) { // 通知电源管理模块(PRM)
bta_sys_cb.prm_cb(BTA_SYS_CONN_BUSY, id, app_id, peer_addr);
}
if (bta_sys_cb.ppm_cb) { // 通知策略管理模块(PPM)
bta_sys_cb.ppm_cb(BTA_SYS_CONN_BUSY, id, app_id, peer_addr);
}
}
通知电源管理和连接管理模块,当前与对等设备的连接正处于繁忙状态,暂时不宜进入低功耗模式。

bta_dm_rm_cback(BTA_SYS_CONN_BUSY)
cpp
packages/modules/Bluetooth/system/bta/dm/bta_dm_act.cc
*******************************************************************************
*
* Function bta_dm_rm_cback
*
* Description Role management callback from sys
*
*
* Returns void
*
******************************************************************************/
static void bta_dm_rm_cback(tBTA_SYS_CONN_STATUS status, tBTA_SYS_ID id,
uint8_t app_id, const RawAddress& peer_addr) {
uint8_t j;
tBTA_PREF_ROLES role;
tBTA_DM_PEER_DEVICE* p_dev;
log::debug("BTA Role management callback count:{} status:{} peer:{}",
bta_dm_cb.cur_av_count, bta_sys_conn_status_text(status),
ADDRESS_TO_LOGGABLE_CSTR(peer_addr));
// 1. 设备查找与连接状态更新
p_dev = bta_dm_find_peer_device(peer_addr);
if (status == BTA_SYS_CONN_OPEN) {
if (p_dev) {
/* Do not set to connected if we are in the middle of unpairing. When AV
* stream is
* started it fakes out a SYS_CONN_OPEN to potentially trigger a role
* switch command.
* But this should not be done if we are in the middle of unpairing.
*/
if (p_dev->conn_state != BTA_DM_UNPAIRING)
p_dev->conn_state = BTA_DM_CONNECTED;
// 2. 首选角色管理
for (j = 1; j <= p_bta_dm_rm_cfg[0].app_id; j++) {
if (((p_bta_dm_rm_cfg[j].app_id == app_id) ||
(p_bta_dm_rm_cfg[j].app_id == BTA_ALL_APP_ID)) &&
(p_bta_dm_rm_cfg[j].id == id)) {
ASSERT_LOG(p_bta_dm_rm_cfg[j].cfg <= BTA_PERIPHERAL_ROLE_ONLY,
"Passing illegal preferred role:0x%02x [0x%02x<=>0x%02x]",
p_bta_dm_rm_cfg[j].cfg, BTA_ANY_ROLE,
BTA_PERIPHERAL_ROLE_ONLY);
role = static_cast<tBTA_PREF_ROLES>(p_bta_dm_rm_cfg[j].cfg);
if (role > p_dev->pref_role) p_dev->pref_role = role;
break;
}
}
}
}
// 3. 音频连接状态管理
if (BTA_ID_AV == id) {
if (status == BTA_SYS_CONN_BUSY) { // 连接繁忙时标记音频活跃并更新计数
if (p_dev) p_dev->set_av_active();
/* AV calls bta_sys_conn_open with the A2DP stream count as app_id */
if (BTA_ID_AV == id) bta_dm_cb.cur_av_count = bta_dm_get_av_count();
} else if (status == BTA_SYS_CONN_IDLE) { // 连接空闲时重置音频活跃状态并更新计数
if (p_dev) p_dev->reset_av_active();
/* get cur_av_count from connected services */
if (BTA_ID_AV == id) bta_dm_cb.cur_av_count = bta_dm_get_av_count();
}
}
// 4. 角色调整
/* Don't adjust roles for each busy/idle state transition to avoid
excessive switch requests when individual profile busy/idle status
changes */
if ((status != BTA_SYS_CONN_BUSY) && (status != BTA_SYS_CONN_IDLE))
bta_dm_adjust_roles(false);
}
处理角色管理回调,用于响应系统连接状态变化,更新对等设备的连接状态和首选角色,并根据音频(AV)连接状态进行相应管理。

bta_dm_pm_cback(BTA_SYS_CONN_BUSY)
cpp
packages/modules/Bluetooth/system/bta/dm/bta_dm_pm.cc
/*******************************************************************************
*
* Function bta_dm_pm_cback
*
* Description Conn change callback from sys for low power management
*
*
* Returns void
*
******************************************************************************/
static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, const tBTA_SYS_ID id,
uint8_t app_id, const RawAddress& peer_addr) {
uint8_t i, j;
tBTA_DM_PEER_DEVICE* p_dev;
tBTA_DM_PM_REQ pm_req = BTA_DM_PM_NEW_REQ;
log::debug("Power management callback status:{}[{}] id:{}[{}], app:{}",
bta_sys_conn_status_text(status), status, BtaIdSysText(id), id,
app_id);
// 1. 配置查找与过滤
/* find if there is an power mode entry for the service */
for (i = 1; i <= p_bta_dm_pm_cfg[0].app_id; i++) {
if ((p_bta_dm_pm_cfg[i].id == id) &&
((p_bta_dm_pm_cfg[i].app_id == BTA_ALL_APP_ID) ||
(p_bta_dm_pm_cfg[i].app_id == app_id)))
break;
}
/* if no entries are there for the app_id and subsystem in
* get_bta_dm_pm_spec()*/
if (i > p_bta_dm_pm_cfg[0].app_id) {
log::debug(
"Ignoring power management callback as no service entries exist");
return;
}
log::debug("Stopped all timers for service to device:{} id:{}[{}]",
ADDRESS_TO_LOGGABLE_CSTR(peer_addr), BtaIdSysText(id), id);
// 2. 定时器管理
// 停止与该服务相关的所有定时器
// 防止旧的定时任务干扰新的电源管理决策
bta_dm_pm_stop_timer_by_srvc_id(peer_addr, static_cast<uint8_t>(id));
p_dev = bta_dm_find_peer_device(peer_addr);
if (p_dev) {
log::debug("Device info:{}", p_dev->info_text());
} else {
log::error("Unable to find peer device...yet soldiering on...");
}
// 3. SSR (Sniff Subrating) 参数设置
/* set SSR parameters on SYS CONN OPEN */
int index = BTA_DM_PM_SSR0;
if ((BTA_SYS_CONN_OPEN == status) && p_dev && (p_dev->is_ssr_active())) {
index = get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx].ssr;
} else if (BTA_ID_AV == id) { // 音频子系统在繁忙状态下使用特殊的 SSR 设置
if (BTA_SYS_CONN_BUSY == status) {
/* set SSR4 for A2DP on SYS CONN BUSY */
index = BTA_DM_PM_SSR4;
} else if (BTA_SYS_CONN_IDLE == status) {
index = get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx].ssr;
}
}
/* if no action for the event */
if (get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx]
.actn_tbl[status][0]
.power_mode == BTA_DM_PM_NO_ACTION) {
if (BTA_DM_PM_SSR0 == index) /* and do not need to set SSR, return. */
return;
}
// 4. 连接服务管理
for (j = 0; j < bta_dm_conn_srvcs.count; j++) {
/* check if an entry already present */
if ((bta_dm_conn_srvcs.conn_srvc[j].id == id) &&
(bta_dm_conn_srvcs.conn_srvc[j].app_id == app_id) &&
bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr == peer_addr) {
bta_dm_conn_srvcs.conn_srvc[j].new_request = true;
break;
}
}
/* if subsystem has no more preference on the power mode remove
the cb */
if (get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx]
.actn_tbl[status][0]
.power_mode == BTA_DM_PM_NO_PREF) {
if (j != bta_dm_conn_srvcs.count) {
bta_dm_conn_srvcs.count--;
for (; j < bta_dm_conn_srvcs.count; j++) {
memcpy(&bta_dm_conn_srvcs.conn_srvc[j],
&bta_dm_conn_srvcs.conn_srvc[j + 1],
sizeof(bta_dm_conn_srvcs.conn_srvc[j]));
}
} else {
log::warn("bta_dm_act no entry for connected service cbs");
return;
}
} else if (j == bta_dm_conn_srvcs.count) {
/* check if we have more connected service that cbs */
if (bta_dm_conn_srvcs.count == BTA_DM_NUM_CONN_SRVS) {
log::warn("bta_dm_act no more connected service cbs");
return;
}
/* fill in a new cb */
bta_dm_conn_srvcs.conn_srvc[j].id = id;
bta_dm_conn_srvcs.conn_srvc[j].app_id = app_id;
bta_dm_conn_srvcs.conn_srvc[j].new_request = true;
bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr = peer_addr;
log::info("New connection service:{}[{}] app_id:{}", BtaIdSysText(id), id,
app_id);
bta_dm_conn_srvcs.count++;
bta_dm_conn_srvcs.conn_srvc[j].state = status; // 更新为 BUSY 状态
} else {
/* no service is added or removed. only updating status. */
bta_dm_conn_srvcs.conn_srvc[j].state = status;
}
/* stop timer */
// 停止与该设备相关的所有电源管理定时器,若仍有其他服务连接,则标记需要重启定时器
bta_dm_pm_stop_timer(peer_addr);
if (bta_dm_conn_srvcs.count > 0) {
pm_req = BTA_DM_PM_RESTART;
log::verbose(
"bta_dm_pm_stop_timer for current service, restart other service "
"timers: count = {}",
bta_dm_conn_srvcs.count);
}
if (p_dev) {
p_dev->pm_mode_attempted = 0;
p_dev->pm_mode_failed = 0;
}
// 5. SSR 应用与电源模式设置
if (p_bta_dm_ssr_spec[index].max_lat || index == BTA_DM_PM_SSR_HH) {
/* do not perform ssr for AVDTP start */
// 对于音频子系统在 BUSY 状态下,不执行 SSR 参数设置(已在前面单独处理)
if (id != BTA_ID_AV || status != BTA_SYS_CONN_BUSY) {
bta_dm_pm_ssr(peer_addr, index);
} else {
log::debug("Do not perform SSR when AVDTP start");
}
} else {
uint8_t* p = NULL;
if (bluetooth::shim::GetController()->SupportsSniffSubrating() &&
((NULL != (p = get_btm_client_interface().peer.BTM_ReadRemoteFeatures(
peer_addr))) &&
HCI_SNIFF_SUB_RATE_SUPPORTED(p)) &&
(index == BTA_DM_PM_SSR0)) {
if (status == BTA_SYS_SCO_OPEN) {
log::verbose("SCO inactive, reset SSR to zero");
get_btm_client_interface().link_policy.BTM_SetSsrParams(peer_addr, 0, 0,
0);
} else if (status == BTA_SYS_SCO_CLOSE) {
log::verbose("SCO active, back to old SSR");
bta_dm_pm_ssr(peer_addr, BTA_DM_PM_SSR0);
}
}
}
bta_dm_pm_set_mode(peer_addr, BTA_DM_PM_NO_ACTION, pm_req); // 设置最终的电源模式
}
处理电源管理回调,根据系统连接状态变化,管理蓝牙设备的低功耗模式,包括**嗅探模式(Sniff Mode)、保持模式(Hold Mode)和停止模式(Park Mode)**等。通过合理配置和动态调整低功耗模式,在保证蓝牙连接质量的同时,最大限度地降低设备功耗,延长电池续航时间。
BTA_SYS_CONN_BUSY
状态主要针对设备连接繁忙时的电源管理策略调整。这个状态表示设备正在进行数据传输,需要确保连接稳定性和低延迟,因此电源管理策略会相应调整。当接收到 BTA_SYS_CONN_BUSY
状态时,主要执行以下操作:
-
音频子系统优化:为音频流(如 A2DP)设置专门的 SSR 参数(SSR4),确保低延迟传输。
-
服务状态更新:记录服务的繁忙状态,用于后续电源策略决策。
-
定时器管理:暂停可能导致低功耗模式的定时器,维持连接活跃状态。
-
避免参数冲突:跳过对音频子系统的常规 SSR 参数设置,使用专用参数。
-
维持连接状态 :通过
bta_dm_pm_set_mode
确保在数据传输期间不进入低功耗模式。
bta_dm_pm_set_mode(BTA_DM_PM_NO_ACTION)
cpp
/*******************************************************************************
*
* Function bta_dm_pm_set_mode
*
* Description Set the power mode for the device
*
*
* Returns void
*
******************************************************************************/
static void bta_dm_pm_set_mode(const RawAddress& peer_addr,
tBTA_DM_PM_ACTION pm_request,
tBTA_DM_PM_REQ pm_req) {
tBTA_DM_PM_ACTION pm_action = BTA_DM_PM_NO_ACTION;
uint64_t timeout_ms = 0;
uint8_t i, j;
tBTA_DM_PM_ACTION failed_pm = 0;
tBTA_DM_PEER_DEVICE* p_peer_device = NULL;
tBTA_DM_PM_ACTION allowed_modes = 0;
tBTA_DM_PM_ACTION pref_modes = 0;
const tBTA_DM_PM_CFG* p_pm_cfg;
const tBTA_DM_PM_SPEC* p_pm_spec;
const tBTA_DM_PM_ACTN* p_act0;
const tBTA_DM_PM_ACTN* p_act1;
tBTA_DM_SRVCS* p_srvcs = NULL;
bool timer_started = false;
uint8_t timer_idx, available_timer = BTA_DM_PM_MODE_TIMER_MAX;
uint64_t remaining_ms = 0;
if (!bta_dm_cb.device_list.count) {
log::info("Device list count is zero");
return;
}
// 1. 设备与失败模式检查
/* see if any attempt to put device in low power mode failed */
p_peer_device = bta_dm_find_peer_device(peer_addr);
/* if no peer device found return */
if (p_peer_device == NULL) {
log::info("No peer device found");
return;
}
failed_pm = p_peer_device->pm_mode_failed; // 获取之前尝试过但失败的电源模式
// 2 .遍历连接服务,确定允许的电源模式
for (i = 0; i < bta_dm_conn_srvcs.count; i++) {
p_srvcs = &bta_dm_conn_srvcs.conn_srvc[i];
if (p_srvcs->peer_bdaddr == peer_addr) {
/* p_bta_dm_pm_cfg[0].app_id is the number of entries */
for (j = 1; j <= p_bta_dm_pm_cfg[0].app_id; j++) {
if ((p_bta_dm_pm_cfg[j].id == p_srvcs->id) &&
((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID) ||
(p_bta_dm_pm_cfg[j].app_id == p_srvcs->app_id)))
break;
}
p_pm_cfg = &p_bta_dm_pm_cfg[j];
p_pm_spec = &get_bta_dm_pm_spec()[p_pm_cfg->spec_idx];
p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state][0];
p_act1 = &p_pm_spec->actn_tbl[p_srvcs->state][1];
// 查找服务对应的配置
allowed_modes |= p_pm_spec->allow_mask;
log::debug(
"Service:{}[{}] state:{}[{}] allowed_modes:0x{:02x} service_index:{}",
BtaIdSysText(p_srvcs->id), p_srvcs->id,
bta_sys_conn_status_text(p_srvcs->state), p_srvcs->state,
allowed_modes, j);
/* PM actions are in the order of strictness */
// 优先选择第一个偏好的模式
/* first check if the first preference is ok */
if (!(failed_pm & p_act0->power_mode)) {
pref_modes |= p_act0->power_mode;
if (p_act0->power_mode >= pm_action) {
pm_action = p_act0->power_mode;
if (pm_req != BTA_DM_PM_NEW_REQ || p_srvcs->new_request) {
p_srvcs->new_request = false;
timeout_ms = p_act0->timeout;
}
}
}
/* if first preference has already failed, try second preference */
// 若第一个偏好失败,选择第二个偏好
else if (!(failed_pm & p_act1->power_mode)) {
pref_modes |= p_act1->power_mode;
if (p_act1->power_mode > pm_action) {
pm_action = p_act1->power_mode;
timeout_ms = p_act1->timeout;
}
}
}
}
// 3. 模式兼容性检查与调整
// 检查选择的电源模式是否被所有服务允许
// 如有冲突,调整为兼容的最高优先级模式
if (pm_action & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF)) {
/* some service don't like the mode */
if (!(allowed_modes & pm_action)) {
/* select the other mode if its allowed and preferred, otherwise 0 which
* is BTA_DM_PM_NO_ACTION */
pm_action =
(allowed_modes & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & pref_modes);
/* no timeout needed if no action is required */
if (pm_action == BTA_DM_PM_NO_ACTION) {
timeout_ms = 0;
}
}
}
// 4. 定时器管理
/* if need to start a timer */
if ((pm_req != BTA_DM_PM_EXECUTE) && (timeout_ms > 0)) {
for (i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {
if (bta_dm_cb.pm_timer[i].in_use &&
bta_dm_cb.pm_timer[i].peer_bdaddr == peer_addr) {
timer_idx = bta_pm_action_to_timer_idx(pm_action);
if (timer_idx != BTA_DM_PM_MODE_TIMER_MAX) {
remaining_ms =
alarm_get_remaining_ms(bta_dm_cb.pm_timer[i].timer[timer_idx]);
if (remaining_ms < timeout_ms) {
/* Cancel and restart the timer */
/*
* TODO: The value of pm_action[timer_idx] is
* conditionally updated between the two function
* calls below when the timer is restarted.
* This logic is error-prone and should be eliminated
* in the future.
*/
bta_dm_pm_stop_timer_by_index(&bta_dm_cb.pm_timer[i], timer_idx);
bta_dm_pm_start_timer(&bta_dm_cb.pm_timer[i], timer_idx, timeout_ms,
p_srvcs->id, pm_action);
}
timer_started = true;
}
break;
} else if (!bta_dm_cb.pm_timer[i].in_use) {
if (available_timer == BTA_DM_PM_MODE_TIMER_MAX) available_timer = i;
}
}
/* new power mode for a new active connection */
if (!timer_started) {
if (available_timer != BTA_DM_PM_MODE_TIMER_MAX) {
bta_dm_cb.pm_timer[available_timer].peer_bdaddr = peer_addr;
timer_idx = bta_pm_action_to_timer_idx(pm_action);
if (timer_idx != BTA_DM_PM_MODE_TIMER_MAX) {
bta_dm_pm_start_timer(&bta_dm_cb.pm_timer[available_timer], timer_idx,
timeout_ms, p_srvcs->id, pm_action);
timer_started = true;
}
} else {
log::warn("no more timers");
}
}
return;
}
/* if pending power mode timer expires, and currecnt link is in a
lower power mode than current profile requirement, igonre it */
if (pm_req == BTA_DM_PM_EXECUTE && pm_request < pm_action) {
log::error("Ignore the power mode request: {}", pm_request);
return;
}
// 5. 执行电源模式切换
if (pm_action == BTA_DM_PM_PARK) {
p_peer_device->pm_mode_attempted = BTA_DM_PM_PARK;
bta_dm_pm_park(peer_addr);
log::warn("DEPRECATED Setting link to park mode peer:{}",
ADDRESS_TO_LOGGABLE_CSTR(peer_addr));
} else if (pm_action & BTA_DM_PM_SNIFF) {
/* dont initiate SNIFF, if link_policy has it disabled */
if (BTM_is_sniff_allowed_for(peer_addr)) {
log::debug("Link policy allows sniff mode so setting mode peer:{}",
ADDRESS_TO_LOGGABLE_CSTR(peer_addr));
p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF;
bta_dm_pm_sniff(p_peer_device, (uint8_t)(pm_action & 0x0F));
} else {
log::debug("Link policy disallows sniff mode, ignore request peer:{}",
ADDRESS_TO_LOGGABLE_CSTR(peer_addr));
}
} else if (pm_action == BTA_DM_PM_ACTIVE) {
log::debug("Setting link to active mode peer:{}",
ADDRESS_TO_LOGGABLE_CSTR(peer_addr));
bta_dm_pm_active(peer_addr);
}
}
负责设置设备电源模式,根据各服务的电源需求,综合决策并应用适当的低功耗模式,包括**嗅探模式(Sniff Mode)、保持模式(Hold Mode)和停止模式(Park Mode)**等。
在 pm_request
为 BTA_DM_PM_NO_ACTION
且 pm_req
为 BTA_DM_PM_NEW_REQ
的情况下:
-
函数会正常遍历所有服务,根据它们的配置和优先级选择合适的电源模式
-
如果选择的模式需要延迟执行(通过定时器),函数会设置相应的定时器
-
BTA_DM_PM_NO_ACTION
参数本身不会阻止电源模式切换,除非所有服务都允许此模式
bta_sys_idle
cpp
packages/modules/Bluetooth/system/bta/sys/bta_sys_conn.cc
/*******************************************************************************
*
* Function bta_sys_idle
*
* Description Called by BTA subsystems to indicate that the connection to
* peer device is idle
*
* Returns void
*
******************************************************************************/
void bta_sys_idle(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_IDLE, id, app_id, peer_addr);
}
if (bta_sys_cb.ppm_cb) {
bta_sys_cb.ppm_cb(BTA_SYS_CONN_IDLE, id, app_id, peer_addr);
}
}
通知系统连接空闲状态,与之前分析的 bta_sys_busy
函数相对应,主要作用是通知电源管理和连接管理模块,当前与对等设备的连接已进入空闲状态,可以考虑进入低功耗模式。
三、时序图

四、典型问题与优化方向
①性能瓶颈
-
MTU碎片化:默认HID_DEV_MTU_SIZE(512B)可能不匹配高清HID设备(如触控笔)
-
优化建议:动态MTU协商(参考BLE ATT_MTU Exchange机制)
②功耗热点
-
虚假BUSY状态:高频小数据包(如游戏手柄)导致持续高功耗
-
优化建议:引入事件合并机制(Debounce算法)
③兼容性缺陷
-
老旧HID主机:对SSR4参数不支持引发连接抖动
-
优化建议:SSR能力嗅探与自适应回退
五、总结
蓝牙 HID 数据传输与电源管理是一个复杂的协同系统,涉及多层协议和多个功能模块的紧密协作。通过分层设计和状态驱动的机制,蓝牙协议栈实现了高效的数据传输和智能的电源管理。在数据传输方面,系统通过通道选择、拥塞控制和异步处理确保了数据的可靠性和实时性;在电源管理方面,通过动态状态监测、低功耗模式切换和参数优化,最大限度地降低了设备功耗。这种设计不仅满足了 HID 设备对数据传输的严格要求,也为电池供电的移动设备提供了良好的续航保障。
①双通道差异化传输
-
中断通道:硬实时保障(<125ms延迟),专用于输入报告,强制20MHz带宽锁定
-
控制通道:支持任意报告类型,但受协议栈QoS策略限制
②电源状态机设计
-
三级仲裁策略:服务优先级 > 历史失败记录 > 设备兼容性
-
嗅探参数动态化:根据服务类型(如A2DP/AVRCP)加载不同SSR预置模板
③错误弹性机制
-
拥塞降级:连续3次L2CAP发送失败触发控制通道回退
-
连接恢复:中断通道数据在断连时缓存并尝试自动重连
④功耗-性能平衡
-
BUSY锁频:维持Baseband Clock不降频以保吞吐量
-
IDLE快速休眠:在首个IDLE事件后500ms内进入SNIFF模式