【Bluedroid】蓝牙本地 IO 能力获取机制与实现流程解析(btif_storage_get_local_io_caps)

发布于:2025-07-13 ⋅ 阅读:(24) ⋅ 点赞:(0)

本文深入分析 Android 蓝牙系统中获取本地设备 IO 能力(用于配对交互)的完整流程。该流程从上层接口调用开始,经过属性管理、配置读取、类型转换、范围校验等多个步骤,最终从持久化存储中获取预设的 IO 能力值(如 DisplayYesNo)。整个流程涉及 6 个核心模块的协作,通过分层设计和安全校验确保配置读写的可靠性。

一、概述

蓝牙本地 IO 能力(如BTM_IO_CAP_IO,对应 “显示并确认” 能力)是设备在简单配对过程中决定认证方式的关键参数。其获取流程涉及蓝牙协议栈多个模块的协同,从上层应用接口到底层存储访问形成完整链路,核心目标是从持久化配置中读取有效配置值,若配置缺失则返回编译时默认值。

该流程的核心模块及功能如下:

  1. 上层接口btif_storage_get_local_io_caps作为对外接口,触发 IO 能力获取流程。

  2. 中间层适配btif_storage_get_io_cap_property封装属性获取逻辑,通过btif_storage_get_adapter_property调用通用属性接口。

  3. 配置读取cfg2prop负责从配置系统读取属性,通过btif_config_get_int系列函数访问存储模块。

  4. 存储与转换ConfigCache从缓存获取字符串配置,经ConfigCacheHelper转换为整数(含字符串→int64_t→int 的安全转换),并通过IsNumberInNumericLimits校验范围。

  5. 错误处理与默认值:若任一环节失败,流程自动回溯并返回默认值(BTM_LOCAL_IO_CAPS)。

二、源码解析

btif_storage_get_io_caps

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
BTM_IO_CAP_IO = 1,     /* DisplayYesNo */

/* The IO capability of the local device (for Simple Pairing) */
#ifndef BTM_LOCAL_IO_CAPS
#define BTM_LOCAL_IO_CAPS BTM_IO_CAP_IO
#endif

/*******************************************************************************
 *
 * Function         btif_storage_get_io_caps
 *
 * Description      BTIF storage API - Fetches the local Input/Output
 *                  capabilities of the device.
 *
 * Returns          Returns local IO Capability of device. If not stored,
 *                  returns BTM_LOCAL_IO_CAPS.
 *
 ******************************************************************************/
tBTM_IO_CAP btif_storage_get_local_io_caps() {
  return static_cast<tBTM_IO_CAP>(btif_storage_get_io_cap_property(
      BT_PROPERTY_LOCAL_IO_CAPS, BTM_LOCAL_IO_CAPS));
}

提供获取本地设备 IO 能力的接口,具体功能包括:

  • 提供 API 从存储中读取已保存的 IO 能力配置

  • 若存储中未保存,则返回编译时定义的默认值

在蓝牙简单配对过程中,设备需要交换 IO 能力信息来确定最佳的配对方式。

btif_storage_get_io_cap_property

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
/**
 * Helper function for fetching a local Input/Output capability property. If not
 * set, it returns the default value.
 */
static uint8_t btif_storage_get_io_cap_property(bt_property_type_t type,
                                                uint8_t default_value) {
  char buf[sizeof(int)];

  bt_property_t property;
  property.type = type;
  property.val = (void*)buf;
  property.len = sizeof(int);

  bt_status_t ret = btif_storage_get_adapter_property(&property);

  return (ret == BT_STATUS_SUCCESS) ? (uint8_t)(*(int*)property.val)
                                    : default_value;
}

