【Bluedroid】蓝牙设备管理器初始化全流程深度解析(BTA_dm_on_hw_on)

本文全面剖析Android蓝牙设备管理器在硬件启动时的初始化流程,涵盖控制块创建、服务发现启动、设备类配置、安全密钥加载、超时参数设置等核心环节。通过分析从底层硬件交互到上层服务注册的全链路调用,揭示蓝牙系统从硬件就绪到功能可用的完整启动机制,重点解读多线程协作、动态适配和资源管理的设计哲学。为蓝牙系统开发与调试提供理论支撑。

一、概述

蓝牙设备管理器初始化包含12个关键步骤:

  1. 控制块初始化:创建禁用/切换/电源管理定时器

  2. 服务发现启动:重置发现状态并创建搜索队列

  3. 设备类配置:解析系统属性并应用BAP规范调整

  4. BLE密钥加载:获取并注入加密根密钥和身份解析密钥

  5. 安全模块初始化:注册SMP回调并重置安全状态

  6. 连接超时设置:配置page扫描超时参数

  7. 控制器特性读取:动态适配厂商扩展功能

  8. BLE扫描器初始化:创建HCI接口和扫描管理器

  9. 设备名读取:直接从控制器获取实时设备名

  10. 资源管理注册:连接管理回调注册

  11. 电源管理初始化:低功耗模式注册与计时器配置

  12. GATT客户端注册:为设备发现提供通信接口

二、源码分析

BTA_dm_on_hw_on

packages/modules/Bluetooth/system/bta/dm/bta_dm_act.cc
void BTA_dm_on_hw_on() {uint8_t key_mask = 0;tBTA_BLE_LOCAL_ID_KEYS id_key;// 1. 控制块初始化/* make sure the control block is properly initialized */bta_dm_init_cb();// 2. 服务发现启动bta_dm_disc_start(osi_property_get_bool("bluetooth.gatt.delay_close.enabled", true));memset(&bta_dm_conn_srvcs, 0, sizeof(bta_dm_conn_srvcs));memset(&bta_dm_di_cb, 0, sizeof(tBTA_DM_DI_CB));// 3. 设备类设置DEV_CLASS dev_class = btif_dm_get_local_class_of_device();log::info("Read default class of device [0x{:x}, 0x{:x}, 0x{:x}]",dev_class[0], dev_class[1], dev_class[2]);get_btm_client_interface().local.BTM_SetDeviceClass(dev_class);// 4. BLE 密钥加载/* load BLE local information: ID keys, ER if available */Octet16 er;btif_dm_get_ble_local_keys(&key_mask, &er, &id_key);if (key_mask & BTA_BLE_LOCAL_KEY_TYPE_ER) {get_btm_client_interface().security.BTM_BleLoadLocalKeys(BTA_BLE_LOCAL_KEY_TYPE_ER, (tBTM_BLE_LOCAL_KEYS*)&er);}if (key_mask & BTA_BLE_LOCAL_KEY_TYPE_ID) {get_btm_client_interface().security.BTM_BleLoadLocalKeys(BTA_BLE_LOCAL_KEY_TYPE_ID, (tBTM_BLE_LOCAL_KEYS*)&id_key);}// 5. 安全模块初始化btm_dm_sec_init();btm_sec_on_hw_on();// 6. page超时设置get_btm_client_interface().link_policy.BTM_WritePageTimeout(osi_property_get_int32(PROPERTY_PAGE_TIMEOUT,p_bta_dm_cfg->page_timeout));// 7. 控制器特性读取if (ble_vnd_is_included()) {get_btm_client_interface().ble.BTM_BleReadControllerFeatures(bta_dm_ctrl_features_rd_cmpl_cback);} else {/* Set controller features even if vendor support is not included */if (bta_dm_acl_cb.p_acl_cback)bta_dm_acl_cb.p_acl_cback(BTA_DM_LE_FEATURES_READ, NULL);}// 8. 扫描器初始化btm_ble_scanner_init();/* Earlier, we used to invoke BTM_ReadLocalAddr which was just copying thebd_addrfrom the control block and invoking the callback which was sending theDM_ENABLE_EVT.But then we have a few HCI commands being invoked above which were stillin progresswhen the ENABLE_EVT was sent. So modified this to fetch the local namewhich forcesthe DM_ENABLE_EVT to be sent only after all the init steps are complete*/// 9. 本地设备名称读取get_btm_client_interface().local.BTM_ReadLocalDeviceNameFromController(bta_dm_local_name_cback);// 10. 资源管理注册与电源管理初始化bta_sys_rm_register(bta_dm_rm_cback);/* if sniff is offload, no need to handle it in the stack */if (IS_FLAG_ENABLED(enable_sniff_offload) &&osi_property_get_bool(kPropertySniffOffloadEnabled, false)) {} else {/* initialize bluetooth low power manager */bta_dm_init_pm();}// 11. GATT 客户端注册bta_dm_disc_gattc_register();
}

初始化蓝牙设备管理器,设定设备基本参数,加载安全密钥,以及准备蓝牙通信所需的各项功能。保证在蓝牙硬件开启之后,软件层面的各项功能都能正常运作。

bta_dm_init_cb

packages/modules/Bluetooth/system/bta/dm/bta_dm_act.cc
#define BTA_DM_NUM_PM_TIMER 7
#define BTA_DM_PM_MODE_TIMER_MAX 3/********************************************************************************* Function         bta_dm_init_cb** Description      Initializes the bta_dm_cb control block*** Returns          void*******************************************************************************/
static void bta_dm_init_cb(void) {bta_dm_cb = {};// 这个定时器用于处理蓝牙禁用操作的超时情况bta_dm_cb.disable_timer = alarm_new("bta_dm.disable_timer");// 用于控制蓝牙状态切换时的延迟,避免状态频繁切换bta_dm_cb.switch_delay_timer = alarm_new("bta_dm.switch_delay_timer");// 电源管理定时器创建for (size_t i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {for (size_t j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) {bta_dm_cb.pm_timer[i].timer[j] = alarm_new("bta_dm.pm_timer");}}
}

初始化蓝牙设备管理器(Device Manager)的控制块 bta_dm_cb,该控制块用于存储蓝牙设备管理相关的状态和配置信息。

bta_dm_disc_start

