【Bluedroid】蓝牙HID DEVICE错误报告处理全流程源码解析

发布于:2025-05-19 ⋅ 阅读:(21) ⋅ 点赞:(0)

本文基于Android蓝牙协议栈代码,深入解析HID设备在接收非法指令(如无效的SET_REPORT)时的错误处理全流程,涵盖错误映射、协议封装、传输控制三大核心模块。重点剖析以下机制:

  1. HID协议规范错误码的动态转换策略

  2. 控制通道的优先级保障与拥塞回避机制

  3. 错误报告与正常数据报文的差异化处理逻辑

  4. 跨层级状态同步与连接恢复机制

一、流程概述

1.1 错误触发与协议栈传递

①应用层检测

上层应用通过report_error()触发错误处理,传入原始错误码(如HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID)。

②协议栈封装

BTA_HdReportError():分配tBTA_HD_REPORT_ERR结构体,封装BTA_HD_API_REPORT_ERROR_EVT事件,通过bta_sys_sendmsg投递到系统队列。

③事件路由

bta_hd_hdl_event():将事件路由至状态机,触发bta_hd_report_error_act动作函数。

1.2 协议规范转换

①错误码标准化

HID_DevReportError():将原始错误码映射为HID协议定义的7种标准错误类型(如未知错误强制转换为HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN)。

②消息构建

构建HID_TRANS_HANDSHAKE类型报文,参数字段携带标准错误码,通过控制通道(CID=0x0040)发送。

1.3 物理层传输控制

①通道选择

强制使用控制通道(HID_CHANNEL_CTRL),确保错误报文优先传输。

②拥塞检测

hidd_conn_send_data()检查HID_CONN_FLAGS_CONGESTED标志,若通道拥塞直接返回HID_ERR_CONGESTED

③断连恢复

若检测到连接断开,丢弃待发错误报文(不同于数据报文的缓存机制),避免无效重传。

二、源码解析

report_error

packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
 *
 * Function         report_error
 *
 * Description      Sends HANDSHAKE with error info for invalid SET_REPORT
 *
 * Returns          bt_status_t
 *
 ******************************************************************************/
static bt_status_t report_error(uint8_t error) {
  log::verbose("");

  // 状态检查:确保应用已注册
  if (!btif_hd_cb.app_registered) {
    log::warn("application not yet registered");
    return BT_STATUS_NOT_READY;
  }

  // 状态检查:确保蓝牙HID设备已启用
  if (btif_hd_cb.status != BTIF_HD_ENABLED) {
    log::warn("BT-HD not enabled, status={}", btif_hd_cb.status);
    return BT_STATUS_NOT_READY;
  }

   // 调用BTA层API发送错误报告
   BTA_HdReportError(error);

  return BT_STATUS_SUCCESS;
}

向 HID 主机发送错误报告,用于响应无效的 SET_REPORT 命令。

BTA_HdReportError

packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/*******************************************************************************
 *
 * Function         BTA_HdReportError
 *
 * Description      This function is called when reporting error for set report
 *
 * Returns          void
 *
 ******************************************************************************/
void BTA_HdReportError(uint8_t error) {
  log::verbose("");
  tBTA_HD_REPORT_ERR* p_buf =
      (tBTA_HD_REPORT_ERR*)osi_malloc(sizeof(tBTA_HD_REPORT_ERR));
  p_buf->hdr.event = BTA_HD_API_REPORT_ERROR_EVT;
  p_buf->error = error;

  bta_sys_sendmsg(p_buf);
}

生成并发送包含错误信息的 HANDSHAKE 消息。

bta_hd_hdl_event(BTA_HD_API_REPORT_ERROR_EVT)

packages/modules/Bluetooth/system/bta/hd/bta_hd_api.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);
}

负责接收和分发来自上层应用或下层协议栈的各种事件。作为 HID 设备状态机的入口点,根据不同的事件类型执行相应的处理逻辑。

bta_hd_better_state_machine

   ...
   case BTA_HD_CONN_ST:
        ...
        case BTA_HD_API_REPORT_ERROR_EVT:
          bta_hd_report_error_act(p_data);
          break;
    ...

bta_hd_report_error_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/*******************************************************************************
 *
 * Function         bta_hd_report_error_act
 *
 * Description
 *
 * Returns          void
 *
 ******************************************************************************/