/* Bluetooth Adapter and Remote Device property types */
typedef enum {
  /* Properties common to both adapter and remote device */
  /**
   * Description - Bluetooth Device Name
   * Access mode - Adapter name can be GET/SET. Remote device can be GET
   * Data type   - bt_bdname_t
   */
  BT_PROPERTY_BDNAME = 0x1,
  /**
   * Description - Bluetooth Device Address
   * Access mode - Only GET.
   * Data type   - RawAddress
   */
  BT_PROPERTY_BDADDR,
  /**
   * Description - Bluetooth Service 128-bit UUIDs
   * Access mode - Only GET.
   * Data type   - Array of bluetooth::Uuid (Array size inferred from property
   *               length).
   */
  BT_PROPERTY_UUIDS,
  /**
   * Description - Bluetooth Class of Device as found in Assigned Numbers
   * Access mode - Only GET.
   * Data type   - uint32_t.
   */
  BT_PROPERTY_CLASS_OF_DEVICE,
  /**
   * Description - Device Type - BREDR, BLE or DUAL Mode
   * Access mode - Only GET.
   * Data type   - bt_device_type_t
   */
  BT_PROPERTY_TYPE_OF_DEVICE,
  /**
   * Description - Bluetooth Service Record
   * Access mode - Only GET.
   * Data type   - bt_service_record_t
   */
  BT_PROPERTY_SERVICE_RECORD,

  /* Properties unique to adapter */
  /**
   * Description - Bluetooth Adapter scan mode
   * Access mode - GET and SET
   * Data type   - bt_scan_mode_t.
   */
  BT_PROPERTY_ADAPTER_SCAN_MODE,
  /**
   * Description - List of bonded devices
   * Access mode - Only GET.
   * Data type   - Array of RawAddress of the bonded remote devices
   *               (Array size inferred from property length).
   */
  BT_PROPERTY_ADAPTER_BONDED_DEVICES,
  /**
   * Description - Bluetooth Adapter Discoverable timeout (in seconds)
   * Access mode - GET and SET
   * Data type   - uint32_t
   */
  BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,

  /* Properties unique to remote device */
  /**
   * Description - User defined friendly name of the remote device
   * Access mode - GET and SET
   * Data type   - bt_bdname_t.
   */
  BT_PROPERTY_REMOTE_FRIENDLY_NAME,
  /**
   * Description - RSSI value of the inquired remote device
   * Access mode - Only GET.
   * Data type   - int8_t.
   */
  BT_PROPERTY_REMOTE_RSSI,
  /**
   * Description - Remote version info
   * Access mode - SET/GET.
   * Data type   - bt_remote_version_t.
   */

  BT_PROPERTY_REMOTE_VERSION_INFO,

  /**
   * Description - Local LE features
   * Access mode - GET.
   * Data type   - bt_local_le_features_t.
   */
  BT_PROPERTY_LOCAL_LE_FEATURES,

  /**
   * Description - Local Input/Output Capabilities for classic Bluetooth
   * Access mode - GET and SET
   * Data Type - bt_io_cap_t.
   */
  BT_PROPERTY_LOCAL_IO_CAPS,

  BT_PROPERTY_RESERVED_0F,

  BT_PROPERTY_DYNAMIC_AUDIO_BUFFER,

  /**
   * Description - True if Remote is a Member of a Coordinated Set.
   * Access mode - GET.
   * Data Type - bool.
   */
  BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER,

  /**
   * Description - Appearance as specified in Assigned Numbers.
   * Access mode - GET.
   * Data Type - uint16_t.
   */
  BT_PROPERTY_APPEARANCE,

  /**
   * Description - Peer devices' vendor and product ID.
   * Access mode - GET.
   * Data Type - bt_vendor_product_info_t.
   */
  BT_PROPERTY_VENDOR_PRODUCT_INFO,
  BT_PROPERTY_WL_MEDIA_PLAYERS_LIST,

  /**
   * Description - ASHA capability.
   * Access mode - GET.
   * Data Type - int16_t.
   */
  BT_PROPERTY_REMOTE_ASHA_CAPABILITY,

  /**
   * Description - ASHA truncated HiSyncID.
   * Access mode - GET.
   * Data Type - uint32_t.
   */
  BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID,

  /**
   * Description - Model name read from Device Information Service(DIS).
   * Access mode - GET and SET.
   * Data Type - char array.
   */
  BT_PROPERTY_REMOTE_MODEL_NUM,

  /**
   * Description - Address type of the remote device - PUBLIC or REMOTE
   * Access mode - GET.
   * Data Type - uint8_t.
   */
  BT_PROPERTY_REMOTE_ADDR_TYPE,

  /**
   * Description - Whether remote device supports Secure Connections mode
   * Access mode - GET and SET.
   * Data Type - uint8_t.
   */
  BT_PROPERTY_REMOTE_SECURE_CONNECTIONS_SUPPORTED,

  /**
   * Description - Maximum observed session key for remote device
   * Access mode - GET and SET.
   * Data Type - uint8_t.
   */
  BT_PROPERTY_REMOTE_MAX_SESSION_KEY_SIZE,

  BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
} bt_property_type_t;