packages/modules/Bluetooth/system/bta/dm/bta_dm_disc.cc
void bta_dm_disc_start(bool delay_close_gatt) {bta_dm_disc_reset();// 搜索定时器创建bta_dm_search_cb.search_timer = alarm_new("bta_dm_search.search_timer");// GATT 关闭定时器创建bta_dm_search_cb.gatt_close_timer =delay_close_gatt ? alarm_new("bta_dm_search.gatt_close_timer") : nullptr;// 发现请求队列初始化, 用于存储待处理的服务发现请求bta_dm_search_cb.pending_discovery_queue = fixed_queue_new(SIZE_MAX);
}

启动蓝牙设备的服务发现功能,该功能可让设备发现并获取远程蓝牙设备所提供的服务和特性信息。

函数主要完成以下工作:

  • 重置服务发现状态。

  • 创建搜索定时器和 GATT 关闭定时器。

  • 初始化待处理的发现请求队列。

bta_dm_disc_reset

packages/modules/Bluetooth/system/bta/dm/bta_dm_disc.cc
// 负责清理服务发现过程中使用的资源,释放定时器和队列,并初始化搜索控制块
static void bta_dm_disc_reset() {alarm_free(bta_dm_search_cb.search_timer);alarm_free(bta_dm_search_cb.gatt_close_timer);osi_free_and_reset((void**)&bta_dm_search_cb.p_pending_search);fixed_queue_free(bta_dm_search_cb.pending_discovery_queue, osi_free);bta_dm_disc_init_search_cb(::bta_dm_search_cb);
}// 将搜索控制块的成员变量设置为默认值,为新一轮的服务发现操作做好准备
static void bta_dm_disc_init_search_cb(tBTA_DM_SEARCH_CB& bta_dm_search_cb) {bta_dm_search_cb = {};bta_dm_search_cb.state = BTA_DM_SEARCH_IDLE;bta_dm_search_cb.conn_id = GATT_INVALID_CONN_ID;bta_dm_search_cb.transport = BT_TRANSPORT_AUTO;
}

通过彻底清理资源和重置状态,为后续的搜索操作提供了一个干净的环境。

btif_dm_get_local_class_of_device

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
#ifndef PROPERTY_CLASS_OF_DEVICE
#define PROPERTY_CLASS_OF_DEVICE "bluetooth.device.class_of_device"
#endif#ifndef PROPERTY_VALUE_MAX
#define PROPERTY_VALUE_MAX 92
#endif  // PROPERTY_VALUE_MAX/********************************************************************************* Function         btif_dm_get_local_class_of_device** Description      Reads the system property configured class of device** Returns          A DEV_CLASS containing the current class of device.*                  If no value is present, or the value is malformed*                  the default kEmpty value will be used*******************************************************************************/
DEV_CLASS btif_dm_get_local_class_of_device() {/* A class of device is a {SERVICE_CLASS, MAJOR_CLASS, MINOR_CLASS}** The input is expected to be a string of the following format:* <decimal number>,<decimal number>,<decimal number>** For example, "90,2,12" (Hex: 0x5A, 0x2, 0xC)** Notice there is always two commas and no spaces.*/// 1. 系统属性获取与默认值处理char prop_cod[PROPERTY_VALUE_MAX];osi_property_get(PROPERTY_CLASS_OF_DEVICE, prop_cod, "");// If the property is empty, use the defaultif (prop_cod[0] == '\0') {log::error("COD property is empty");return kDevClassUnclassified;}// 2. 字符串解析与合法性校验// Start reading the contents of the property string. If at any point anything// is malformed, use the default.DEV_CLASS temp_device_class;int i = 0;int j = 0;for (;;) {// Build a string of all the chars until the next comma, null, or end of the// buffer is reached. If any char is not a digit, then return the default.// 提取逗号分隔的数字字符串std::string value;while (i < PROPERTY_VALUE_MAX && prop_cod[i] != ',' &&prop_cod[i] != '\0') {char c = prop_cod[i++];if (!std::isdigit(c)) {log::error("COD malformed, '{:c}' is a non-digit", c);return kDevClassUnclassified;}value += c;}// If we hit the end and it wasn't null terminated then return the defaultif (i == PROPERTY_VALUE_MAX && prop_cod[PROPERTY_VALUE_MAX - 1] != '\0') {log::error("COD malformed, value was truncated");return kDevClassUnclassified;}// 校验字符串长度(0-3字符,对应0-255)// Each number in the list must be one byte, meaning 0 (0x00) -> 255 (0xFF)if (value.size() > 3 || value.size() == 0) {log::error("COD malformed, '{}' must be between [0, 255]", value);return kDevClassUnclassified;}// 转换为数值并校验范围(0-255)// Grab the value. If it's too large, then return the defaultuint32_t uint32_val = static_cast<uint32_t>(std::stoul(value.c_str()));if (uint32_val > 0xFF) {log::error("COD malformed, '{}' must be between [0, 255]", value);return kDevClassUnclassified;}// Otherwise, it's safe to usetemp_device_class[j++] = uint32_val;  // 存储到临时数组// 校验是否读取了3个数字且字符串结束// If we've reached 3 numbers then make sure we're at a null terminatorif (j >= 3) {if (prop_cod[i] != '\0') {log::error("COD malformed, more than three numbers");return kDevClassUnclassified;}break;}// If we're at a null terminator then we're doneif (prop_cod[i] == '\0') {break; // 跳过逗号}// Otherwise, skip over the comma++i;}// We must have read exactly 3 numbersDEV_CLASS device_class = kDevClassUnclassified;if (j == 3) {device_class[0] = temp_device_class[0];device_class[1] = temp_device_class[1];device_class[2] = temp_device_class[2];} else {log::error("COD malformed, fewer than three numbers");}log::debug("Using class of device '0x{:x}, 0x{:x}, 0x{:x}' from CoD system property",device_class[0], device_class[1], device_class[2]);// 3. Android 平台特定的 COD 调整
#ifdef __ANDROID__// Per BAP 1.0.1, 8.2.3. Device discovery, the stack needs to set Class of// Device (CoD) field Major Service Class bit 14 to 0b1 when Unicast Server,// Unicast Client, Broadcast Source, Broadcast Sink, Scan Delegator, or// Broadcast Assistant is supported on this deviceif (android::sysprop::BluetoothProperties::isProfileBapUnicastClientEnabled().value_or(false) ||android::sysprop::BluetoothProperties::isProfileBapBroadcastAssistEnabled().value_or(false) ||android::sysprop::BluetoothProperties::isProfileBapBroadcastSourceEnabled().value_or(false)) {device_class[1] |= 0x01 << 6; // 主设备类第6位设为1} else {device_class[1] &= ~(0x01 << 6); // 清除第6位}log::debug("Check LE audio enabled status, update class of device to '0x{:x}, ""0x{:x}, 0x{:x}'",device_class[0], device_class[1], device_class[2]);
#endifreturn device_class;
}