void bta_hd_report_error_act(tBTA_HD_DATA* p_data) {
  tBTA_HD_REPORT_ERR* p_report = (tBTA_HD_REPORT_ERR*)p_data;
  tHID_STATUS ret;

  log::verbose("error = {}", p_report->error);

  ret = HID_DevReportError(p_report->error);

  if (ret != HID_SUCCESS) {
    log::warn("HID_DevReportError returned {}", ret);
  }
}

将错误信息封装为 HID 协议规范的错误报告并发送给主机设备。

HID_DevReportError

packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/*******************************************************************************
 *
 * Function         HID_DevReportError
 *
 * Description      Reports error for Set Report via HANDSHAKE
 *
 * Returns          tHID_STATUS
 *
 ******************************************************************************/
tHID_STATUS HID_DevReportError(uint8_t error) {
  uint8_t handshake_param;

  log::verbose("error = {}", error);

  // 错误码映射
  switch (error) {
    case HID_PAR_HANDSHAKE_RSP_SUCCESS:
    case HID_PAR_HANDSHAKE_RSP_NOT_READY:
    case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID:
    case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ:
    case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM:
    case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN:
    case HID_PAR_HANDSHAKE_RSP_ERR_FATAL:
      handshake_param = error;
      break;
    default:
      handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN;
      break;
  }

  // 构建 HANDSHAKE 消息
  return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0,
                             NULL);
}

负责将错误信息封装为 HID 协议规范的 HANDSHAKE 消息发送给主机设备。当设备接收到无效的 SET_REPORT 命令或其他错误条件时,通过此函数向主机反馈具体的错误类型。

对应 HID 协议规范中的标准错误类型:

错误码常量 描述
HID_PAR_HANDSHAKE_RSP_SUCCESS 0x00 成功
HID_PAR_HANDSHAKE_RSP_NOT_READY 0x01 设备未准备好
HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID 0x02 无效的报告 ID
HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ 0x03 不支持的请求
HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM 0x04 无效的参数
HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN 0x14 未知错误
HID_PAR_HANDSHAKE_RSP_ERR_FATAL 0x15 致命错误

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. 通道和缓冲区分配
  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)
  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;
  }

  // 4. 连接状态检查
  // check if connected
  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. 数据发送
  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 数据包并通过蓝牙物理层发送给主机设备。实现了数据发送的核心逻辑,包括通道选择、消息封装、连接状态检查和拥塞控制等功能。

三、关键机制解析

①错误码映射策略

  • 严格过滤:将用户输入的任意错误码映射到HID协议定义的7种类型,防止协议违规。

  • 安全兜底:未定义错误码统一转换为HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN,保障协议兼容性。

②控制通道保障机制

  • 独立缓冲区:使用固定512字节控制缓冲区(HID_CONTROL_BUF_SIZE),与数据通道隔离。

  • 优先级抢占:控制报文可中断正在传输的数据报文,确保错误及时反馈。

③差异化重传策略

报文类型 重传机制 缓存策略
数据报文 自动重试3次 保留最新数据
错误报文 立即丢弃 不缓存

④状态同步设计

  • 跨层状态机:通过bta_hd_cb.state同步应用层与协议栈状态,避免在禁用状态发送错误。

  • 原子化操作:错误处理全程关闭中断,通过osi_malloc保证内存操作的原子性。

四、时序图

五、总结

蓝牙 HID 协议中的错误报告机制是确保设备与主机之间可靠通信的重要组成部分。通过多层级的组件协作和标准化的消息格式,系统能够准确地将错误信息反馈给主机。核心代码实现采用了事件驱动架构、状态机模式和适配器模式,确保了系统的可扩展性和可维护性。

该流程体现了蓝牙HID设备错误处理的核心设计哲学:

  1. 协议合规性优先:通过严格的错误码映射和通道选择,确保符合HID 1.11规范。

  2. 故障弱化原则:在连接异常时主动丢弃错误报告,避免协议栈状态不一致。

  3. 实时性保障:控制通道的独立缓冲区设计和优先级机制,确保关键错误信息毫秒级送达。

  4. 状态驱动架构:通过事件-状态机模型实现跨层级状态同步,提升系统健壮性。



网站公告

今日签到

点亮在社区的每一天
去签到