/** Bluetooth Adapter Property data structure */
typedef struct {
  bt_property_type_t type;
  int len;
  void* val;
} bt_property_t;

从存储中获取指定类型的蓝牙属性值,若失败则返回默认值

btif_storage_get_adapter_property(BT_PROPERTY_LOCAL_IO_CAPS)

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
/*******************************************************************************
 *
 * Function         btif_storage_get_adapter_property
 *
 * Description      BTIF storage API - Fetches the adapter property->type
 *                  from NVRAM and fills property->val.
 *                  Caller should provide memory for property->val and
 *                  set the property->val
 *
 * Returns          BT_STATUS_SUCCESS if the fetch was successful,
 *                  BT_STATUS_FAIL otherwise
 *
 ******************************************************************************/
bt_status_t btif_storage_get_adapter_property(bt_property_t* property) {
  /* Special handling for adapter address and BONDED_DEVICES */
  if (property->type == BT_PROPERTY_BDADDR) {
    RawAddress* bd_addr = (RawAddress*)property->val;
    /* Fetch the local BD ADDR */
    const controller_t* controller = controller_get_interface();
    if (!controller->get_is_ready()) {
      log::error("Controller not ready! Unable to return Bluetooth Address");
      *bd_addr = RawAddress::kEmpty;
      return BT_STATUS_FAIL;
    } else {
      log::info("Controller ready!");
      *bd_addr = *controller->get_address();
    }
    property->len = RawAddress::kLength;
    return BT_STATUS_SUCCESS;
  } else if (property->type == BT_PROPERTY_ADAPTER_BONDED_DEVICES) {
    btif_bonded_devices_t bonded_devices;

    btif_in_fetch_bonded_devices(&bonded_devices, 0);

    log::verbose(
        "BT_PROPERTY_ADAPTER_BONDED_DEVICES: Number of bonded devices={}",
        bonded_devices.num_devices);

    property->len = bonded_devices.num_devices * RawAddress::kLength;
    memcpy(property->val, bonded_devices.devices, property->len);

    /* if there are no bonded_devices, then length shall be 0 */
    return BT_STATUS_SUCCESS;
  } else if (property->type == BT_PROPERTY_UUIDS) {
    /* publish list of local supported services */
    Uuid* p_uuid = reinterpret_cast<Uuid*>(property->val);
    uint32_t num_uuids = 0;
    uint32_t i;

    tBTA_SERVICE_MASK service_mask = btif_get_enabled_services_mask();
    log::info("Service_mask=0x{:x}", service_mask);
    for (i = 0; i < BTA_MAX_SERVICE_ID; i++) {
      /* This should eventually become a function when more services are enabled
       */
      if (service_mask & (tBTA_SERVICE_MASK)(1 << i)) {
        switch (i) {
          case BTA_HFP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);
            num_uuids++;
          }
            FALLTHROUGH_INTENDED; /* FALLTHROUGH */
          /* intentional fall through: Send both BFP & HSP UUIDs if HFP is
           * enabled */
          case BTA_HSP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY);
            num_uuids++;
          } break;
          case BTA_A2DP_SOURCE_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SOURCE);
            num_uuids++;
          } break;
          case BTA_A2DP_SINK_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SINK);
            num_uuids++;
          } break;
          case BTA_PBAP_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PSE);
            num_uuids++;
          } break;
          case BTA_HFP_HS_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_HF_HANDSFREE);
            num_uuids++;
          } break;
          case BTA_MAP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_ACCESS);
            num_uuids++;
          } break;
          case BTA_MN_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_NOTIFICATION);
            num_uuids++;
          } break;
          case BTA_PCE_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PCE);
            num_uuids++;
          } break;
        }
      }
    }
    property->len = (num_uuids) * sizeof(Uuid);
    return BT_STATUS_SUCCESS;
  }

  // 通用属性获取
  /* fall through for other properties */
  if (!cfg2prop(NULL, property)) {
    return btif_dm_get_adapter_property(property);
  }
  return BT_STATUS_SUCCESS;
}