从系统属性中读取并解析本地蓝牙设备的设备类(Class of Device, COD)。COD 是蓝牙设备的重要标识,用于描述设备类型和支持的服务。

  1. 从系统属性 PROPERTY_CLASS_OF_DEVICE 读取 COD 配置字符串。

  2. 解析字符串为三个字节(服务类、主设备类、次设备类),并进行合法性校验。

  3. 针对 Android 平台,根据 BAP(蓝牙音频配置文件)规范调整主设备类的标志位。

  4. 返回最终的 COD 数组,若解析失败则使用默认值。

根据 BAP 1.0.1 规范,当设备支持以下功能时,需将主设备类的第 6 位(二进制第 14 位)设为 1:

  • 单播客户端(Unicast Client)

  • 广播助手(Broadcast Assistant)

  • 广播源(Broadcast Source)

该位用于标识设备支持 LE 音频相关功能,确保与其他蓝牙设备的兼容性。

COD 由三个字节组成:

  1. 服务类(Service Class):标识设备支持的服务(如电话、网络、音频等)。

  2. 主设备类(Major Device Class):标识设备的主要类型(如手机、电脑、耳机等)。

  3. 次设备类(Minor Device Class):对主设备类的细分(如手机的翻盖 / 直板类型)。

BTM_SetDeviceClass

packages/modules/Bluetooth/system/stack/btm/btm_devctl.cc
/********************************************************************************* Function         BTM_SetDeviceClass** Description      This function is called to set the local device class** Returns          status of the operation*******************************************************************************/
tBTM_STATUS BTM_SetDeviceClass(DEV_CLASS dev_class) {// 1. 重复设置优化if (btm_cb.devcb.dev_class == dev_class) return (BTM_SUCCESS);// 2. 更新软件状态btm_cb.devcb.dev_class = dev_class;// 3. 控制器状态校验if (!controller_get_interface()->get_is_ready()) return (BTM_DEV_RESET);// 4. HCI命令发送btsnd_hcic_write_dev_class(dev_class);return (BTM_SUCCESS);
}

通过 “软件状态更新 + HCI命令发送” 的方式,实现了本地设备类的设置。核心逻辑包括重复设置优化、控制器状态校验和HCI命令发送,确保设备类能正确应用到蓝牙控制器中,为设备发现和连接提供准确的类型标识。

对应的HCI log:

btif_dm_get_ble_local_keys

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
typedef uint8_t tBTA_DM_BLE_LOCAL_KEY_MASK;
typedef struct {Octet16 ir;Octet16 irk;Octet16 dhk;
} tBTA_BLE_LOCAL_ID_KEYS;void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask,Octet16* p_er,tBTA_BLE_LOCAL_ID_KEYS* p_id_keys) {ASSERT(p_key_mask != nullptr);  if (ble_local_key_cb.is_er_rcvd) {  // 检查加密根密钥是否已接收ASSERT(p_er != nullptr);  *p_er = ble_local_key_cb.er;  // 复制ER密钥*p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ER;  // 设置ER掩码位}if (ble_local_key_cb.is_id_keys_rcvd) {  // 检查身份解析密钥是否已接收ASSERT(p_id_keys != nullptr);  p_id_keys->ir = ble_local_key_cb.id_keys.ir;    // 复制IRp_id_keys->irk = ble_local_key_cb.id_keys.irk;  // 复制IRKp_id_keys->dhk = ble_local_key_cb.id_keys.dhk;  // 复制DHK*p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ID;  // 设置ID掩码位}log::verbose("*p_key_mask=0x{:02x}", *p_key_mask);  
}

获取本地 BLE设备的安全密钥,包括加密根密钥(ER)和身份解析密钥(ID Keys)。这些密钥用于保障 BLE 通信的安全性。主要功能:

  1. 从本地存储中获取 BLE 安全密钥(ER 和 ID Keys)。

  2. 通过密钥掩码(key_mask)标识可用的密钥类型。

  3. 将密钥数据填充到输出参数中,供上层模块使用。

密钥的作用与应用场景

1. 加密根密钥(ER)

  • 用于生成会话密钥,保障 BLE 连接的加密通信。

  • 当设备需要与其他设备建立安全连接时,ER 用于密钥派生。

2. 身份解析密钥(ID Keys)

  • irk(身份解析密钥):用于解析隐私地址,实现设备身份识别。

  • ir(身份解析随机数):与 irk 配合生成解析地址。

  • dhk(Diffie-Hellman 密钥):用于密钥协商,增强连接安全性。

BTM_BleLoadLocalKeys

packages/modules/Bluetooth/system/stack/btm/btm_ble_sec.cc
/********************************************************************************* Function         BTM_BleLoadLocalKeys** Description      Local local identity key, encryption root or sign counter.** Parameters:      key_type: type of key, can be BTM_BLE_KEY_TYPE_ID,*                                                BTM_BLE_KEY_TYPE_ER*                                             or BTM_BLE_KEY_TYPE_COUNTER.*                  p_key: pointer to the key.** Returns          non2.*******************************************************************************/
void BTM_BleLoadLocalKeys(uint8_t key_type, tBTM_BLE_LOCAL_KEYS* p_key) {tBTM_SEC_DEVCB* p_devcb = &btm_sec_cb.devcb; // 获取安全控制块指针log::verbose("type:{}", key_type);if (p_key != NULL) {  // 校验密钥指针有效性switch (key_type) {case BTM_BLE_KEY_TYPE_ID:// 加载身份解析密钥(复制整个结构体)memcpy(&p_devcb->id_keys, &p_key->id_keys,sizeof(tBTM_BLE_LOCAL_ID_KEYS));break;case BTM_BLE_KEY_TYPE_ER:// 加载加密根密钥(直接赋值16字节数据)p_devcb->ble_encryption_key_value = p_key->er;break;default:log::error("unknown key type:{}", key_type);break;}}
}

将上层提供的密钥数据存储到控制器的安全上下文中,为 BLE 通信的安全机制提供支持。

用于加载三种类型的本地密钥:

  1. 身份解析密钥(ID Keys):用于设备身份识别和隐私地址解析

  2. 加密根密钥(ER):用于生成会话加密密钥

  3. 签名计数器(Counter):用于安全签名机制

btm_dm_sec_init

packages/modules/Bluetooth/system/bta/dm/bta_dm_sec.cc
void btm_dm_sec_init() {get_btm_client_interface().security.BTM_SecRegister(&bta_security);
}

向底层控制器注册安全服务的回调。通过 BTM_SecRegister 函数,上层应用(BTA_DM)将自身的安全处理逻辑注册到底层,以便接收和处理蓝牙安全相关事件(如配对请求、密钥生成等)。

BTM_SecRegister

packages/modules/Bluetooth/system/stack/btm/btm_sec.cc
/********************************************************************************* Function         BTM_SecRegister** Description      Application manager calls this function to register for*                  security services.  There can be one and only one*                  application saving link keys.  BTM allows only first*                  registration.** Returns          true if registered OK, else false*******************************************************************************/
bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {log::info("p_cb_info->p_le_callback == 0x{}",fmt::ptr(p_cb_info->p_le_callback));// SMP 协议注册if (p_cb_info->p_le_callback) {log::verbose("SMP_Register( btm_proc_smp_cback )");SMP_Register(btm_proc_smp_cback);// 身份密钥状态检查Octet16 zero{0};/* 若未加载身份随机数,需重新生成所有密钥 *//* if no IR is loaded, need to regenerate all the keys */if (btm_sec_cb.devcb.id_keys.ir == zero) {btm_ble_reset_id();}} else {log::warn("p_cb_info->p_le_callback == NULL");}// 回调接口保存btm_sec_cb.api = *p_cb_info;log::info("btm_sec_cb.api.p_le_callback = 0x{} ",fmt::ptr(btm_sec_cb.api.p_le_callback));log::verbose("application registered");return (true);
}

注册上层应用的安全回调接口,是连接上层逻辑与底层安全机制的桥梁。主要完成以下工作:

  1. 注册安全管理器协议(SMP)的回调函数,处理 BLE 安全事件。

  2. 检查身份解析密钥状态,必要时重新生成密钥。

  3. 保存上层回调接口,以便底层事件触发时通知上层。

SMP_Register
packages/modules/Bluetooth/system/stack/smp/smp_api.cc
/********************************************************************************* Function         SMP_Register** Description      This function register for the SMP services callback.** Returns          void*******************************************************************************/
bool SMP_Register(tSMP_CALLBACK* p_cback) {log::verbose("state={}", smp_cb.state);if (smp_cb.p_callback != NULL) {log::error("duplicate registration, overwrite it");}smp_cb.p_callback = p_cback;return (true);
}

将上层回调函数注册到底层 SMP 模块。确保在蓝牙设备配对、密钥交换等安全操作过程中,底层事件能够正确通知到上层应用。

btm_ble_reset_id

btm_ble_reset_id后面分析

packages/modules/Bluetooth/system/bta/dm/bta_dm_sec.cc
tBTA_DM_SEC_CB bta_dm_sec_cb;  // 全局安全控制块void btm_sec_on_hw_on() {tBTA_DM_SEC_CBACK* temp_sec_cback = bta_dm_sec_cb.p_sec_cback;  // 保存当前回调指针bta_dm_sec_cb = {};  // 重置控制块为零值bta_dm_sec_cb.p_sec_cback = temp_sec_cback;  // 恢复回调指针
}

