【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

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

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

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

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

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

  5. 维持连接状态 :通过 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_requestBTA_DM_PM_NO_ACTIONpm_reqBTA_DM_PM_NEW_REQ 的情况下:

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

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

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


相关推荐
darkchink5 分钟前
[LevelDB]LevelDB版本管理的黑魔法-为什么能在不锁表的情况下管理数据?
c语言·数据库·c++·oracle·数据库开发·dba·db
易只轻松熊12 分钟前
C++(23):容器类<vector>
开发语言·数据结构·c++
ha204289419423 分钟前
c++学习之--- list
c语言·c++·学习·list
君鼎1 小时前
muduo库TcpServer模块详解
linux·网络·c++
龙湾开发2 小时前
轻量级高性能推理引擎MNN 学习笔记 03.在iOS运行MNN的示例
c++·学习·ios·图形渲染·mnn
li星野3 小时前
C++:⾯向对象的三⼤特性
c++
苕皮蓝牙土豆5 小时前
C++ map容器: 插入操作
开发语言·c++
顾子茵5 小时前
c++从入门到精通(六)--特殊工具与技术-完结篇
android·开发语言·c++
孞㐑¥6 小时前
Linux之基础IO
linux·开发语言·c++·经验分享·笔记