本文深入分析 Android 蓝牙系统中获取本地设备 IO 能力(用于配对交互)的完整流程。该流程从上层接口调用开始,经过属性管理、配置读取、类型转换、范围校验等多个步骤,最终从持久化存储中获取预设的 IO 能力值(如 DisplayYesNo)。整个流程涉及 6 个核心模块的协作,通过分层设计和安全校验确保配置读写的可靠性。
一、概述
蓝牙本地 IO 能力(如BTM_IO_CAP_IO
,对应 “显示并确认” 能力)是设备在简单配对过程中决定认证方式的关键参数。其获取流程涉及蓝牙协议栈多个模块的协同,从上层应用接口到底层存储访问形成完整链路,核心目标是从持久化配置中读取有效配置值,若配置缺失则返回编译时默认值。
该流程的核心模块及功能如下:
上层接口:
btif_storage_get_local_io_caps
作为对外接口,触发 IO 能力获取流程。中间层适配:
btif_storage_get_io_cap_property
封装属性获取逻辑,通过btif_storage_get_adapter_property
调用通用属性接口。配置读取:
cfg2prop
负责从配置系统读取属性,通过btif_config_get_int
系列函数访问存储模块。存储与转换:
ConfigCache
从缓存获取字符串配置,经ConfigCacheHelper
转换为整数(含字符串→int64_t→int 的安全转换),并通过IsNumberInNumericLimits
校验范围。错误处理与默认值:若任一环节失败,流程自动回溯并返回默认值(
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 服务列表
通用属性的获取(通过
cfg2prop
或btif_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)” 对应的字符串值,支持三类存储区域的查询:
通用信息区(
information_sections_
):存储蓝牙适配器的通用配置(如本地 IO 能力、扫描模式)。持久化设备区(
persistent_devices_
):存储已配对设备的持久化配置(如远程设备的安全参数、名称)。临时设备区(
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);
}
从配置缓存中获取指定 section
和 property
对应的字符串值,将其转换为 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 能力获取流程通过分层设计实现了高内聚低耦合:上层接口屏蔽底层细节,中间层处理属性适配,底层负责存储访问与类型转换。多重校验机制(线程安全锁、类型转换校验、范围检查)确保了配置值的合法性与安全性,而默认值机制则提升了系统的健壮性。