在蓝牙硬件启动时重置安全控制块的状态,同时保留上层注册的安全回调函数。确保安全模块在硬件重启后能以干净的状态运行,同时维持与上层的事件通信通道。

BTM_WritePageTimeout

packages/modules/Bluetooth/system/stack/btm/btm_devctl.cc
/********************************************************************************* Function         BTM_WritePageTimeout** Description      Send HCI Write Page Timeout.*******************************************************************************/
void BTM_WritePageTimeout(uint16_t timeout) {log::verbose("BTM: BTM_WritePageTimeout: Timeout: {}.", timeout);/* Send the HCI command */btsnd_hcic_write_page_tout(timeout);
}

向蓝牙控制器发送 HCI_Write_Page_Timeout 命令,设置page扫描的超时时间。

HCI Write Page Timeout log:

timeout 表示page超时值(单位为 10 毫秒),用于控制设备在连接建立过程中等待对端响应的最长时间。

Page超时的作用与原理

1. 蓝牙连接建立流程

  • 页面扫描(Page Scan):从设备(Slave)通过apge扫描监听主设备(Master)的连接请求。

  • 页面超时:主设备发送连接请求后,等待从设备响应的最长时间。若超时未收到响应,则认为连接失败。

2. 参数影响

  • 连接速度:较短的超时值(如 1 秒)可加快连接尝试频率,缩短连接建立时间,但增加功耗。

  • 功耗消耗:较长的超时值(如 10 秒)减少扫描频率,降低功耗,但可能延长连接时间。

  • 兼容性:需与对端设备的page超时设置匹配,否则可能导致连接失败(如一方设置过短,另一方未及时响应)。

HCI_Write_Page_Timeout详见:【0x0018】HCI_Write_Page_Timeout命令详解_write page timeout command-CSDN博客

ble_vnd_is_included

packages/modules/Bluetooth/system/stack/btm/btm_ble_gap.cc
bool ble_vnd_is_included() {// replace build time config BLE_VND_INCLUDED with runtimereturn GET_SYSPROP(Ble, vnd_included, true);
}packages/modules/Bluetooth/sysprop/ble.sysprop
prop {api_name: "vnd_included"type: Booleanscope: Internalaccess: Readonlyprop_name: "bluetooth.ble.vnd.included"
}

判断当前蓝牙系统是否包含厂商特定的 BLE 扩展功能(Vendor-specific Extensions)。核心逻辑是通过系统属性 bluetooth.ble.vnd.included 获取配置值,若属性不存在则默认返回 true(启用厂商扩展)。这种设计将编译时配置转换为运行时可调整的属性,既保持了系统的灵活性和兼容性,又为厂商定制功能提供了安全的接入方式,是现代蓝牙系统中解耦标准功能与定制需求的重要手段。

典型调用流程:

1. 系统启动时,通过init脚本或服务设置属性:setprop bluetooth.ble.vnd.included [true|false]2. 蓝牙协议栈初始化时,调用ble_vnd_is_included()获取属性值:if (ble_vnd_is_included()) {加载厂商特定的BLE功能(如私有命令处理、自定义配置文件)} else {仅运行标准BLE功能,忽略厂商扩展}3. 例如,在读取控制器特性时:if (ble_vnd_is_included()) {BTM_BleReadControllerFeatures(...)  // 读取包含厂商扩展的特性} else {直接通知上层特性读取完成(使用标准特性)}

BTM_BleReadControllerFeatures

packages/modules/Bluetooth/system/stack/btm/btm_ble_gap.cc
/******************************************************************************** Function         BTM_BleReadControllerFeatures** Description      Reads BLE specific controller features** Parameters:      tBTM_BLE_CTRL_FEATURES_CBACK : Callback to notify when*                  features are read** Returns          void*******************************************************************************/
void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK* p_vsc_cback) {// 若未启用厂商扩展,直接返回if (!ble_vnd_is_included()) return;if (btm_cb.cmn_ble_vsc_cb.values_read) return;log::verbose("BTM_BleReadControllerFeatures");if (IS_FLAG_ENABLED(report_vsc_data_from_the_gd_controller)) {// 处理GD控制器(直接从控制器接口获取特性)btm_cb.cmn_ble_vsc_cb.values_read = true;bluetooth::hci::Controller::VendorCapabilities vendor_capabilities =GetController()->GetVendorCapabilities();// 提取并保存各项控制器能力btm_cb.cmn_ble_vsc_cb.adv_inst_max =vendor_capabilities.max_advt_instances_;btm_cb.cmn_ble_vsc_cb.rpa_offloading =vendor_capabilities.offloaded_resolution_of_private_address_;btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg =vendor_capabilities.total_scan_results_storage_;btm_cb.cmn_ble_vsc_cb.max_irk_list_sz =vendor_capabilities.max_irk_list_sz_;btm_cb.cmn_ble_vsc_cb.filter_support =vendor_capabilities.filtering_support_;btm_cb.cmn_ble_vsc_cb.max_filter = vendor_capabilities.max_filter_;btm_cb.cmn_ble_vsc_cb.energy_support =vendor_capabilities.activity_energy_info_support_;btm_cb.cmn_ble_vsc_cb.version_supported =vendor_capabilities.version_supported_;btm_cb.cmn_ble_vsc_cb.total_trackable_advertisers =vendor_capabilities.total_num_of_advt_tracked_;btm_cb.cmn_ble_vsc_cb.extended_scan_support =vendor_capabilities.extended_scan_support_;btm_cb.cmn_ble_vsc_cb.debug_logging_supported =vendor_capabilities.debug_logging_supported_;btm_cb.cmn_ble_vsc_cb.le_address_generation_offloading_support =vendor_capabilities.le_address_generation_offloading_support_;btm_cb.cmn_ble_vsc_cb.a2dp_source_offload_capability_mask =vendor_capabilities.a2dp_source_offload_capability_mask_;btm_cb.cmn_ble_vsc_cb.quality_report_support =vendor_capabilities.bluetooth_quality_report_support_;btm_cb.cmn_ble_vsc_cb.dynamic_audio_buffer_support =vendor_capabilities.dynamic_audio_buffer_support_;btm_cb.cmn_ble_vsc_cb.a2dp_offload_v2_support =vendor_capabilities.a2dp_offload_v2_support_;// 动态音频缓冲区能力处理if (vendor_capabilities.dynamic_audio_buffer_support_) {std::array<bluetooth::hci::DynamicAudioBufferCodecCapability,BTM_CODEC_TYPE_MAX_RECORDS>capabilities = GetController()->GetDabCodecCapabilities();for (size_t i = 0; i < capabilities.size(); i++) {btm_cb.dynamic_audio_buffer_cb[i].default_buffer_time =capabilities[i].default_time_ms_;btm_cb.dynamic_audio_buffer_cb[i].maximum_buffer_time =capabilities[i].maximum_time_ms_;btm_cb.dynamic_audio_buffer_cb[i].minimum_buffer_time =capabilities[i].minimum_time_ms_;}}// 高通控制器特殊处理if (btm_cb.cmn_ble_vsc_cb.filter_support == 1 &&GetController()->GetLocalVersionInformation().manufacturer_name_ ==LMP_COMPID_QTI) {// QTI controller, TDS data filter are supported by default.btm_cb.cmn_ble_vsc_cb.adv_filter_extended_features_mask = 0x01;} else {btm_cb.cmn_ble_vsc_cb.adv_filter_extended_features_mask = 0x00;}log::verbose("irk={}, ADV ins:{}, rpa={}, ener={}, ext_scan={}",btm_cb.cmn_ble_vsc_cb.max_irk_list_sz,btm_cb.cmn_ble_vsc_cb.adv_inst_max,btm_cb.cmn_ble_vsc_cb.rpa_offloading,btm_cb.cmn_ble_vsc_cb.energy_support,btm_cb.cmn_ble_vsc_cb.extended_scan_support);// 初始化广告过滤器和解析列表if (btm_cb.cmn_ble_vsc_cb.max_filter > 0) btm_ble_adv_filter_init();/* VS capability included and non-4.2 device */if (GetController()->SupportsBle() &&GetController()->SupportsBlePrivacy() &&btm_cb.cmn_ble_vsc_cb.max_irk_list_sz > 0 &&GetController()->GetLeResolvingListSize() == 0) {btm_ble_resolving_list_init(btm_cb.cmn_ble_vsc_cb.max_irk_list_sz);}// 调用回调通知特性读取完成if (p_vsc_cback != NULL) {p_vsc_cback(tHCI_STATUS::HCI_SUCCESS);}} else {// 非GD控制器:通过厂商特定命令获取特性p_ctrl_le_feature_rd_cmpl_cback = p_vsc_cback;BTM_VendorSpecificCommand(HCI_BLE_VENDOR_CAP, 0, NULL,btm_ble_vendor_capability_vsc_cmpl_cback);}
}

获取控制器支持的厂商特定功能和扩展能力。

1. 特性读取目的:

  • 获取 BLE 控制器支持的厂商特定功能(如私有广告实例、隐私地址解析卸载等)。

  • 初始化控制器特性相关的全局状态,为后续 BLE 功能(如扫描、广告、连接)提供能力依据。

2. 执行流程:

  • 检查是否启用厂商扩展功能(ble_vnd_is_included())。

  • 根据控制器类型(标准 / GD 控制器)选择不同的特性获取方式。

  • 解析控制器能力并更新全局控制块。

  • 通过回调通知上层特性读取完成。

BTM_VendorSpecificCommand