获取本地蓝牙适配器的各种属性。根据不同属性类型提供了专门处理逻辑,并通过统一接口返回结果。其主要功能包括:

  • 获取蓝牙设备地址(BDADDR)

  • 获取已配对设备列表

  • 获取设备支持的 UUID 服务列表

  • 通用属性的获取(通过cfg2propbtif_dm_get_adapter_property

cfg2prop(BT_PROPERTY_LOCAL_IO_CAPS)

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
#define BTIF_STORAGE_KEY_LOCAL_IO_CAPS "LocalIOCaps"
#define BTIF_STORAGE_SECTION_ADAPTER "Adapter"

static bool cfg2prop(const RawAddress* remote_bd_addr, bt_property_t* prop) {
  std::string bdstr;
  if (remote_bd_addr) {
    bdstr = remote_bd_addr->ToString();
  }
  if (prop->len <= 0) {
    log::warn("Invalid property read from configuration file type:{}, len:{}",
              prop->type, prop->len);
    return false;
  }
  bool ret = false;
  switch (prop->type) {
      ...
    case BT_PROPERTY_LOCAL_IO_CAPS:
      if (prop->len >= (int)sizeof(int))
        ret = btif_config_get_int(BTIF_STORAGE_SECTION_ADAPTER,
                                  BTIF_STORAGE_KEY_LOCAL_IO_CAPS,
                                  (int*)prop->val);
      break;
    case BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
      if (prop->len >= (int)sizeof(int))
        ret =
            btif_config_get_int(BTIF_STORAGE_SECTION_ADAPTER,
                                BTIF_STORAGE_KEY_DISC_TIMEOUT, (int*)prop->val);
      break;
      ...
   default:
      log::error("Unknown prop type:{}", prop->type);
      return false;
  }
  return ret;
}

从持久化配置中读取属性值并填充到bt_property_t结构体中。对于BT_PROPERTY_LOCAL_IO_CAPS,其核心作用是从设备的持久化存储中读取本地蓝牙适配器的 IO 能力配置值,为配对过程提供基础参数。

btif_config_get_int

packages/modules/Bluetooth/system/btif/src/btif_config.cc
// 负责前置状态检查(蓝牙栈是否就绪)
bool btif_config_get_int(const std::string& section, const std::string& key,
                         int* value) {
  CHECK(bluetooth::shim::is_gd_stack_started_up());
  return bluetooth::shim::BtifConfigInterface::GetInt(section, key, value);
}

// 负责参数校验和存储模块的调用
bool BtifConfigInterface::GetInt(const std::string& section,
                                 const std::string& property, int* value) {
  ASSERT(value != nullptr);
  auto ret = GetStorage()->GetInt(section, property);
  if (ret) {
    *value = *ret;
  }
  return ret.has_value();
}

// 负责线程安全控制和从配置缓存中读取值
std::optional<int> StorageModule::GetInt(
    const std::string& section, const std::string& property) const {
  std::lock_guard<std::recursive_mutex> lock(mutex_);
  return ConfigCacheHelper::FromConfigCache(pimpl_->cache_).GetInt(section, property);
}

通过分层设计、安全校验、线程安全和缓存优化,实现蓝牙配置系统中整数类型配置的高效、可靠读取。其核心价值在于:

  • 对上层隐藏存储细节,降低使用复杂度;

  • 通过多重校验(状态、参数、线程)确保系统稳定性;

  • 利用缓存和std::optional提升性能和代码可读性。

ConfigCacheHelper::GetInt

packages/modules/Bluetooth/system/gd/storage/config_cache_helper.cc
std::optional<int> ConfigCacheHelper::GetInt(const std::string& section, const std::string& property) const {
  // 1. 从配置缓存中获取 section 和 property 对应的字符串值
  auto value_str = config_cache_.GetProperty(section, property);
  if (!value_str) {
    return std::nullopt;
  }
  
  // 2. 将字符串转换为 64 位整数
  auto large_value = GetInt64(section, property);
  if (!large_value) {
    return std::nullopt;
  }
  
  // 3. 验证整数范围(是否在 int 范围内)
  if (!common::IsNumberInNumericLimits<int>(*large_value)) {
    return std::nullopt;
  }
  
  // 4. 转换为 int 并返回
  return static_cast<uint32_t>(*large_value);
}

将缓存中指定 section(配置节)和 property(配置键)对应的字符串值,安全转换为 int 类型,并验证其有效性。若转换失败(如值不存在、非数字、超出范围),则返回空(std::nullopt)。

ConfigCache::GetProperty

packages/modules/Bluetooth/system/gd/storage/config_cache.cc
std::optional<std::string> ConfigCache::GetProperty(const std::string& section, const std::string& property) const {
  // 1. 线程安全锁
  std::lock_guard<std::recursive_mutex> lock(mutex_);
  
  // 2. 第一步:查询通用信息区
  auto section_iter = information_sections_.find(section);
  if (section_iter != information_sections_.end()) {
    auto property_iter = section_iter->second.find(property);
    if (property_iter != section_iter->second.end()) {
      return property_iter->second;
    }
  }
  
  // 3. 第二步:查询持久化设备区(
  section_iter = persistent_devices_.find(section);
  if (section_iter != persistent_devices_.end()) {
    auto property_iter = section_iter->second.find(property);
    if (property_iter != section_iter->second.end()) {
      std::string value = property_iter->second;
      if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && value == kEncryptedStr) {
        return os::ParameterProvider::GetBtKeystoreInterface()->get_key(section + "-" + property);
      }
      return value;
    }
  }
  
  // 4. 第三步:查询临时设备区
  section_iter = temporary_devices_.find(section);
  if (section_iter != temporary_devices_.end()) {
    auto property_iter = section_iter->second.find(property);
    if (property_iter != section_iter->second.end()) {
      return property_iter->second;
    }
  }
  
  // 5. 未找到配置项
  return std::nullopt;
}

