当前位置: 首页 > news >正文

【Bluedroid】蓝牙HID DEVICE 报告发送与电源管理源码解析

本文基于Android蓝牙协议栈代码,深度解析HID设备(如键盘、鼠标)从应用层发送输入报告到主机设备的完整流程,涵盖数据封装、通道选择、L2CAP传输、电源管理四大核心模块。通过函数调用链(send_reportBTA_HdSendReportHID_DevSendReporthidd_conn_send_data)的逐层分析,重点剖析以下机制:

  1. 中断通道与控制通道的动态选择策略

  2. 蓝牙协议栈异步消息队列与状态机协作

  3. BUSY/IDLE状态下的电源模式动态调整

  4. 低功耗模式(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_INTRHID_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

/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

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)

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)

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

/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

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

/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_idp_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 nowif (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

/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)

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 avoidexcessive switch requests when individual profile busy/idle statuschanges */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)

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 removethe 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 状态时,主要执行以下操作:

  1. 音频子系统优化:为音频流(如 A2DP)设置专门的 SSR 参数(SSR4),确保低延迟传输。

  2. 服务状态更新:记录服务的繁忙状态,用于后续电源策略决策。

  3. 定时器管理:暂停可能导致低功耗模式的定时器,维持连接活跃状态。

  4. 避免参数冲突:跳过对音频子系统的常规 SSR 参数设置,使用专用参数。

  5. 维持连接状态:通过 bta_dm_pm_set_mode 确保在数据传输期间不进入低功耗模式。

bta_dm_pm_set_mode(BTA_DM_PM_NO_ACTION)

/********************************************************************************* 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 alower 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_requestBTA_DM_PM_NO_ACTIONpm_reqBTA_DM_PM_NEW_REQ 的情况下:

  1. 函数会正常遍历所有服务,根据它们的配置和优先级选择合适的电源模式

  2. 如果选择的模式需要延迟执行(通过定时器),函数会设置相应的定时器

  3. BTA_DM_PM_NO_ACTION 参数本身不会阻止电源模式切换,除非所有服务都允许此模式

bta_sys_idle

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模式


相关文章:

  • 《Python星球日记》 第92天:AI模型部署工程化基础
  • DSU-Net
  • 第二十八天打卡
  • 基于PageHelper的分页查询
  • Leetcode 3552. Grid Teleportation Traversal
  • 数据库触发器Trigger
  • 算法题(149):矩阵消除游戏
  • 大模型评测体系综述
  • 学习黑客Active Directory 入门指南(四)
  • 苍穹外卖--修改菜品
  • 【超详细】面试中问到事件循环(Event Loop)机制?
  • 如何本地部署Qwen3系列的大小模型235B/32B并进行推理服务及并发测试?
  • C#自定义扩展方法 及 EventHandler<TEventArgs> 委托
  • 【linux驱动】【设备树】按键设备树讲解
  • WaterStamp —— 一个实用的网页水印生成器开发记
  • 【周输入】510周阅读推荐-3
  • c/c++数据类型转换.
  • 二:操作系统之进程控制块(PCB)
  • Selinux权限问题处理指导文档分享
  • 菱形继承原理
  • 新华社千笔楼:地方文旅宣传应走出“魔性尬舞”的流量焦虑
  • 上海这个咖啡文化节首次“走出去”,率本土品牌亮相英国伦敦
  • 3月中国减持189亿美元美债、持仓规模降至第三,英国升至第二
  • 广西壮族自治区党委常委会:坚决拥护党中央对蓝天立进行审查调查的决定
  • 特朗普再提“接管”加沙,要将其变为“自由区”
  • 国税总局上海市税务局通报:收到王某对刘某某及相关企业涉税问题举报,正依法依规办理