packages/modules/Bluetooth/system/stack/btm/btm_devctl.cc
/********************************************************************************* Function         BTM_VendorSpecificCommand** Description      Send a vendor specific HCI command to the controller.** Notes*      Opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC.*******************************************************************************/
void BTM_VendorSpecificCommand(uint16_t opcode, uint8_t param_len,uint8_t* p_param_buf, tBTM_VSC_CMPL_CB* p_cb) {log::verbose("BTM: Opcode: 0x{:04X}, ParamLen: {}.", opcode, param_len);/* Send the HCI command (opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC) */btsnd_hcic_vendor_spec_cmd(opcode, param_len, p_param_buf, p_cb);
}

向蓝牙控制器发送厂商自定义的 HCI 命令,用于实现非标准的硬件功能或扩展特性。在硬件适配、性能优化和调试场景中至关重要,允许协议栈针对不同厂商的控制器进行定制化处理,确保蓝牙设备在保持标准兼容性的同时,充分发挥硬件特性。

HCI Vendor Command HCI LOG:

与标准 HCI 命令的区别:

类别标准 HCI 命令厂商特定 HCI 命令
定义来源蓝牙核心规范定义由蓝牙芯片厂商(如高通、博通)自定义
命令组标识非 0xFF(如链路控制组为 0x01)固定为 HCI_GRP_VENDOR_SPECIFIC(0xFF)
兼容性所有符合规范的控制器均支持仅特定厂商的控制器支持
用途实现标准蓝牙功能(如连接、配对、加密)实现厂商特定功能(如硬件优化、调试)

btm_ble_scanner_init