从配置缓存中获取指定 “配置节(section)” 和 “配置键(property)” 对应的字符串值,支持三类存储区域的查询:

  1. 通用信息区(information_sections_:存储蓝牙适配器的通用配置(如本地 IO 能力、扫描模式)。

  2. 持久化设备区(persistent_devices_:存储已配对设备的持久化配置(如远程设备的安全参数、名称)。

  3. 临时设备区(temporary_devices_:存储临时设备的非持久化配置(如未配对设备的临时连接参数)。

ConfigCacheHelper::GetInt64

packages/modules/Bluetooth/system/gd/storage/config_cache_helper.cc
std::optional<int64_t> ConfigCacheHelper::GetInt64(const std::string& section, const std::string& property) const {
  // 1. 获取字符串形式的配置值
  auto value_str = config_cache_.GetProperty(section, property);
  if (!value_str) {
    return std::nullopt;
  }
  // 2. 字符串转换为 64 位整数
  return common::Int64FromString(*value_str);
}

从配置缓存中获取指定 sectionproperty对应的字符串值,将其转换为 int64_t 类型(64 位整数),并通过 std::optional 标识转换成功与否。它是 “字符串→int64_t→int” 转换链路的中间环节,为后续的范围校验(如是否符合 int 类型范围)提供基础。

Int64FromString

packages/modules/Bluetooth/system/gd/common/strings.cc
std::optional<int64_t> Int64FromString(const std::string& str) {
  // 1. 初始化指针与错误码
  char* ptr = nullptr;
  errno = 0;
  
  // 2. 字符串转换为长整数
  int64_t value = std::strtoll(str.c_str(), &ptr, 10);
  // 3. 校验:是否发生溢出或其他错误
  if (errno != 0) {
    LOG_INFO("cannot parse string '%s' with error '%s'", str.c_str(), strerror(errno));
    return std::nullopt;
  }
  // 4. 校验:是否有有效字符被转换
  if (ptr == str.c_str()) {
    LOG_INFO("string '%s' is empty or has wrong format", str.c_str());
    return std::nullopt;
  }
  // 5. 校验:字符串是否被完整转换
  if (ptr != (str.c_str() + str.size())) {
    LOG_INFO("cannot parse whole string '%s'", str.c_str());
    return std::nullopt;
  }
  return value;
}

将输入的字符串转换为 int64_t 类型,并通过多重校验确保转换的完整性和合法性。若字符串是有效的整数(如 "123" -456),返回转换后的 int64_t 值;若字符串无效(如 "abc" "12.3" "123x" 或超出 int64_t 范围),返回 std::nullopt。它是配置系统中 “字符串→整数” 转换的 “守门人”,直接决定上层能否获取到合法的配置数值。

IsNumberInNumericLimits

packages/modules/Bluetooth/system/gd/common/numbers.h
namespace bluetooth {
namespace common {

// Check if input is within numeric limits of RawType
template <typename RawType, typename InputType>
bool IsNumberInNumericLimits(InputType input) {
  // Only arithmetic types are supported
  static_assert(std::is_arithmetic_v<RawType> && std::is_arithmetic_v<InputType>);
  // Either both are signed or both are unsigned
  static_assert(
      (std::is_signed_v<RawType> && std::is_signed_v<InputType>) ||
      (std::is_unsigned_v<RawType> && std::is_unsigned_v<InputType>));
  //  最大值范围校验
  if (std::numeric_limits<InputType>::max() > std::numeric_limits<RawType>::max()) {
    if (input > std::numeric_limits<RawType>::max()) {
      return false;
    }
  }
  //  最小值范围校验
  if (std::numeric_limits<InputType>::lowest() < std::numeric_limits<RawType>::lowest()) {
    if (input < std::numeric_limits<RawType>::lowest()) {
      return false;
    }
  }
  return true;
}

}  // namespace common
}  // namespace bluetooth

校验输入值(InputType 类型)是否在目标类型(RawType 类型)的合法数值范围内。若在范围内,返回 true;否则返回 false。其设计目的是在类型转换(尤其是 “宽类型→窄类型”)前进行安全检查,防止因数值超出目标类型范围导致的溢出(如 int64_t 的大值转换为 int 时的溢出)。

三、关键模块交互时序图

蓝牙本地 IO 能力获取流程通过分层设计实现了高内聚低耦合:上层接口屏蔽底层细节,中间层处理属性适配,底层负责存储访问与类型转换。多重校验机制(线程安全锁、类型转换校验、范围检查)确保了配置值的合法性与安全性,而默认值机制则提升了系统的健壮性。



网站公告

今日签到

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