【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
英文地址,請注明出處:http://en.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狀態不丟失。 分布式爬蟲的數據存儲…