packages/modules/Bluetooth/system/stack/btm/btm_ble_scanner.cc
/*** This function initializes the scanning manager.**/
void btm_ble_scanner_init() {// 初始化HCI扫描接口:创建并初始化与 HCI 层交互的扫描接口BleScannerHciInterface::Initialize();if (BleScannerHciInterface::Get()) {// 初始化扫描管理器:启动扫描状态管理逻辑,处理扫描配置、事件和结果BleScanningManager::Initialize(BleScannerHciInterface::Get());} else {log::verbose("BleScannerHciInterface::Get() returns null");}if ((BleScannerHciInterface::Get()) && (BleScanningManager::Get())) {// 设置扫描事件观察者,建立接口与管理器的关联BleScannerHciInterface::Get()->SetScanEventObserver((BleScanningManagerImpl*)BleScanningManager::Get().get());} else {log::verbose("BleScannerHciInterface or BleScanningManager is null");}

通过初始化 HCI 扫描接口和扫描管理器,并建立两者的事件交互链路,为 BLE 设备发现功能提供了完整的底层支撑。运用单例模式和观察者模式,确保扫描功能的唯一性和事件处理的高效性,是蓝牙协议栈中设备发现流程的起点。初始化完成后,系统即可通过扫描管理器发起设备扫描,获取周围 BLE 设备的广告信息,为后续的连接和通信做准备。

BleScannerHciInterface::Initialize

packages/modules/Bluetooth/system/stack/btm/ble_scanner_hci_interface.cc
void BleScannerHciInterface::Initialize() {LOG_ASSERT(instance == nullptr) << "Was already initialized.";if ((controller_get_interface()->get_ble_periodic_advertiser_list_size()) &&(controller_get_interface()->SupportsBlePeriodicAdvertisingSyncTransferSender())) {log::info("Advertiser list in controller can be used");log::info("Periodic Adv Sync Transfer Sender role is supported");instance = new BleScannerCompleteImpl();  // 创建完整功能实现} else if (controller_get_interface()->SupportsBlePeriodicAdvertisingSyncTransferSender()) {log::info("Periodic Adv Sync Transfer Sender role is supported");instance = new BleScannerSyncTransferImpl();  // 创建同步传输实现} else if (controller_get_interface()->get_ble_periodic_advertiser_list_size()) {log::info("Periodic Adv Sync Transfer Recipient role is supported");instance = new BleScannerListImpl(); // 创建列表管理实现}// TODO: Implement periodic adv. sync. recipient role if ever needed.
}

根据蓝牙控制器支持的硬件特性,动态选择并创建合适的 BLE 扫描接口实现类,为上层扫描功能提供底层支持。通过检查控制器对周期性广播(Periodic Advertising)和同步传输(Sync Transfer)的支持情况,决定创建完整功能、同步传输或列表管理的扫描接口实现。

BleScanningManager::Initialize

packages/modules/Bluetooth/system/stack/btm/btm_ble_scanner.cc
void BleScanningManager::Initialize(BleScannerHciInterface* interface) {instance = new BleScanningManagerImpl(interface); // 创建具体实现类实例instance_weakptr = ((BleScanningManagerImpl*)instance)->GetWeakPtr();  // 获取弱引用
}

通过创建单例实例并建立弱引用机制,为 BLE 扫描功能提供了核心管理组件。该设计遵循了依赖倒置原则,将扫描管理器与底层 HCI 接口解耦,同时通过弱引用提升了异步操作的内存安全性。初始化完成后,扫描管理器可接收上层扫描请求,通过 HCI 接口与硬件交互,并处理扫描结果的分发,是 BLE 设备发现功能的核心枢纽。

数据流向:

上层扫描请求 → BleScanningManager → BleScannerHciInterface → HCI 命令
HCI 扫描事件 → BleScannerHciInterface → BleScanningManager → 上层处理

设计模式:

  • 单例模式:确保全局仅有一个扫描管理器实例。

  • 依赖注入:通过参数传入 BleScannerHciInterface,实现与底层 HCI 的解耦。

  • 弱引用机制:使用 WeakPtr 避免循环引用,提高内存安全性。

BTM_ReadLocalDeviceNameFromController

packages/modules/Bluetooth/system/stack/btm/btm_devctl.cc
/********************************************************************************* Function         BTM_ReadLocalDeviceNameFromController** Description      Get local device name from controller. Do not use cached*                  name (used to get chip-id prior to btm reset complete).** Returns          BTM_CMD_STARTED if successful, otherwise an error*******************************************************************************/
tBTM_STATUS BTM_ReadLocalDeviceNameFromController(tBTM_CMPL_CB* p_rln_cmpl_cback) {/* Check if rln already in progress */if (btm_cb.devcb.p_rln_cmpl_cb) return (BTM_NO_RESOURCES);/* Save callback */btm_cb.devcb.p_rln_cmpl_cb = p_rln_cmpl_cback;btsnd_hcic_read_name(); // 发送HCI命令读取设备名称// 设置超时定时器,处理可能的读取超时情况alarm_set_on_mloop(btm_cb.devcb.read_local_name_timer,BTM_DEV_NAME_REPLY_TIMEOUT_MS, btm_read_local_name_timeout,NULL);return BTM_CMD_STARTED;
}

直接从蓝牙控制器读取本地设备名称,绕过软件缓存,确保获取的是控制器当前的真实名称。主要用于蓝牙控制器重置完成前获取设备标识(如芯片 ID),或需要实时获取控制器名称的场景。

流程示意图:

上层调用 BTM_ReadLocalDeviceNameFromController()
↓
检查并发 → 保存回调 → 发送HCI命令 → 设置超时定时器 → 返回BTM_CMD_STARTED
↓
控制器处理HCI命令并返回HCI_Read_Local_Name_Complete事件
↓
底层处理事件 → 调用保存的回调函数 → 上层获取设备名称

HCI Read Local Name HCI LOG:

HCI Read Local Name详细参考: 【0x0014】HCI_Read_Local_Name命令详解_蓝牙hci标准命令-CSDN博客

bta_sys_rm_register

packages/modules/Bluetooth/system/bta/sys/bta_sys_conn.cc
/********************************************************************************* Function         bta_sys_rm_register** Description      Called by BTA DM to register role management callbacks*** Returns          void*******************************************************************************/
void bta_sys_rm_register(tBTA_SYS_CONN_CBACK* p_cback) {bta_sys_cb.prm_cb = p_cback;
}

将上层提供的回调函数指针存储到系统控制块中,供底层在事件发生时调用。遵循了 “注册 - 通知” 的事件驱动模式,是蓝牙系统中模块间通信的基础机制之一,确保了上层逻辑与底层功能的解耦和灵活适配。

bta_dm_init_pm

packages/modules/Bluetooth/system/bta/dm/bta_dm_pm.cc
tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs;/********************************************************************************* Function         bta_dm_init_pm** Description      Initializes the BT low power manager*** Returns          void*******************************************************************************/
void bta_dm_init_pm(void) {// 1. 清零连接服务状态结构体memset(&bta_dm_conn_srvcs, 0x00, sizeof(bta_dm_conn_srvcs));// 2. 检查是否存在有效的电源管理器配置/* if there are no power manger entries, so not register */if (p_bta_dm_pm_cfg[0].app_id != 0) {// 注册系统级电源管理回调bta_sys_pm_register(bta_dm_pm_cback);bta_sys_sniff_register(bta_dm_sniff_cback);// 注册BTM级电源管理回调get_btm_client_interface().lifecycle.BTM_PmRegister((BTM_PM_REG_SET), &bta_dm_cb.pm_id, bta_dm_pm_btm_cback);}// 初始化低功耗计时器服务ID数组/* Need to initialize all PM timer service IDs */for (int i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {for (int j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++)bta_dm_cb.pm_timer[i].srvc_id[j] = BTA_ID_MAX;}
}

通过初始化连接服务状态、注册电源管理回调和配置低功耗计时器,为蓝牙设备的功耗优化奠定基础。根据配置动态决定是否启用功耗管理功能,并建立了从系统层到 BTM 层的完整回调链路,使上层应用能够根据连接状态和活动情况灵活调整功耗策略。这对于资源受限的移动设备尤为重要,是蓝牙协议栈中实现能效优化的关键环节。

bta_dm_disc_gattc_register

packages/modules/Bluetooth/system/bta/dm/bta_dm_disc.cc
void bta_dm_disc_gattc_register() { bta_dm_gattc_register(); }/********************************************************************************* Function         bta_dm_gattc_register** Description      Register with GATTC in DM if BLE is needed.*** Returns          void*******************************************************************************/
static void bta_dm_gattc_register(void) {// 1. 检查是否已注册if (bta_dm_search_cb.client_if != BTA_GATTS_INVALID_IF) {// Already registeredreturn;}// 2. 调用GATT接口注册get_gatt_interface().BTA_GATTC_AppRegister(bta_dm_gattc_callback,  // 主回调函数,处理GATT事件base::Bind([](uint8_t client_id, uint8_t status) { // 注册结果回调tGATT_STATUS gatt_status = static_cast<tGATT_STATUS>(status);disc_gatt_history_.Push(base::StringPrintf("%-32s client_id:%hu status:%s", "GATTC_RegisteredCallback",client_id, gatt_status_text(gatt_status).c_str()));// 处理注册结果if (static_cast<tGATT_STATUS>(status) == GATT_SUCCESS) {log::info("Registered device discovery search gatt client tGATT_IF:{}",client_id);bta_dm_search_cb.client_if = client_id; // 保存有效客户端ID} else {log::warn("Failed to register device discovery search gatt client ""gatt_status:{} previous tGATT_IF:{}",bta_dm_search_cb.client_if, status);bta_dm_search_cb.client_if = BTA_GATTS_INVALID_IF;  // 重置为无效ID}}),false);
}
  • 向 GATT 客户端注册设备发现功能,获取唯一的客户端接口 ID(client_if)。

  • 确保注册操作的幂等性(多次调用不会重复注册)。

  • 处理注册结果回调,更新本地状态并记录日志。

注册成功后,上层模块可使用分配的客户端 ID 发起 GATT 操作,是蓝牙设备发现流程中的关键初始化步骤。

三、流程图

四、时序图

BTA_DM 硬件开启初始化通过分层初始化机制,实现了从硬件启动到软件功能就绪的完整流程。该过程通过控制块状态重置、设备参数配置、安全密钥加载、功能模块初始化等核心环节,确保蓝牙设备能够安全、高效地启动并提供服务。各模块通过回调机制与事件驱动模式协同工作,形成了层次清晰、可扩展的初始化架构,为后续的设备发现、连接建立和数据传输奠定了基础。

关键字

BTA_dm_on_hw_on|btif_dm_get_local_class_of_device|btif_dm_get_ble_local_keys|BTM_BleLoadLocalKeys|BTM_SecRegister|SMP_Register|BTM_WritePageTimeout:|BTM_BleReadControllerFeatures:|BTM_VendorSpecificCommand|btm_ble_scanner_init|bluetooth: Initialize|BTM_SecRegister|SMP_Register|BTA_GATTC_AppRegister

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87145.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大语言模型:是逐字生成还是一次多词?

大语言模型(LLM)既可以按顺序逐个生成单词(token),也能实现一次生成多个 token 核心差异源于解码策略与模型架构设计 一、常规“逐个生成”模式(基础逻辑) 多数入门级演示或简单文本生成中,LLM 会默认按 “生成一个 token → 拼接回输入 → 再生成下一个” 的流程,…

通俗易懂的LangGraph图定义解析

LangGraph 是一个基于状态的工作流框架&#xff0c;它通过 节点&#xff08;Nodes&#xff09; 和 边&#xff08;Edges&#xff09; 的组合&#xff0c;构建出复杂的工作流逻辑。这种设计特别适合处理需要动态决策、循环、多步骤交互的场景&#xff08;比如对话系统、智能代理…

K8s Pod调度基础——2

目录 一、Deployment ‌一、Deployment 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ StatefulSet ‌一、StatefulSet 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ 彼此的区别 一、本质…

Java 大视界 -- Java 大数据在智能医疗健康管理中的慢性病风险预测与个性化干预(330)

Java 大视界 -- Java 大数据在智能医疗健康管理中的慢性病风险预测与个性化干预&#xff08;330&#xff09; 引言&#xff1a;正文&#xff1a;一、Java 构建的医疗数据融合平台&#xff08;多源数据安全打通&#xff09;1.1 分布式医疗数据集成系统&#xff08;符合 HIPAA 与…

beego打包发布到Centos系统及国产麒麟系统完整教程

1、先清除go缓存&#xff0c;用下面命令 go clean -cache go clean -modcache 2、更新库文件 go mod tidy 3、安装beego go install github.com/beego/bee/v2latest 4、查看bee版本 5、进行打包然后传到Centos和麒麟服务器如下代码 bee pack -be GOOSlinux -be GOARCHa…

Instagram和facebook广告对比解析

一、平台用户画像对比 用户基础数据 &#xff08;1&#xff09;活跃用户规模 Instagram&#xff1a;20亿MAU&#xff0c;以年轻群体为主力 Facebook&#xff1a;29亿MAU&#xff0c;覆盖全年龄段用户 &#xff08;2&#xff09;核心用户特征 Instagram&#xff1a; • 25-3…

[MIA 2025]CLIP in medical imaging: A survey

论文网址&#xff1a;CLIP in medical imaging: A survey - ScienceDirect 项目页面&#xff1a;github.com 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏…

Python通讯录系统实战教程

具体介绍见 通讯录管理系统设计与实现&#xff08;C&#xff09;-CSDN博客 class Person:def __init__(self, name"", sex0, age0, phone"", addr""):self.m_name name # 姓名self.m_Sex sex # 性别&#xff08;1-男&#xff0c;2-女…

虾米壁纸分类页面代码

<template> <view class"wallpaper-category"> <custom-nav-bar title"分类列表"></custom-nav-bar> <!-- 分类展示 --> <scroll-view scroll-y class"category-scroll-view"> <view cl…

K8s-pod 调度基础

目录 Replication Controller&#xff08;RC&#xff09; 概念 关键字段 Replica Set&#xff08;RS&#xff09; 概念 关键字段 RC 与 RS 的区别 无状态应用管理Deployment 无状态应用&#xff08;Stateless Application&#xff09; 什么是无状态&#xff1f; 无状…

Vue + RuoYi 前后端分离入门手册

Vue RuoYi 前后端分离技术栈是一个非常流行且成熟的企业级后台管理系统开发方案&#xff0c;尤其在国内 Java 开发社区中广泛应用。它结合了现代化的前端框架 Vue.js 和基于 Spring Boot 的后端框架 RuoYi&#xff0c;提供了开箱即用的权限管理、代码生成、监控等功能&#xf…

JSON 安装使用教程

一、JSON 简介 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。它广泛应用于前后端数据通信、配置文件、API 传输等场景。 二、JSON 是否需要安装&#xff1f; 不需要…

十大网络协议

十大网络协议 标题1. HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;标题2. HTTPS&#xff08;Secure Hypertext Transfer Protocol&#xff0c;安全超文本传输协议&#xff09;标题3. HTTP/3标题4. TCP&#xff08;Transmission Control…

【语音告警】博灵智能语音报警灯Modbus TCP触发告警实例-语音报警灯|声光报警器|网络信号灯

功能说明 本文将以Python代码为例&#xff0c;讲解如何通过Python代码调用博灵语音通知终端A4实现声光语音告警。 本代码实现Python触发Modbus写多寄存器和写单寄存器实现调用通知终端模板播报功能&#xff08;通知终端内置TTS语音合成技术&#xff0c;本案例不讲解如何文本转…

摄像头 rtsp数据量 和正常数据流有什么区别

摄像头RTSP数据流和正常数据流&#xff08;如HTTP传输的普通文件或网页数据&#xff09;在多个方面存在显著差异&#xff0c;主要体现在协议特性、数据量、实时性、应用场景等方面。以下是具体对比&#xff1a; 1. 协议与传输方式 RTSP流&#xff1a; 实时流协议&#xff08;R…

深入理解装饰器模式:动态扩展对象功能的灵活设计模式

深入理解装饰器模式&#xff1a;动态扩展对象功能的灵活设计模式 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量世界…

141.在 Vue 3 中使用 OpenLayers Link 交互:把地图中心点 / 缩放级别 / 旋转角度实时写进 URL,并同步解析显示

本文分享一个前端小技巧&#xff1a;借助 OpenLayers 的 Link 交互 在浏览器地址栏实时记录地图状态&#xff0c;同时把这些参数解析出来展示在页面上。 ✨ 双向同步&#xff1a;拖动、缩放、旋转地图时&#xff0c;URL 自动更新&#xff1b;手动修改 URL 或后退 / 前进&#x…

数字人的形象与内容,虚拟形象背后的权益暗战

&#xff08;首席数据官高鹏律师数字经济团队创作&#xff0c;AI辅助&#xff09; 当某科技公司的虚拟偶像在直播间收获百万打赏时&#xff0c;当某品牌的数字代言人形象被篡改成表情包全网传播时&#xff0c;当网红博主的AI分身开始替代真人直播带货时&#xff0c;一场关于数…

【python】pdf拆成图片,加中文,再合成pdf

前期搞了个pdf加页脚&#xff0c;但是搞了半天中文加不了&#xff0c;就换了个思路。 直接说结论&#xff0c;pdf拆成图片&#xff0c;加中文&#xff0c;再合成pdf&#xff0c;会导致pdf模糊。 import os import fitz # PyMuPDF from PIL import Image, ImageDraw, ImageFon…

分布式爬虫数据存储开发实战

分布式爬虫存储的核心矛盾在于&#xff1a;既要高吞吐又要强一致性&#xff0c;还要避免重复。比如Kafka虽然吞吐高但无法去重&#xff0c;Redis去重快但容量有限。所以我们可能低估了状态同步的复杂度——比如暂停爬虫时如何保证内存中的URL状态不丢失。 分布式爬虫的数据存储…