本文深入剖析Bluedroid蓝牙协议栈中 SDP(服务发现协议)服务记录的全生命周期管理流程,涵盖初始化、记录创建、服务搜索、记录删除等核心环节。通过解析代码逻辑与数据结构,揭示各模块间的协作机制,包括线程安全设计、回调机制及协议栈交互细节,为蓝牙服务开发与调试提供系统性参考。
一、概述
蓝牙SDP协议用于设备间服务发现,其实现分为四层:
-
接口层(btif):提供
btif_sdp_get_interface
接口,封装init
、search
、create_sdp_record
等操作。 -
业务逻辑层(BTA):通过
BTA_SdpSearch
、BTA_SdpCreateRecordByUser
派发任务到主线程。 -
协议栈层(Stack):实现SDP数据库管理(
SDP_CreateRecord
、SDP_AddAttribute
)。 -
资源管理层:通过槽位(
sdp_slots
)管理记录内存,支持动态分配与释放。
关键设计:
-
线程安全:使用
std::recursive_mutex
保护槽位操作。 -
内存优化:预计算记录大小实现紧凑存储。
-
异步模型:通过
do_in_main_thread
解耦调用与执行。
二、源码解读
2.1 SDP 模块初始化与架构设计
①接口封装:通过btif_sdp_get_interface
暴露统一接口结构体sdp_if
,包含初始化(init
)、反初始化(deinit
)、搜索(search
)、记录创建(create_sdp_record
)和删除(remove_sdp_record
)等核心功能,实现模块解耦。
②初始化流程
-
init
函数注册回调bt_sdp_callbacks
,调用sdp_server_init
初始化槽位(sdp_slots
),并通过btif_enable_service
启用 SDP 服务。 -
sdp_server_init
初始化固定大小的槽位数组(MAX_SDP_SLOTS=128
),标记所有槽位为空闲(SDP_RECORD_FREE
)。
③线程安全:关键操作(如槽位分配、释放)通过std::recursive_mutex
加锁,避免多线程竞争。
④deinit
:init的反向操作,释放槽位内存并禁用服务。
btif_sdp_get_interface
packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
// 使用初始化列表对 sdp_if 进行初始化
static const btsdp_interface_t sdp_if = {sizeof(btsdp_interface_t), init, deinit, search, create_sdp_record,remove_sdp_record};// 返回定义的 SDP 接口结构体实例的指针。其他模块可以通过调用这个函数来获取 SDP 接口的访问权限
const btsdp_interface_t* btif_sdp_get_interface(void) {log::verbose("");return &sdp_if;
}
定义了一个 SDP 接口结构体实例,封装 SDP 接口的实现,并提供一个统一的接口供其他模块使用。通过 btif_sdp_get_interface
函数,其他模块可以获取到 SDP 接口的指针,从而调用接口中定义的各种功能函数,如初始化、搜索服务、创建和移除 SDP 记录等。
init
packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static btsdp_callbacks_t* bt_sdp_callbacks = NULL;static bt_status_t init(btsdp_callbacks_t* callbacks) {log::verbose("Sdp Search Init");bt_sdp_callbacks = callbacks;sdp_server_init(); // 对 SDP 服务器进行初始化btif_enable_service(BTA_SDP_SERVICE_ID); // 启用 SDP 服务return BT_STATUS_SUCCESS;
}
对蓝牙 SDP(Service Discovery Protocol,服务发现协议)进行初始化操作。SDP 协议用于在蓝牙设备之间发现可用的服务及其相关信息。
sdp_server_init
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
bt_status_t sdp_server_init() {log::verbose("Sdp Server Init");init_sdp_slots();return BT_STATUS_SUCCESS;
}
对蓝牙 SDP服务器进行初始化操作。调用 init_sdp_slots
函数执行具体的初始化工作,最后返回表示操作成功的状态码。
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
typedef union {bluetooth_sdp_hdr_overlay hdr;bluetooth_sdp_mas_record mas;bluetooth_sdp_mns_record mns;bluetooth_sdp_pse_record pse;bluetooth_sdp_pce_record pce;bluetooth_sdp_ops_record ops;bluetooth_sdp_sap_record sap;bluetooth_sdp_dip_record dip;bluetooth_sdp_mps_record mps;
} bluetooth_sdp_record;typedef struct {sdp_state_t state;int sdp_handle;bluetooth_sdp_record* record_data;
} sdp_slot_t;#define MAX_SDP_SLOTS 128
static sdp_slot_t sdp_slots[MAX_SDP_SLOTS];static void init_sdp_slots() {int i;memset(sdp_slots, 0, sizeof(sdp_slot_t) * MAX_SDP_SLOTS);/* if SDP_RECORD_FREE is zero - no need to set the value */if (SDP_RECORD_FREE != 0) {for (i = 0; i < MAX_SDP_SLOTS; i++) {sdp_slots[i].state = SDP_RECORD_FREE;}}
}
对 SDP 槽位进行初始化,确保所有槽位在使用前都处于空闲状态。
btif_enable_service(BTA_SDP_SERVICE_ID)
packages/modules/Bluetooth/system/btif/src/btif_core.cc
typedef uint32_t tBTA_SERVICE_MASK;static tBTA_SERVICE_MASK btif_enabled_services = 0;/********************************************************************************* Function btif_enable_service** Description Enables the service 'service_ID' to the service_mask.* Upon BT enable, BTIF core shall invoke the BTA APIs to* enable the profiles*******************************************************************************/
void btif_enable_service(tBTA_SERVICE_ID service_id) {btif_enabled_services |= (1 << service_id); // 设置服务掩码,表示该服务已启用log::verbose("current services:0x{:x}", btif_enabled_services);if (btif_is_enabled()) {btif_dm_enable_service(service_id, true); // 向蓝牙协议栈发送指令,实际启用该服务}
}
启用 SDP 服务。
deinit
packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static bt_status_t deinit() {log::verbose("Sdp Search Deinit");bt_sdp_callbacks = NULL;sdp_server_cleanup();btif_disable_service(BTA_SDP_SERVICE_ID);return BT_STATUS_SUCCESS;
}
清理蓝牙 SDP相关资源并禁用 SDP 服务。包括清空回调函数指针、清理 SDP 服务器和禁用 SDP 服务等操作。
sdp_server_cleanup
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
void sdp_server_cleanup() {log::verbose("Sdp Server Cleanup");std::unique_lock<std::recursive_mutex> lock(sdp_lock);int i;for (i = 0; i < MAX_SDP_SLOTS; i++) {/*remove_sdp_record(i); we cannot send messages to the other threads, since* they might* have been shut down already. Just do local cleanup.*/free_sdp_slot(i);}
}
对蓝牙 SDP 服务器进行清理,确保服务器在关闭时释放所有占用的资源。通过加锁操作保证了清理过程的线程安全,避免了多线程环境下的资源竞争问题。同时,由于考虑到其他线程可能已经关闭,只进行本地清理操作。
free_sdp_slot
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
static int free_sdp_slot(int id) {int handle = -1;bluetooth_sdp_record* record = NULL;if (id < 0 || id >= MAX_SDP_SLOTS) {log::error("failed - id {} is invalid", id);return handle;}{std::unique_lock<std::recursive_mutex> lock(sdp_lock);handle = sdp_slots[id].sdp_handle;sdp_slots[id].sdp_handle = 0;if (sdp_slots[id].state != SDP_RECORD_FREE) {/* safe a copy of the pointer, and free after unlock() */record = sdp_slots[id].record_data;}sdp_slots[id].state = SDP_RECORD_FREE;}if (record != NULL) {osi_free(record);} else {// Record have already been freedhandle = -1;}return handle;
}
通过对传入的 ID 进行有效性检查、加锁确保线程安全、清理槽位信息和释放记录数据,实现了对指定 SDP 槽位的释放操作。在释放过程中,遵循了先保存数据再释放内存的原则,避免了在持有锁的情况下进行内存操作,提高了程序的并发性能和健壮性。
2.2 服务搜索流程
①异步搜索发起:search
函数调用BTA_SdpSearch
,通过do_in_main_thread
在主线程启动搜索:
-
检查当前 SDP 操作状态(
sdp_active
),避免并发搜索。 -
初始化发现数据库(
SDP_InitDiscoveryDb
),设置 UUID 和属性过滤器。 -
发起 L2CAP 连接(
sdp_conn_originate
),通过SDP_ServiceSearchAttributeRequest2
执行搜索请求。
②连接与安全策略
-
L2CA_ConnectReq2
设置安全级别(BTM_SetSecurityLevel
),确保连接符合协议规范(如加密需伴随认证)。 -
sdp_conn_originate
分配连接控制块(CCB),处理链路状态(已连接 / 断开中),确保连接请求可靠传递。
③结果回调:搜索完成后通过bta_sdp_search_cback
触发BTA_SDP_SEARCH_COMP_EVT
,sdp_dm_cback
调用btif_transfer_context
深拷贝结果数据,避免跨模块指针引用问题。
search
/packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static bt_status_t search(RawAddress* bd_addr, const Uuid& uuid) {BTA_SdpSearch(*bd_addr, uuid);return BT_STATUS_SUCCESS;
}
BTA_SdpSearch
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_api.cc
/********************************************************************************* Function BTA_SdpSearch** Description This function performs service discovery for a specific* service on given peer device. When the operation is* completed the tBTA_SDP_DM_CBACK callback function will be* called with a BTA_SDP_SEARCH_COMPLETE_EVT.** Returns BTA_SDP_SUCCESS, if the request is being processed.* BTA_SDP_FAILURE, otherwise.*******************************************************************************/
tBTA_SDP_STATUS BTA_SdpSearch(const RawAddress& bd_addr,const bluetooth::Uuid& uuid) {do_in_main_thread(FROM_HERE, base::BindOnce(bta_sdp_search, bd_addr, uuid));return BTA_SDP_SUCCESS;
}
在指定的蓝牙对等设备上执行特定服务的发现操作。启动一个异步的蓝牙 SDP 服务发现操作。将服务发现任务封装在一个一次性的任务中,并在主线程中异步执行。函数立即返回 BTA_SDP_SUCCESS
,表示请求正在处理,而实际的操作结果会通过回调函数通知调用者。提高了程序的响应性,避免阻塞主线程。
当服务发现操作完成后,会调用 tBTA_SDP_DM_CBACK
回调函数,并传递 BTA_SDP_SEARCH_COMPLETE_EVT
事件。该函数返回一个 tBTA_SDP_STATUS
类型的值,表示请求是否正在处理。
bta_sdp_search
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_act.cc
/********************************************************************************* Function bta_sdp_search** Description Discovers all sdp records for an uuid on remote device** Returns void*******************************************************************************/
void bta_sdp_search(const RawAddress bd_addr, const bluetooth::Uuid uuid) {tBTA_SDP_STATUS status = BTA_SDP_FAILURE;log::verbose("in, sdp_active:{}", bta_sdp_cb.sdp_active);// 1. 检查 SDP 是否正在进行if (bta_sdp_cb.sdp_active) { // 表示 SDP 操作正在进行/* SDP is still in progress */status = BTA_SDP_BUSY;if (bta_sdp_cb.p_dm_cback) {tBTA_SDP_SEARCH_COMP result;memset(&result, 0, sizeof(result));result.uuid = uuid;result.remote_addr = bd_addr;result.status = status;tBTA_SDP bta_sdp;bta_sdp.sdp_search_comp = result;bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp, NULL); // 通知调用者SDP操作忙碌}return;}// 2. 启动 SDP 操作bta_sdp_cb.sdp_active = true; // 表示 SDP 操作开始bta_sdp_cb.remote_addr = bd_addr;/* initialize the search for the uuid */log::verbose("init discovery with UUID: {}", uuid.ToString());get_legacy_stack_sdp_api()->service.SDP_InitDiscoveryDb(p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1, &uuid, 0, NULL); // 初始化 SDP 发现数据库// 3. 启动 SDP 服务搜索请求Uuid* bta_sdp_search_uuid = (Uuid*)osi_malloc(sizeof(Uuid));*bta_sdp_search_uuid = uuid;// 发起 SDP 服务搜索属性请求if (!get_legacy_stack_sdp_api()->service.SDP_ServiceSearchAttributeRequest2(bd_addr, p_bta_sdp_cfg->p_sdp_db, bta_sdp_search_cback,(void*)bta_sdp_search_uuid)) {bta_sdp_cb.sdp_active = false; // 表示 SDP 操作结束/* failed to start SDP. report the failure right away */if (bta_sdp_cb.p_dm_cback) {tBTA_SDP_SEARCH_COMP result;memset(&result, 0, sizeof(result));result.uuid = uuid;result.remote_addr = bd_addr;result.status = status;tBTA_SDP bta_sdp;bta_sdp.sdp_search_comp = result;bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp, NULL); // 通知调用者SDP操作失败bluetooth::shim::CountCounterMetrics(android::bluetooth::CodePathCounterKeyEnum::SDP_FAILURE, 1);}}/*else report the result when the cback is called*/
}
在远程蓝牙设备上发现指定 UUID 对应的所有 SDP记录。通过检查 SDP 操作状态,避免了同时进行多个 SDP 操作。在 SDP 操作空闲时,会初始化 SDP 发现数据库并发起服务搜索请求。若请求失败,会立即通知回调函数;若请求成功,结果将在回调函数 bta_sdp_search_cback
被调用时处理。
SDP_InitDiscoveryDb
packages/modules/Bluetooth/system/stack/sdp/sdp_api.cc
/********************************************************************************* Function SDP_InitDiscoveryDb** Description This function is called to initialize a discovery database.** Parameters: p_db - (input) address of an area of memory where the* discovery database is managed.* len - (input) size (in bytes) of the memory* NOTE: This must be larger than* sizeof(tSDP_DISCOVERY_DB)* num_uuid - (input) number of UUID filters applied* p_uuid_list - (input) list of UUID filters* num_attr - (input) number of attribute filters applied* p_attr_list - (input) list of attribute filters*** Returns bool* true if successful* false if one or more parameters are bad*******************************************************************************/
bool SDP_InitDiscoveryDb(tSDP_DISCOVERY_DB* p_db, uint32_t len,uint16_t num_uuid, const Uuid* p_uuid_list,uint16_t num_attr, const uint16_t* p_attr_list) {// 1. 参数验证uint16_t xx;/* verify the parameters */if (p_db == NULL || (sizeof(tSDP_DISCOVERY_DB) > len) ||num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS) {log::error("SDP_InitDiscoveryDb Illegal param: p_db {}, len {}, num_uuid {}, ""num_attr {}",fmt::ptr(p_db), len, num_uuid, num_attr);return (false);}// 2. 数据库内存初始化memset(p_db, 0, (size_t)len);p_db->mem_size = len - sizeof(tSDP_DISCOVERY_DB); // 计算数据库可用于存储记录的有效内存大小p_db->mem_free = p_db->mem_size; // 初始化可用内存大小为总有效内存大小p_db->p_first_rec = NULL;p_db->p_free_mem = (uint8_t*)(p_db + 1); // 将指向可用内存起始位置的指针设置为p_db结构体之后的地址// 3. UUID 过滤器设置// 将 p_uuid_list中的 UUID 过滤器依次复制到 p_db->uuid_filters 数组中for (xx = 0; xx < num_uuid; xx++) p_db->uuid_filters[xx] = *p_uuid_list++;p_db->num_uuid_filters = num_uuid; // 记录实际使用的 UUID 过滤器数量// 4. 属性过滤器设置及排序for (xx = 0; xx < num_attr; xx++) p_db->attr_filters[xx] = *p_attr_list++;/* sort attributes */sdpu_sort_attr_list(num_attr, p_db); // 对属性过滤器列表进行排序,以便后续处理p_db->num_attr_filters = num_attr; //记录实际使用的属性过滤器数量return (true);
}
初始化一个用于蓝牙服务发现协议(SDP)的发现数据库。该数据库用于存储和管理在服务发现过程中检索到的信息。通过对传入参数的严格验证和对发现数据库的初始化设置,确保了数据库在使用前处于正确的状态。设置了数据库的内存信息、UUID 过滤器和属性过滤器,并对属性过滤器进行排序,为后续的服务发现操作提供了基础。
SDP_ServiceSearchAttributeRequest2
packages/modules/Bluetooth/system/stack/sdp/sdp_api.cc
/********************************************************************************* Function SDP_ServiceSearchAttributeRequest2** Description This function queries an SDP server for information.** The difference between this API function and the function* SDP_ServiceSearchRequest is that this one does a* combined ServiceSearchAttributeRequest SDP function.* (This is for Unplug Testing)** Returns true if discovery started, false if failed.*******************************************************************************/
bool SDP_ServiceSearchAttributeRequest2(const RawAddress& p_bd_addr,tSDP_DISCOVERY_DB* p_db,tSDP_DISC_CMPL_CB2* p_cb2,const void* user_data) {//1. 发起连接tCONN_CB* p_ccb; // 表示连接控制块,用于管理与 SDP 服务器的连接/* Specific BD address */p_ccb = sdp_conn_originate(p_bd_addr); // 尝试发起与指定设备的 SDP 连接if (!p_ccb) return (false);//2. 配置连接控制块p_ccb->disc_state = SDP_DISC_WAIT_CONN; // 表示当前正在等待与 SDP 服务器建立连接p_ccb->p_db = p_db; // 将传入的发现数据库指针赋值给连接控制块,以便后续将服务发现结果存储到该数据库中p_ccb->p_cb2 = p_cb2; // 将传入的回调函数指针赋值给连接控制块,以便在服务发现完成时调用该回调函数p_ccb->is_attr_search = true; // 表示这是一个属性搜索请求p_ccb->user_data = user_data; // 将用户自定义的数据指针赋值给连接控制块,以便在回调函数被调用时传递给它return (true);
}
通过发起与目标蓝牙设备的 SDP 连接,并配置连接控制块的相关信息,为服务发现过程做好准备。若连接发起成功,则返回 true
表示发现过程已启动;若连接发起失败,则返回 false
。为后续的服务发现操作奠定了基础。
与 SDP_ServiceSearchRequest
函数的区别在于,此函数执行的是一个组合的 ServiceSearchAttributeRequest
SDP 功能,为了进行拔插测试(Unplug Testing)而设计。函数返回一个布尔值,若成功启动发现过程则返回 true
,若失败则返回 false
。
sdp_conn_originate
packages/modules/Bluetooth/system/stack/sdp/sdp_main.cc
/********************************************************************************* Function sdp_conn_originate** Description This function is called from the API to originate a* connection.** Returns void*******************************************************************************/
tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr) {// 1. 变量声明tCONN_CB* p_ccb; // 表示连接控制块,存储与连接相关的信息uint16_t cid; // 存储 L2CAP 连接的连接 ID// 2. 分配连接控制块/* Allocate a new CCB. Return if none available. */p_ccb = sdpu_allocate_ccb(); // 尝试分配一个新的连接控制块if (p_ccb == NULL) {log::warn("no spare CCB for peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr));return (NULL);}log::verbose("SDP - Originate started for peer {}",ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr));// 3. 检查是否已有活动的 SDP 连接/* Look for any active sdp connection on the remote device */cid = sdpu_get_active_ccb_cid(p_bd_addr);// 4. 设置连接标志和保存设备地址/* We are the originator of this connection */p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; // 表示当前设备是该连接的发起者/* Save the BD Address */p_ccb->device_address = p_bd_addr;// 5. 根据情况发起 L2CAP 连接请求/* Transition to the next appropriate state, waiting for connection confirm */if (!bluetooth::common::init_flags::sdp_serialization_is_enabled() ||cid == 0) {p_ccb->con_state = SDP_STATE_CONN_SETUP; // 表示正在进行连接建立cid = L2CA_ConnectReq2(BT_PSM_SDP, p_bd_addr, BTM_SEC_NONE); // 发起 L2CAP 连接请求} else { // 已有活动的 SDP 连接p_ccb->con_state = SDP_STATE_CONN_PEND; // 表示连接处于等待状态log::warn("SDP already active for peer {}. cid={:#0x}",ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr), cid);}// 6. 检查 L2CAP 连接请求是否成功/* Check if L2CAP started the connection process */if (cid == 0) { // 表示 L2CAP 连接请求失败log::warn("SDP - Originate failed for peer {}",ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr));sdpu_release_ccb(*p_ccb); // 释放之前分配的连接控制块return (NULL);}// 7. 返回连接控制块指针p_ccb->connection_id = cid; // 将连接 ID 保存到连接控制块中return (p_ccb);
}
发起一个到指定蓝牙设备的 SDP连接。从 API 层被调用,会尝试分配连接控制块(CCB),检查是否已有活动的 SDP 连接,然后根据情况发起 L2CAP(Logical Link Control and Adaptation Protocol,逻辑链路控制和适配协议)连接请求。如果连接发起成功,返回指向连接控制块的指针;若失败,则返回 NULL
。
L2CA_ConnectReq2
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
uint16_t L2CA_ConnectReq2(uint16_t psm, const RawAddress& p_bd_addr,uint16_t sec_level) {get_btm_client_interface().security.BTM_SetSecurityLevel(true, "", 0, sec_level, psm, 0, 0);return L2CA_ConnectReq(psm, p_bd_addr);
}
发起一个 L2CAP连接请求。在发起连接请求之前,先设置目标设备的安全级别,然后调用 L2CA_ConnectReq
函数来实际发起连接。
BTM_SetSecurityLevel
packages/modules/Bluetooth/system/stack/btm/btm_sec.cc
/********************************************************************************* Function BTM_SetSecurityLevel** Description Register service security level with Security Manager** Parameters: is_originator - true if originating the connection* p_name - Name of the service relevant only if* authorization will show this name to user.* Ignored if BT_MAX_SERVICE_NAME_LEN is 0.* service_id - service ID for the service passed to* authorization callback* sec_level - bit mask of the security features* psm - L2CAP PSM* mx_proto_id - protocol ID of multiplexing proto below* mx_chan_id - channel ID of multiplexing proto below** Returns true if registered OK, else false*******************************************************************************/
bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,uint8_t service_id, uint16_t sec_level, uint16_t psm,uint32_t mx_proto_id, uint32_t mx_chan_id) {return btm_sec_cb.AddService(is_originator, p_name, service_id, sec_level,psm, mx_proto_id, mx_chan_id);
}
向蓝牙安全管理器(Security Manager)注册服务的安全级别。通过调用 btm_sec_cb
对象的 AddService
方法,将服务的相关信息(包括发起连接标志、服务名称、服务 ID、安全级别、L2CAP PSM 等)传递给安全管理器,以便在建立连接时应用相应的安全策略。
tBTM_SEC_CB::AddService
packages/modules/Bluetooth/system/stack/btm/btm_sec_cb.cc
#define BTM_NO_AVAIL_SEC_SERVICES ((uint16_t)0xffff)
bool tBTM_SEC_CB::AddService(bool is_originator, const char* p_name,uint8_t service_id, uint16_t sec_level,uint16_t psm, uint32_t mx_proto_id,uint32_t mx_chan_id) {tBTM_SEC_SERV_REC* p_srec;uint16_t index;uint16_t first_unused_record = BTM_NO_AVAIL_SEC_SERVICES;bool record_allocated = false;log::verbose("sec_level:0x{:x}", sec_level);/* See if the record can be reused (same service name, psm, mx_proto_id,service_id, and mx_chan_id), or obtain the next unused record */p_srec = &sec_serv_rec[0];// 1. 记录复用与分配(核心资源管理)for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) {/* Check if there is already a record for this service */if (p_srec->security_flags & BTM_SEC_IN_USE) {// // 检查是否存在相同服务的已有记录(PSM、协议 ID、服务名等匹配)if (p_srec->psm == psm && p_srec->mx_proto_id == mx_proto_id &&service_id == p_srec->service_id && p_name &&/* 匹配发起方或接收方服务名 */(!strncmp(p_name, (char*)p_srec->orig_service_name,/* strlcpy replaces end char with termination char*/BT_MAX_SERVICE_NAME_LEN - 1) ||!strncmp(p_name, (char*)p_srec->term_service_name,/* strlcpy replaces end char with termination char*/BT_MAX_SERVICE_NAME_LEN - 1))) {record_allocated = true;break;}}/* Mark the first available service record */else if (!record_allocated) {// 找到第一个未使用的记录,清零并标记*p_srec = {};record_allocated = true;first_unused_record = index;}}if (!record_allocated) {log::warn("Out of Service Records ({})", BTM_SEC_MAX_SERVICE_RECORDS);return (record_allocated); // 无可用记录时失败}/* Process the request if service record is valid *//* If a duplicate service wasn't found, use the first available */if (index >= BTM_SEC_MAX_SERVICE_RECORDS) {index = first_unused_record;p_srec = &sec_serv_rec[index];}p_srec->psm = psm;p_srec->service_id = service_id;p_srec->mx_proto_id = mx_proto_id;// 2. 安全级别配置(发起方 vs 接收方)if (is_originator) { // 场景一:连接发起方// 存储发起方通道 ID 和服务名p_srec->orig_mx_chan_id = mx_chan_id;strlcpy((char*)p_srec->orig_service_name, p_name,BT_MAX_SERVICE_NAME_LEN + 1);// 清除接收方相关的旧标志(确保仅保留发起方策略)/* clear out the old setting, just in case it exists */{p_srec->security_flags &=~(BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM);}// 安全模式适配:SP/SC 模式下,认证必须伴随 MITM 保护/* Parameter validation. Originator should not set requirements for* incoming connections */sec_level &= ~(BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE |BTM_SEC_IN_MITM | BTM_SEC_IN_MIN_16_DIGIT_PIN);if (security_mode == BTM_SEC_MODE_SP || security_mode == BTM_SEC_MODE_SC) {if (sec_level & BTM_SEC_OUT_AUTHENTICATE) sec_level |= BTM_SEC_OUT_MITM;}// 协议强制规则:加密必须开启认证/* Make sure the authenticate bit is set, when encrypt bit is set */if (sec_level & BTM_SEC_OUT_ENCRYPT) sec_level |= BTM_SEC_OUT_AUTHENTICATE;/* outgoing connections usually set the security level right before* the connection is initiated.* set it to be the outgoing service */p_out_serv = p_srec; // 更新全局发起方服务指针} else { // 场景二:连接接收方// 存储接收方通道 ID 和服务名p_srec->term_mx_chan_id = mx_chan_id;strlcpy((char*)p_srec->term_service_name, p_name,BT_MAX_SERVICE_NAME_LEN + 1);// 清除发起方相关的旧标志(确保仅保留接收方策略)/* clear out the old setting, just in case it exists */{p_srec->security_flags &=~(BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM |BTM_SEC_IN_MIN_16_DIGIT_PIN);}// 过滤发起方参数(接收方不应设置发起方规则)/* Parameter validation. Acceptor should not set requirements for outgoing* connections */sec_level &=~(BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM);// 安全模式适配:逻辑与发起方对称if (security_mode == BTM_SEC_MODE_SP || security_mode == BTM_SEC_MODE_SC) {if (sec_level & BTM_SEC_IN_AUTHENTICATE) sec_level |= BTM_SEC_IN_MITM;}// 协议强制规则:逻辑与发起方对称/* Make sure the authenticate bit is set, when encrypt bit is set */if (sec_level & BTM_SEC_IN_ENCRYPT) sec_level |= BTM_SEC_IN_AUTHENTICATE;}// 3. 最终标志设置与p_srec->security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE);log::debug("[{}]: id:{}, is_orig:{} psm:0x{:04x} proto_id:{} chan_id:{} : ""sec:0x{:x} service_name:[{}] (up to {} chars saved)",index, service_id, logbool(is_originator).c_str(), psm, mx_proto_id,mx_chan_id, p_srec->security_flags, p_name, BT_MAX_SERVICE_NAME_LEN);return (record_allocated);
}
AddService
函数是蓝牙安全管理模块(BTM)的核心方法,用于注册服务的安全策略。其主要职责包括:
-
管理服务安全记录:维护一个固定大小的安全服务记录数组(
sec_serv_rec
),支持复用或创建新记录。 -
设置安全级别:根据是否为连接发起方(
is_originator
),配置不同的安全标志(加密、认证、MITM 保护等)。 -
参数校验与策略适配:确保安全级别参数符合蓝牙协议规范(如加密必须伴随认证),并根据安全模式(SP/SC)调整策略。
L2CA_ConnectReq
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
/********************************************************************************* Function L2CA_ConnectReq** Description Higher layers call this function to create an L2CAP* connection.* Note that the connection is not established at this time,* but connection establishment gets started. The callback* will be invoked when connection establishes or fails.** Returns the CID of the connection, or 0 if it failed to start*******************************************************************************/
uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& p_bd_addr) {log::verbose("BDA {} PSM: 0x{:04x}", ADDRESS_TO_LOGGABLE_STR(p_bd_addr), psm);// 1. 初始化与参数校验/* Fail if we have not established communications with the controller */if (!BTM_IsDeviceUp()) {log::warn("BTU not ready");return 0; // 蓝牙未启动,连接失败}/* Fail if the PSM is not registered */tL2C_RCB* p_rcb = l2cu_find_rcb_by_psm(psm);if (p_rcb == nullptr) {log::warn("no RCB, PSM={}", loghex(psm));return 0; // 目标 PSM 未注册,服务不可用}// 2. 链路控制块(LCB)管理/* First, see if we already have a link to the remote *//* assume all ERTM l2cap connection is going over BR/EDR for now */tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR);if (p_lcb == nullptr) {// 无链路连接,创建新的 LCB 并启动链路建立流程/* No link. Get an LCB and start link establishment */p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);/* currently use BR/EDR for ERTM mode l2cap connection */if (p_lcb == nullptr) {log::warn("connection not started for PSM={}, p_lcb={}", loghex(psm),fmt::ptr(p_lcb));return 0; // 分配 LCB 失败}l2cu_create_conn_br_edr(p_lcb); // 发起 BR/EDR 链路连接}// 3. 通道控制块(CCB)分配与配置/* Allocate a channel control block */tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);if (p_ccb == nullptr) {log::warn("no CCB, PSM={}", loghex(psm));return 0;}/* Save registration info */p_ccb->p_rcb = p_rcb; // 关联目标服务的 RCBp_ccb->connection_initiator = L2CAP_INITIATOR_LOCAL; // 标记为本地发起连接// 4. 根据链路状态触发连接流程/* If link is up, start the L2CAP connection */if (p_lcb->link_state == LST_CONNECTED) {// 链路已连接,直接触发 L2CAP 连接请求l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, nullptr);} else if (p_lcb->link_state == LST_DISCONNECTING) {/* If link is disconnecting, save link info to retry after disconnect* Possible Race condition when a reconnect occurs* on the channel during a disconnect of link. This* ccb will be automatically retried after link disconnect* arrives*/log::verbose("L2CAP API - link disconnecting: RETRY LATER");// 链路正在断开,缓存 CCB 以便断开后重试/* Save ccb so it can be started after disconnect is finished */p_lcb->p_pending_ccb = p_ccb;}log::verbose("L2CAP - L2CA_conn_req(psm: 0x{:04x}) returned CID: 0x{:04x}",psm, p_ccb->local_cid);/* Return the local CID as our handle *///无论链路是否立即建立,只要请求成功发起,即返回分配的本地 CID(非零值表示成功,0 表示失败)。后续通过 CID 管理通道状态,连接结果通过回调通知上层return p_ccb->local_cid;
}
发起 L2CAP 连接请求。其主要流程包括:
-
校验蓝牙控制器状态:确保蓝牙已启动且可用。
-
查找协议服务复用器(PSM)对应的注册控制块(RCB):验证目标服务是否已注册。
-
管理链路控制块(LCB):处理与目标设备的链路连接(若未建立,则创建并启动链路建立流程)。
-
分配通道控制块(CCB):管理 L2CAP 通道的生命周期。
-
触发连接状态机:根据链路状态(已连接 / 断开中)执行相应操作,或缓存待处理的连接请求。
-
链路已连接:若链路状态为
LST_CONNECTED
,调用l2c_csm_execute
触发状态机处理L2CEVT_L2CA_CONNECT_REQ
事件,发送 L2CAP Connect Request PDU。
结果回调:bta_sdp_search_cback(BTA_SDP_SEARCH_COMP_EVT
)
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_act.cc
/** Callback from btm after search is completed */
static void bta_sdp_search_cback(UNUSED_ATTR const RawAddress& bd_addr,tSDP_RESULT result, const void* user_data) {// 1. 局部变量初始化tBTA_SDP_STATUS status = BTA_SDP_FAILURE;int count = 0;log::verbose("res: 0x{:x}", result);bta_sdp_cb.sdp_active = false;// 2. 检查回调函数指针if (bta_sdp_cb.p_dm_cback == NULL) return;// 3. 提取 UUIDUuid& uuid = *(reinterpret_cast<Uuid*>(const_cast<void*>(user_data)));// 4. 初始化事件数据结构tBTA_SDP_SEARCH_COMP evt_data;memset(&evt_data, 0, sizeof(evt_data));evt_data.remote_addr = bta_sdp_cb.remote_addr;evt_data.uuid = uuid;// 5. 处理搜索成功或数据库满的情况if (result == SDP_SUCCESS || result == SDP_DB_FULL) {tSDP_DISC_REC* p_rec = NULL;do {p_rec = get_legacy_stack_sdp_api()->db.SDP_FindServiceUUIDInDb(p_bta_sdp_cfg->p_sdp_db, uuid, p_rec);/* generate the matching record data pointer */if (!p_rec) {log::verbose("UUID not found");continue;}// 根据不同的 UUID 生成相应的 SDP 记录status = BTA_SDP_SUCCESS;if (uuid == UUID_MAP_MAS) {log::verbose("found MAP (MAS) uuid");bta_create_mas_sdp_record(&evt_data.records[count], p_rec);} else if (uuid == UUID_MAP_MNS) {log::verbose("found MAP (MNS) uuid");bta_create_mns_sdp_record(&evt_data.records[count], p_rec);} else if (uuid == UUID_PBAP_PSE) {log::verbose("found PBAP (PSE) uuid");bta_create_pse_sdp_record(&evt_data.records[count], p_rec);} else if (uuid == UUID_OBEX_OBJECT_PUSH) {log::verbose("found Object Push Server (OPS) uuid");bta_create_ops_sdp_record(&evt_data.records[count], p_rec);} else if (uuid == UUID_SAP) {log::verbose("found SAP uuid");bta_create_sap_sdp_record(&evt_data.records[count], p_rec);} else if (uuid == UUID_PBAP_PCE) { // 处理 PBAP PCE UUID 特殊情况log::verbose("found PBAP (PCE) uuid");if (p_rec != NULL) {uint16_t peer_pce_version = 0;get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &peer_pce_version);if (peer_pce_version != 0) {btif_storage_set_pce_profile_version(p_rec->remote_bd_addr,peer_pce_version);}} else {log::verbose("PCE Record is null");}} else if (uuid == UUID_DIP) {log::verbose("found DIP uuid");bta_create_dip_sdp_record(&evt_data.records[count], p_rec);} else {/* we do not have specific structure for this */log::verbose("profile not identified. using raw data");bta_create_raw_sdp_record(&evt_data.records[count], p_rec);p_rec = NULL; // Terminate loop/* For raw, we only extract the first entry, and then return theentire raw data chunk.TODO: Find a way to split the raw data into record chunks, anditerate to extract generic data for each chunk - e.g. rfcommchannel and service name. */}count++;} while (p_rec != NULL && count < BTA_SDP_MAX_RECORDS);evt_data.record_count = count;}// 6. 完成事件数据结构设置evt_data.status = status;tBTA_SDP bta_sdp;bta_sdp.sdp_search_comp = evt_data;// 调用回调函数 bta_sdp_cb.p_dm_cback 通知搜索完成bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp, (void*)&uuid);bluetooth::shim::CountCounterMetrics(android::bluetooth::CodePathCounterKeyEnum::SDP_SUCCESS, 1);osi_free(const_cast<void*>(user_data)); // We no longer need the user data to track the search
}
bta_sdp_search_cback
是一个回调函数,在 SDP搜索完成后由 btm
调用。主要任务是处理搜索结果,根据搜索结果生成相应的 SDP 记录,并将处理后的结果通过回调函数反馈给调用者,同时记录搜索成功的指标数据。
sdp_dm_cback(BTA_SDP_SEARCH_COMP_EVT)
packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static void sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data,void* user_data) {switch (event) {case BTA_SDP_SEARCH_COMP_EVT: {int size = sizeof(tBTA_SDP);// 1. 计算所需内存大小size += get_sdp_records_size(p_data->sdp_search_comp.records,p_data->sdp_search_comp.record_count);// 2. 深度复制记录内容/* need to deep copy the record content */btif_transfer_context(btif_sdp_search_comp_evt, event, (char*)p_data,size, sdp_search_comp_copy_cb);break;}case BTA_SDP_CREATE_RECORD_USER_EVT: {on_create_record_event(PTR_TO_INT(user_data));break;}case BTA_SDP_REMOVE_RECORD_USER_EVT: {on_remove_record_event(PTR_TO_INT(user_data));break;}default:break;}
}
sdp_dm_cback
是一个回调函数,用于处理蓝牙 SDP相关的事件。它根据不同的事件类型执行不同的操作,这里主要分析 BTA_SDP_SEARCH_COMP_EVT
事件。当接收到 BTA_SDP_SEARCH_COMP_EVT
事件时,首先计算存储 SDP 搜索结果所需的总内存大小,然后调用 btif_transfer_context
函数进行上下文转移和数据的深度复制。目的是为了在不同的线程或者上下文环境中安全地处理 SDP 搜索结果,避免数据在传递过程中被意外修改或者丢失。
btif_sdp_search_comp_evt
/packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static void btif_sdp_search_comp_evt(uint16_t event, char* p_param) {tBTA_SDP_SEARCH_COMP* evt_data = (tBTA_SDP_SEARCH_COMP*)p_param; // 提取事件数据log::verbose("event = {}", event);if (event != BTA_SDP_SEARCH_COMP_EVT) return;HAL_CBACK(bt_sdp_callbacks, sdp_search_cb, (bt_status_t)evt_data->status,evt_data->remote_addr, evt_data->uuid, evt_data->record_count,evt_data->records);
}
处理蓝牙 SDP搜索完成事件。当接收到 SDP 搜索完成的通知时,该函数会对事件进行检查,并将搜索结果通过回调函数传递给上层模块。以便上层模块根据搜索结果进行相应的处理,如显示搜索到的服务信息、连接到特定的服务等。
2.3 SDP 记录创建流程
①上层接口调用:create_sdp_record
触发以下步骤:
-
调用
alloc_sdp_slot
分配空闲槽位,计算记录内存需求(含动态数据)并深拷贝数据。 -
通过
BTA_SdpCreateRecordByUser
将创建请求派发到主线程,触发bta_sdp_create_record
回调。
②协议栈处理
-
主线程通过
sdp_dm_cback
分发BTA_SDP_CREATE_RECORD_USER_EVT
事件,调用on_create_record_event
。 -
根据记录类型(如
SDP_TYPE_PBAP_PSE
)调用具体创建函数(如add_pbaps_sdp
),最终通过SDP_CreateRecord
在底层数据库创建记录,并调用SDP_AddAttribute
添加属性。
③内存管理
-
copy_sdp_records
实现动态数据深拷贝,确保结构体指针指向独立内存。 -
SDP_AddAttributeToRecord
管理属性存储,按 ID 排序并校验内存空间。
create_sdp_record
bt_status_t create_sdp_record(bluetooth_sdp_record* record,int* record_handle) {int handle;// 1. 分配 SDP 槽位handle = alloc_sdp_slot(record);log::verbose("handle = 0x{:08x}", handle);if (handle < 0) return BT_STATUS_FAIL;// 2. 通知协议栈创建记录BTA_SdpCreateRecordByUser(INT_TO_PTR(handle));*record_handle = handle;return BT_STATUS_SUCCESS;
}
在蓝牙 SDP服务器中创建一个新的服务记录。通过以下步骤完成记录创建:
-
分配 SDP 槽位:从预定义的槽位数组中获取一个空闲槽位,用于存储服务记录数据。
-
通知底层协议栈:通过
BTA_SdpCreateRecordByUser
函数告知蓝牙协议栈创建服务记录,并关联槽位句柄。 -
返回句柄:将新创建记录的句柄返回给调用者,以便后续操作(如更新、删除记录)。
alloc_sdp_slot
/packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
/* Reserve a slot in sdp_slots, copy data and set a reference to the copy.* The record_data will contain both the record and any data pointed to by* the record.* Currently this covers:* service_name string,* user1_ptr and* user2_ptr. */
static int alloc_sdp_slot(bluetooth_sdp_record* in_record) {// 1. 计算记录内存大小int record_size = get_sdp_records_size(in_record, 1);// 2. 预分配内存并复制数据/* We are optimists here, and preallocate the record.* This is to reduce the time we hold the sdp_lock. */bluetooth_sdp_record* record = (bluetooth_sdp_record*)osi_malloc(record_size);copy_sdp_records(in_record, record, 1);// 3. 加锁分配槽位{std::unique_lock<std::recursive_mutex> lock(sdp_lock); // 自动加锁(作用域内有效)for (int i = 0; i < MAX_SDP_SLOTS; i++) {if (sdp_slots[i].state == SDP_RECORD_FREE) {sdp_slots[i].state = SDP_RECORD_ALLOCED;sdp_slots[i].record_data = record; // 绑定记录数据到槽位return i; // 返回槽位索引作为句柄}}} // 作用域结束,锁自动释放// 4. 分配失败处理log::error("failed - no more free slots!");/* Rearly the optimist is too optimistic, and cleanup is needed...*/osi_free(record);return -1;
}
在 SDP 服务器的槽位数组(sdp_slots
)中分配一个空闲槽位,用于存储蓝牙服务记录。其主要流程包括:
-
计算记录内存需求:根据输入的服务记录数据,计算所需的内存大小(包括记录本身和指向的数据)。
-
预分配内存:提前分配内存并复制记录数据,减少锁的持有时间,提升并发性能。
-
槽位分配:通过加锁遍历槽位数组,找到空闲槽位并绑定记录数据。
-
错误处理:若分配失败,释放预分配的内存并返回错误。
get_sdp_records_size
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
int get_sdp_records_size(bluetooth_sdp_record* in_record, int count) {bluetooth_sdp_record* record = in_record; // 指向 SDP 记录数组的指针,用于获取每条记录的动态数据长度信息// 1. 初始化总大小int records_size = 0;// 2. 遍历每条记录计算内存int i;for (i = 0; i < count; i++) {record = &in_record[i];records_size += sizeof(bluetooth_sdp_record); // 结构体自身大小// 3. 服务名称字符串内存records_size += record->hdr.service_name_length; // 字符串内容长度if (record->hdr.service_name_length > 0) {records_size++; /* + '\0' termination of string */}// 4. 用户自定义数据内存records_size += record->hdr.user1_ptr_len; // user1_ptr 指向的数据长度records_size += record->hdr.user2_ptr_len; // user2_ptr 指向的数据长度}return records_size;
}
计算指定数量的 SDP 记录及其关联动态数据的总内存需求,用于提前分配内存以完成深拷贝。具体计算内容包括:
-
结构体自身大小:每个
bluetooth_sdp_record
结构体的固定内存占用。 -
动态数据大小:结构体中动态分配的字符串(服务名称)和用户自定义数据(
user1_ptr
、user2_ptr
)的实际数据长度。 -
额外开销:服务名称字符串的终止符
\0
占用的额外字节。
copy_sdp_records
packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
/* Deep copy all content of in_records into out_records.* out_records must point to a chunk of memory large enough to contain all* the data. Use getSdpRecordsSize() to calculate the needed size. */
void copy_sdp_records(bluetooth_sdp_record* in_records,bluetooth_sdp_record* out_records, int count) {int i;bluetooth_sdp_record* in_record;bluetooth_sdp_record* out_record;// 1. 内存布局初始化// 从目标数组末尾开始向前分配空间,确保记录结构体和动态数据在内存中连续存储char* free_ptr =(char*)(&out_records[count]); /* set pointer to after the last entry */// 2. 逐记录复制for (i = 0; i < count; i++) {in_record = &in_records[i];out_record = &out_records[i];*out_record = *in_record; // 浅拷贝结构体成员// 3. 服务名称深拷贝if (in_record->hdr.service_name == NULL ||in_record->hdr.service_name_length == 0) {out_record->hdr.service_name = NULL;out_record->hdr.service_name_length = 0;} else {// 修正目标指针指向新内存out_record->hdr.service_name = free_ptr; // Update service_name pointer// Copy stringmemcpy(free_ptr, in_record->hdr.service_name,in_record->hdr.service_name_length);free_ptr += in_record->hdr.service_name_length; // 指针后移*(free_ptr) = '\0'; // Set '\0' termination of stringfree_ptr++; // 跳过终止符位置}// 4. 用户自定义数据深拷贝// 处理 user1_ptrif (in_record->hdr.user1_ptr != NULL) {out_record->hdr.user1_ptr = (uint8_t*)free_ptr; // Update pointer // 修正目标指针memcpy(free_ptr, in_record->hdr.user1_ptr,in_record->hdr.user1_ptr_len); // Copy contentfree_ptr += in_record->hdr.user1_ptr_len;}// 处理 user2_ptr(逻辑与 user1_ptr 一致)if (in_record->hdr.user2_ptr != NULL) {out_record->hdr.user2_ptr = (uint8_t*)free_ptr; // Update pointermemcpy(free_ptr, in_record->hdr.user2_ptr,in_record->hdr.user2_ptr_len); // Copy contentfree_ptr += in_record->hdr.user2_ptr_len;}}return;
}
将源 SDP 记录数据深拷贝到目标内存区域,确保目标数据独立于源数据,避免指针引用导致的内存问题。具体功能包括:
-
结构体成员浅拷贝:复制
bluetooth_sdp_record
结构体的基本成员(如服务 UUID、属性列表指针等)。 -
动态数据深拷贝:对结构体中指向动态分配内存的指针(如服务名称字符串、
user1_ptr
、user2_ptr
)进行内存复制,创建独立副本。 -
连续内存管理:将所有复制后的数据存储在目标内存的连续区域,通过指针偏移计算确保内存布局正确。
BTA_SdpCreateRecordByUser
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_api.cc
/********************************************************************************* Function BTA_SdpCreateRecordByUser** Description This function is used to request a callback to create a SDP* record. The registered callback will be called with event* BTA_SDP_CREATE_RECORD_USER_EVT.** Returns BTA_SDP_SUCCESS, if the request is being processed.* BTA_SDP_FAILURE, otherwise.*******************************************************************************/
tBTA_SDP_STATUS BTA_SdpCreateRecordByUser(void* user_data) {do_in_main_thread(FROM_HERE,base::BindOnce(bta_sdp_create_record, user_data));return BTA_SDP_SUCCESS;
}
请求创建一个 SDP(服务发现协议)记录。通过向主线程发送一个任务,让主线程调用 bta_sdp_create_record
函数来完成 SDP 记录的创建操作。该函数会触发之前注册的回调函数,回调事件为 BTA_SDP_CREATE_RECORD_USER_EVT
。
bta_sdp_record
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_act.cc
/********************************************************************************* Function bta_sdp_record** Description Discovers all sdp records for an uuid on remote device** Returns void*******************************************************************************/
void bta_sdp_create_record(void* user_data) {if (bta_sdp_cb.p_dm_cback)bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, NULL, user_data);
}
蓝牙协议栈中 SDP模块的核心回调函数,用于触发用户自定义的 SDP 记录创建流程。主要作用是将创建记录的请求传递给上层模块(如设备管理模块),通过预先注册的回调接口完成记录的实际创建或配置。
sdp_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT)
/packages/modules/Bluetooth/system/btif/src/btif_sdp.cc
static void sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data,void* user_data) {switch (event) {case BTA_SDP_SEARCH_COMP_EVT: {int size = sizeof(tBTA_SDP);size += get_sdp_records_size(p_data->sdp_search_comp.records,p_data->sdp_search_comp.record_count);/* need to deep copy the record content */btif_transfer_context(btif_sdp_search_comp_evt, event, (char*)p_data,size, sdp_search_comp_copy_cb);break;}case BTA_SDP_CREATE_RECORD_USER_EVT: {on_create_record_event(PTR_TO_INT(user_data)); // 转换句柄并触发上层回调break;}case BTA_SDP_REMOVE_RECORD_USER_EVT: {on_remove_record_event(PTR_TO_INT(user_data));break;}default:break;}
}
sdp_dm_cback
是蓝牙 SDP 模块与设备管理模块(DM)之间的回调函数,用于处理 SDP 相关事件。通过匹配不同的事件类型(如服务搜索完成、创建记录请求等),调用相应的处理函数,实现跨模块的事件驱动逻辑。其核心职责包括:
-
事件分发:根据事件类型(
tBTA_SDP_EVT
)执行不同的处理分支。 -
数据处理:对服务搜索完成事件(
BTA_SDP_SEARCH_COMP_EVT
)进行内存深拷贝,确保数据跨模块安全传递。 -
用户回调触发:将创建 / 删除记录的事件传递给上层业务逻辑(如
on_create_record_event
)。
on_create_record_event
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
/******************************************************************************* CALLBACK FUNCTIONS* Called in BTA context to create/remove SDP records.******************************************************************************/
typedef enum {SDP_TYPE_RAW, // Used to carry raw SDP search data for unknown UUIDsSDP_TYPE_MAP_MAS, // Message Access Profile - ServerSDP_TYPE_MAP_MNS, // Message Access Profile - Client (Notification Server)SDP_TYPE_PBAP_PSE, // Phone Book Profile - ServerSDP_TYPE_PBAP_PCE, // Phone Book Profile - ClientSDP_TYPE_OPP_SERVER, // Object Push ProfileSDP_TYPE_SAP_SERVER, // SIM Access ProfileSDP_TYPE_DIP, // Device Identification ProfileSDP_TYPE_MPS // Multi-Profile Specification
} bluetooth_sdp_types;void on_create_record_event(int id) {/** 1) Fetch the record pointer, and change its state?* 2) switch on the type to create the correct record* 3) Update state on completion* 4) What to do at fail?* */// 1. 函数参数和初始化log::verbose("Sdp Server");const sdp_slot_t* sdp_slot = start_create_sdp(id); // 获取指定 ID 对应的 SDP 槽位信息tBTA_SERVICE_ID service_id = -1;bluetooth_sdp_record* record; // 用于指向 SDP 记录数据// 2. 检查记录是否有效/* In the case we are shutting down, sdp_slot is NULL */if (sdp_slot != nullptr && (record = sdp_slot->record_data) != nullptr) {int handle = -1;// 3. 根据记录类型创建 SDP 记录switch (record->hdr.type) {case SDP_TYPE_MAP_MAS:handle = add_maps_sdp(&record->mas);service_id = BTA_MAP_SERVICE_ID;break;case SDP_TYPE_MAP_MNS:handle = add_mapc_sdp(&record->mns);service_id = BTA_MN_SERVICE_ID;break;case SDP_TYPE_PBAP_PSE:handle = add_pbaps_sdp(&record->pse);service_id = BTA_PBAP_SERVICE_ID;break;case SDP_TYPE_OPP_SERVER:handle = add_opps_sdp(&record->ops);break;case SDP_TYPE_SAP_SERVER:handle = add_saps_sdp(&record->sap);break;case SDP_TYPE_PBAP_PCE:handle = add_pbapc_sdp(&record->pce);service_id = BTA_PCE_SERVICE_ID;break;case SDP_TYPE_MPS:handle = add_mps_sdp(&record->mps);break;case SDP_TYPE_RAW:if (record->hdr.rfcomm_channel_number > 0) {handle = add_rfc_sdp_rec(record->hdr.service_name, record->hdr.uuid,record->hdr.rfcomm_channel_number);}break;default:log::verbose("Record type {} is not supported", record->hdr.type);break;}// 4. 处理记录创建成功的情况if (handle != -1) {set_sdp_handle(id, handle); // 将创建成功的记录句柄与记录 ID 关联起来if (service_id > 0) {/*** {@link btif_enable_service} calls {@link btif_dm_enable_service}, which calls {@link* btif_in_execute_service_request}.* - {@link btif_enable_service} btif_enable_servicesets the mask {@link btif_enabled_services}.* - {@link btif_dm_enable_service} invokes the java callback to return uuids based* on the enabled services mask.* - {@link btif_in_execute_service_request} gates the java callback in {@link* btif_dm_enable_service}.*/btif_enable_service(service_id); // 启用该服务}}}
}
on_create_record_event
函数是一个回调函数,在 BTA(蓝牙应用层)上下文环境中被调用,用于创建 SDP(服务发现协议)记录。它根据传入的记录 ID,获取对应的 SDP 记录信息,依据记录的类型调用不同的函数来创建具体的 SDP 记录,并在创建成功后更新相关状态和启用对应的服务。
这里以SDP_TYPE_RAW记录类型为例进行分析。SDP_TYPE_RAW用于携带未知通用唯一识别码(UUID)的原始 SDP 搜索数据。在蓝牙服务发现过程中,可能会遇到一些未识别的 UUID,此时可以使用
SDP_TYPE_RAW
类型来存储和处理这些原始数据,以便后续进一步分析或调试。
add_rfc_sdp_rec
/packages/modules/Bluetooth/system/btif/src/btif_sock_sdp.cc
// Adds an SDP record to the SDP database using the given |name|, |uuid|, and
// |channel|. Note that if the |uuid| is empty, the |uuid| will be set based
// upon the |channel| passed in.
int add_rfc_sdp_rec(const char* name, Uuid uuid, const int channel) {if (uuid.IsEmpty()) {switch (channel) {case RESERVED_SCN_PBS: // PBAP Reserved portuuid = UUID_PBAP_PSE;break;case RESERVED_SCN_OPS:uuid = UUID_OBEX_OBJECT_PUSH;break;default:uuid = UUID_SPP;break;}}return add_rfc_sdp_by_uuid(name, uuid, channel);
}
向 SDP数据库中添加一条 SDP 记录。接收服务名称(name
)、服务的 UUID(uuid
)以及 RFCOMM 通道号(channel
)作为参数。若传入的 UUID 为空,根据通道号为其设置合适的 UUID,最后调用 add_rfc_sdp_by_uuid
函数完成 SDP 记录的添加操作。
add_rfc_sdp_by_uuid
packages/modules/Bluetooth/system/btif/src/btif_sock_sdp.cc
// Adds an RFCOMM SDP record for a service with the given |name|, |uuid|, and
// |channel|. This function attempts to identify the type of the service based
// upon its |uuid|, and will override the |channel| with a reserved channel
// number if the |uuid| matches one of the preregistered bluez SDP records.
static int add_rfc_sdp_by_uuid(const char* name, const Uuid& uuid,const int channel) {log::verbose("uuid: {}, service_name: {}, channel: {}", uuid.ToString(), name,channel);/** Bluetooth Socket API relies on having preregistered bluez sdp records for* HSAG, HFAG, OPP & PBAP that are mapped to rc chan 10, 11,12 & 19. Today* HSAG and HFAG is routed to BRCM AG and are not using BT socket API so for* now we will need to support OPP and PBAP to enable 3rd party developer apps* running on BRCM Android.** To do this we will check the UUID for the requested service and mimic the* SDP records of bluez upon reception. See functions add_opush() and* add_pbap() in sdptool.c for actual records.*/// 1. 确定最终通道号int final_channel = get_reserved_rfc_channel(uuid);if (final_channel == -1) { // 表示没有为该 uuid找到保留的通道号final_channel = channel;}// 2. 根据uuid调用不同的添加记录函数int handle = 0;if (uuid == UUID_OBEX_OBJECT_PUSH) {handle = add_ops_sdp(name, final_channel);} else if (uuid == UUID_PBAP_PSE) {// PBAP Server is always channel 19handle = add_pbap_sdp(name, final_channel);} else if (uuid == UUID_SPP) {handle = add_spp_sdp(name, final_channel);} else if (uuid == UUID_MAP_MAS) {// Record created by new SDP create record interfacehandle = 0xff;} else {handle = add_sdp_by_uuid(name, uuid, final_channel);}return handle;
}
为具有指定名称(name
)、通用唯一识别码(uuid
)和通道号(channel
)的服务添加一个 RFCOMM(Radio Frequency Communication,射频通信)的 SDP记录。根据 uuid
识别服务类型,若 uuid
匹配预注册的 BlueZ SDP 记录,会用保留的通道号覆盖传入的 channel
,最后根据不同的 uuid
调用相应的函数来添加 SDP 记录,并返回记录的句柄。
这里主要分析调用 add_sdp_by_uuid
函数添加通用的 SDP 记录的情况。
add_sdp_by_uuid
packages/modules/Bluetooth/system/btif/src/btif_sock_sdp.cc
// Registers a service with the given |name|, |uuid|, and |channel| in the SDP
// database as a generic L2CAP RFCOMM protocol, storing its |uuid| as a service
// class sequence.
static int add_sdp_by_uuid(const char* name, const Uuid& uuid,const uint16_t channel) {log::verbose("uuid: {}, scn: {}, service_name: {}", uuid.ToString(), channel,name);// 1. 记录创建uint32_t handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord();if (handle == 0) {log::error("failed to create sdp record, scn: {}, service_name: {}",channel, name);return 0;}// 2. 数据准备// Convert the |uuid| into a big-endian representation and add it as a// sequence.uint8_t type = UUID_DESC_TYPE;uint8_t type_len = UUID_MAX_LENGTH;uint8_t type_buf[48];// Store the address of type buf in a pointer on the stack, so we can pass// a double pointer to SDP_AddSequenceuint8_t* type_buf_ptr = type_buf;uint8_t* tmp = type_buf;// 3. 创建基本 SDP 记录// Create the base SDP record.const char* stage = "create_base_record";if (!create_base_record(handle, name, channel, false /* with_obex */))goto error;// Do the conversion to big-endian -- tmp is only used to iterate through the// UUID array in the macro and serves no other purpose as the conversion// macros are not hygenic.// 4. UUID 转换为大端格式{ ARRAY_TO_BE_STREAM(tmp, uuid.To128BitBE().data(), UUID_MAX_LENGTH); }// 5. 添加服务类序列stage = "service_class_sequence";if (!get_legacy_stack_sdp_api()->handle.SDP_AddSequence(handle, (uint16_t)ATTR_ID_SERVICE_CLASS_ID_LIST, 1, &type, &type_len,&type_buf_ptr))goto error;// 6. 输出成功日志并写入 EIRlog::verbose("service registered successfully, service_name: {}, handle: 0x{:08x}",name, handle);{// Write the custom 128-bit UUID to EIRtBTA_CUSTOM_UUID curr = {uuid, handle};bta_sys_add_cust_uuid(curr); // 将自定义的 128 位 UUID 写入 EIR}return handle;error:get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle);log::error("failed to register service stage: {}, service_name: {}", stage,name);return 0;
}
在 SDP数据库中注册一个服务。该服务使用通用的 L2CAP RFCOMM 协议,会将传入的 uuid
作为服务类序列存储。会创建 SDP 记录,添加基本信息和服务类序列,并在成功时将自定义的 128 位 UUID 写入 EIR(Extended Inquiry Response),最后返回服务记录的句柄;若过程中出现错误,则删除已创建的记录并返回 0。
SDP_CreateRecord
/********************************************************************************* Function SDP_CreateRecord** Description This function is called to create a record in the database.* This would be through the SDP database maintenance API. The* record is created empty, teh application should then call* "add_attribute" to add the record's attributes.** Returns Record handle if OK, else 0.*******************************************************************************/
uint32_t SDP_CreateRecord(void) {uint32_t handle;uint8_t buf[4];tSDP_DB* p_db = &sdp_cb.server_db;// 1. 记录池可用性检查/* First, check if there is a free record */if (p_db->num_records < SDP_MAX_RECORDS) {// 2. 记录结构体初始化memset(&p_db->record[p_db->num_records], 0, sizeof(tSDP_RECORD));// 3. 生成记录句柄/* We will use a handle of the first unreserved handle plus last record** number + 1 */if (p_db->num_records)handle = p_db->record[p_db->num_records - 1].record_handle + 1;elsehandle = 0x10000;p_db->record[p_db->num_records].record_handle = handle;p_db->num_records++;log::verbose("SDP_CreateRecord ok, num_records:{}", p_db->num_records);// 4. 自动添加句柄属性/* Add the first attribute (the handle) automatically */UINT32_TO_BE_FIELD(buf, handle);SDP_AddAttribute(handle, ATTR_ID_SERVICE_RECORD_HDL, UINT_DESC_TYPE, 4,buf);return (p_db->record[p_db->num_records - 1].record_handle);} elselog::error("SDP_CreateRecord fail, exceed maximum records:{}",SDP_MAX_RECORDS);return (0);
}
用于创建一个空的 SDP 记录。其主要流程包括:
-
记录池管理:检查是否有可用记录槽位(不超过
SDP_MAX_RECORDS
)。 -
句柄生成:为新记录分配唯一的句柄(Handle),基于前一条记录句柄递增或从初始值
0x10000
开始。 -
初始化记录:清空记录结构体,自动添加
ATTR_ID_SERVICE_RECORD_HDL
属性(存储句柄值)。 -
返回句柄:成功时返回新记录句柄,失败时返回
0
(如记录池已满)。
SDP_AddAttribute
packages/modules/Bluetooth/system/stack/sdp/sdp_db.cc
/********************************************************************************* Function SDP_AddAttribute** Description This function is called to add an attribute to a record.* This would be through the SDP database maintenance API.* If the attribute already exists in the record, it is* replaced with the new value.** NOTE Attribute values must be passed as a Big Endian stream.** Returns true if added OK, else false*******************************************************************************/
bool SDP_AddAttribute(uint32_t handle, uint16_t attr_id, uint8_t attr_type,uint32_t attr_len, uint8_t* p_val) {uint16_t zz;tSDP_RECORD* p_rec = &sdp_cb.server_db.record[0]; // 指向 SDP 记录数组首元素// sdp_cb.server_db 是全局的 SDP 服务器数据库,存储所有已创建的 SDP 记录// 1. 空指针校验if (p_val == nullptr) {log::warn("Trying to add attribute with p_val == nullptr, skipped");return (false);}// 2. 日志格式化输出// TODO(305066880): invoke would_log when implemented to check// if LOG_VERBOSE is displayed.if (true) {if ((attr_type == UINT_DESC_TYPE) ||(attr_type == TWO_COMP_INT_DESC_TYPE) ||(attr_type == UUID_DESC_TYPE) ||(attr_type == DATA_ELE_SEQ_DESC_TYPE) ||(attr_type == DATA_ELE_ALT_DESC_TYPE)) {#define MAX_ARR_LEN 200// one extra byte for storing terminating zero byte// 将二进制数据转换为十六进制字符串(如 UUID 的大端字节流)char num_array[2 * MAX_ARR_LEN + 1] = {0};uint32_t len = (attr_len > MAX_ARR_LEN) ? MAX_ARR_LEN : attr_len;#undef MAX_ARR_LENfor (uint32_t i = 0; i < len; i++) {snprintf(&num_array[i * 2], sizeof(num_array) - i * 2, "%02X",(uint8_t)(p_val[i]));}log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, ""p_val:{}, *p_val:{}",handle, attr_id, attr_type, attr_len, fmt::ptr(p_val), num_array);} else if (attr_type == BOOLEAN_DESC_TYPE) {log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, ""p_val:{}, *p_val:{}",handle, attr_id, attr_type, attr_len, fmt::ptr(p_val), *p_val);} else if ((attr_type == TEXT_STR_DESC_TYPE) ||(attr_type == URL_DESC_TYPE)) {if (p_val[attr_len - 1] == '\0') {log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, ""p_val:{}, *p_val:{}",handle, attr_id, attr_type, attr_len, fmt::ptr(p_val),(char*)p_val);} else {log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, ""p_val:{}",handle, attr_id, attr_type, attr_len, fmt::ptr(p_val));}} else {log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}",handle, attr_id, attr_type, attr_len, fmt::ptr(p_val));}}// 3. 数据库查找目标记录/* Find the record in the database */for (zz = 0; zz < sdp_cb.server_db.num_records; zz++, p_rec++) {if (p_rec->record_handle == handle) { // 匹配记录句柄// 校验记录剩余空间// error out early, no need to look upif (p_rec->free_pad_ptr >= SDP_MAX_PAD_LEN) {log::error("the free pad for SDP record with handle {} is full, skip adding ""the attribute",handle);return (false);}// 调用底层函数添加属性return SDP_AddAttributeToRecord(p_rec, attr_id, attr_type, attr_len,p_val);}}return (false);
}
向指定 SDP 记录中添加或更新属性。其核心职责包括:
-
属性唯一性管理:若属性已存在,覆盖原有值;若不存在,新增属性并按 ID 排序存储。
-
数据格式校验:确保属性值以大端格式(Big Endian)传递,符合 SDP 协议规范,并对不同类型属性(如 UUID、文本、布尔值等)进行差异化日志处理。
-
数据库遍历与操作:通过记录句柄查找目标记录,调用底层函数
SDP_AddAttributeToRecord
完成属性的实际存储。 -
内存安全控制:检查记录剩余空间(
free_pad_ptr
),避免缓冲区溢出,对文本类型支持截断处理。
SDP_AddAttributeToRecord
/home/yanlongli/code/android/packages/modules/Bluetooth/system/stack/sdp/sdp_db.cc
********************************************************************************* Function SDP_AddAttributeToRecord** Description This function is called to add an attribute to a record.* This would be through the SDP database maintenance API.* If the attribute already exists in the record, it is* replaced with the new value.** NOTE Attribute values must be passed as a Big Endian stream.** Returns true if added OK, else false*******************************************************************************/
bool SDP_AddAttributeToRecord(tSDP_RECORD* p_rec, uint16_t attr_id,uint8_t attr_type, uint32_t attr_len,uint8_t* p_val) {uint16_t xx, yy;tSDP_ATTRIBUTE* p_attr = &p_rec->attribute[0];// 1. 查找属性是否存在/* Found the record. Now, see if the attribute already exists */for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) {/* The attribute exists. replace it */if (p_attr->id == attr_id) {SDP_DeleteAttributeFromRecord(p_rec, attr_id); // 先删除旧属性break;}if (p_attr->id > attr_id) break; // 属性数组按 ID 升序排列,提前终止查找}// 2. 校验属性数量限制if (p_rec->num_attributes >= SDP_MAX_REC_ATTR) return (false);// 3. 确定属性插入位置/* If not found, see if we can allocate a new entry */if (xx == p_rec->num_attributes) // 未找到属性,追加到数组末尾p_attr = &p_rec->attribute[p_rec->num_attributes];else { // 插入到有序数组的指定位置,后续属性后移/* Since the attributes are kept in sorted order, insert ours here */for (yy = p_rec->num_attributes; yy > xx; yy--)p_rec->attribute[yy] = p_rec->attribute[yy - 1];}p_attr->id = attr_id;p_attr->type = attr_type;p_attr->len = attr_len;if (p_rec->free_pad_ptr + attr_len >= SDP_MAX_PAD_LEN) {if (p_rec->free_pad_ptr >= SDP_MAX_PAD_LEN) {log::error("SDP_AddAttributeToRecord failed: free pad {} equals or exceeds max ""padding length {}",p_rec->free_pad_ptr, SDP_MAX_PAD_LEN);return (false);}// 4. 内存空间校验与分配/* do truncate only for text string type descriptor */if (attr_type == TEXT_STR_DESC_TYPE) {log::warn("SDP_AddAttributeToRecord: attr_len:{} too long. truncate to ({})",attr_len, SDP_MAX_PAD_LEN - p_rec->free_pad_ptr);attr_len = SDP_MAX_PAD_LEN - p_rec->free_pad_ptr;p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr - 1] = '\0';} elseattr_len = 0;}if (attr_len > 0) {p_attr->len = attr_len;memcpy(&p_rec->attr_pad[p_rec->free_pad_ptr], p_val, (size_t)attr_len); // 写入属性值p_attr->value_ptr = &p_rec->attr_pad[p_rec->free_pad_ptr]; // 记录值指针p_rec->free_pad_ptr += attr_len; // 更新剩余空间指针} else if (attr_len == 0 && p_attr->len != 0) {/* if truncate to 0 length, simply don't add */log::error("SDP_AddAttributeToRecord fail, length exceed maximum: ID {}: ""attr_len:{}",attr_id, attr_len);p_attr->id = p_attr->type = p_attr->len = 0;return (false);}// 5. 更新属性计数并返回p_rec->num_attributes++;return (true);
}
向指定 SDP 记录中添加或更新属性。其核心逻辑包括:
-
属性唯一性检查:若属性已存在,先删除旧属性再插入新值;若不存在,按属性 ID 排序插入合适位置。
-
内存分配与校验:确保属性值大小不超过记录的可用空间(
SDP_MAX_PAD_LEN
),对文本类型支持截断处理。 -
数据持久化:将属性值存储到记录的连续内存块(
attr_pad
)中,并更新属性指针和剩余空间。
SDP_AddSequence
packages/modules/Bluetooth/system/stack/sdp/sdp_db.cc
/********************************************************************************* Function SDP_AddSequence** Description This function is called to add a sequence to a record.* This would be through the SDP database maintenance API.* If the sequence already exists in the record, it is replaced* with the new sequence.** NOTE Element values must be passed as a Big Endian stream.** Returns true if added OK, else false*******************************************************************************/
bool SDP_AddSequence(uint32_t handle, uint16_t attr_id, uint16_t num_elem,uint8_t type[], uint8_t len[], uint8_t* p_val[]) {uint16_t xx;uint8_t* p;uint8_t* p_head;bool result;// 1. 内存分配与初始化uint8_t* p_buff =(uint8_t*)osi_malloc(sizeof(uint8_t) * SDP_MAX_ATTR_LEN * 2);p = p_buff;// 2. 构建序列/* First, build the sequence */for (xx = 0; xx < num_elem; xx++) {p_head = p;switch (len[xx]) {case 1:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_ONE_BYTE);break;case 2:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_TWO_BYTES);break;case 4:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_FOUR_BYTES);break;case 8:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_EIGHT_BYTES);break;case 16:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_SIXTEEN_BYTES);break;default:UINT8_TO_BE_STREAM(p, (type[xx] << 3) | SIZE_IN_NEXT_BYTE);UINT8_TO_BE_STREAM(p, len[xx]);break;}ARRAY_TO_BE_STREAM(p, p_val[xx], len[xx]);if (p - p_buff > SDP_MAX_ATTR_LEN) {/* go back to before we add this element */p = p_head;if (p_head == p_buff) {/* the first element exceed the max length */log::error("SDP_AddSequence - too long(attribute is not added)!!");osi_free(p_buff);return false;} elselog::error("SDP_AddSequence - too long, add {} elements of {}", xx,num_elem);break;}}// 3. 添加序列到记录result = SDP_AddAttribute(handle, attr_id, DATA_ELE_SEQ_DESC_TYPE,(uint32_t)(p - p_buff), p_buff);osi_free(p_buff);return result;
}
向 SDP记录中添加一个序列。该函数属于 SDP 数据库维护 API 的一部分,若指定的序列已存在于记录中,会用新的序列替换它。函数要求元素值以大端字节序(Big Endian)的流形式传入,最终返回操作是否成功的布尔值。
SDP_AddAttribute前面已分析,不在赘述。
2.4 SDP 记录删除流程
①上层触发删除:remove_sdp_record
执行以下操作:
-
校验记录 ID 有效性,通过互斥锁获取记录类型,禁用关联的蓝牙服务(如
BTA_PBAP_SERVICE_ID
)。 -
调用
free_sdp_slot
释放槽位,通过BTA_SdpRemoveRecordByUser
触发主线程删除。
②协议栈删除逻辑
-
主线程通过
bta_sdp_remove_record
回调触发BTA_SDP_REMOVE_RECORD_USER_EVT
,on_remove_record_event
调用SDP_DeleteRecord
。 -
SDP_DeleteRecord
根据句柄(handle
)执行单记录或全量删除:-
单记录删除:遍历数组找到目标记录,前移后续记录以保持连续,更新主 DI 句柄(若必要)。
-
全量删除:直接重置记录数与主 DI 句柄。
-
③数据一致性:删除过程中调整属性指针(value_ptr
)以适应数组重组,但存在潜在逻辑错误(实际无需调整,因指针为记录内相对偏移)。
remove_sdp_record
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
bt_status_t remove_sdp_record(int record_id) {int handle;// 1. 参数有效性校验if (record_id >= MAX_SDP_SLOTS) {return BT_STATUS_PARM_INVALID;}bluetooth_sdp_record* record;bluetooth_sdp_types sdp_type = SDP_TYPE_RAW;// 2. 线程安全的记录获取{std::unique_lock<std::recursive_mutex> lock(sdp_lock);record = sdp_slots[record_id].record_data;if (record != NULL) {sdp_type = record->hdr.type; // 获取记录类型}}// 3. 服务类型匹配与服务禁用tBTA_SERVICE_ID service_id = -1;switch (sdp_type) {case SDP_TYPE_MAP_MAS:service_id = BTA_MAP_SERVICE_ID;break;case SDP_TYPE_MAP_MNS:service_id = BTA_MN_SERVICE_ID;break;case SDP_TYPE_PBAP_PSE:service_id = BTA_PBAP_SERVICE_ID;break;case SDP_TYPE_PBAP_PCE:service_id = BTA_PCE_SERVICE_ID;break;default:/* other enumeration values were not enabled in {@link on_create_record_event} */break;}if (service_id > 0) {// {@link btif_disable_service} sets the mask {@link btif_enabled_services}.btif_disable_service(service_id); // 禁用对应的蓝牙服务}// 4. 释放 SDP 槽位/* Get the Record handle, and free the slot */handle = free_sdp_slot(record_id); // 释放槽位并获取记录句柄log::verbose("Sdp Server id={} to handle=0x{:08x}", record_id, handle);/* Pass the actual record handle */if (handle > 0) {BTA_SdpRemoveRecordByUser(INT_TO_PTR(handle)); // 通过 BTA 接口删除记录return BT_STATUS_SUCCESS;}log::verbose("Sdp Server - record already removed - or never created");return BT_STATUS_FAIL;
}
从蓝牙 SDP数据库中删除指定 ID 的服务记录。其核心流程包括:
-
参数校验:检查记录 ID 是否在有效范围内。
-
线程安全访问:通过互斥锁确保多线程环境下对 SDP 槽位的安全操作。
-
服务类型识别:根据记录类型获取对应的服务 ID,以便禁用相关服务。
-
资源释放:释放 SDP 槽位,并通过 BTA 接口触发记录删除。
-
状态同步:更新服务启用状态,确保协议栈与上层逻辑一致。
BTA_SdpRemoveRecordByUser
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_api.cc
/********************************************************************************* Function BTA_SdpRemoveRecordByUser** Description This function is used to request a callback to remove a SDP* record. The registered callback will be called with event* BTA_SDP_REMOVE_RECORD_USER_EVT.** Returns BTA_SDP_SUCCESS, if the request is being processed.* BTA_SDP_FAILURE, otherwise.*******************************************************************************/
tBTA_SDP_STATUS BTA_SdpRemoveRecordByUser(void* user_data) {do_in_main_thread(FROM_HERE,base::BindOnce(bta_sdp_remove_record, user_data));return BTA_SDP_SUCCESS;
}
蓝牙协议栈(BTA)中用于请求删除 SDP 记录的接口。其核心作用是将删除记录的请求提交到主线程处理,并通过回调机制通知上层逻辑操作结果。具体流程包括:
-
异步处理封装:通过
do_in_main_thread
将删除操作派发到主线程执行,避免在当前线程阻塞。 -
回调触发:当删除操作完成后,上层注册的回调函数(如
sdp_dm_cback
)会收到BTA_SDP_REMOVE_RECORD_USER_EVT
事件,从而更新状态或释放资源。
bta_sdp_remove_record
packages/modules/Bluetooth/system/bta/sdp/bta_sdp_act.cc
/********************************************************************************* Function bta_sdp_create_record** Description Discovers all sdp records for an uuid on remote device** Returns void*******************************************************************************/
void bta_sdp_remove_record(void* user_data) {if (bta_sdp_cb.p_dm_cback)bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, NULL, user_data);
}
触发回调函数,以通知上层应用程序有一个 SDP(服务发现协议)记录删除事件发生。当 BTA_SdpRemoveRecordByUser
函数将删除 SDP 记录的请求派发到主线程后,主线程会调用此函数来处理该请求,最终通过回调机制通知上层应用。
on_remove_record_event
packages/modules/Bluetooth/system/btif/src/btif_sdp_server.cc
void on_remove_record_event(int handle) {log::verbose("Sdp Server");// User data carries the actual SDP handle, not the ID.if (handle != -1 && handle != 0) {bool result;result = get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle);if (!result) {log::error("Unable to remove handle 0x{:08x}", handle);}}
}
SDP模块的回调处理函数,用于响应 SDP 记录删除事件。其核心职责是:
-
校验记录句柄的有效性。
-
调用底层 API 删除指定句柄的 SDP 记录。
-
记录操作结果日志,便于调试和故障排查。
SDP_DeleteRecord
/********************************************************************************* Function SDP_DeleteRecord** Description This function is called to add a record (or all records)* from the database. This would be through the SDP database* maintenance API.** If a record handle of 0 is passed, all records are deleted.** Returns true if succeeded, else false*******************************************************************************/
bool SDP_DeleteRecord(uint32_t handle) {uint16_t xx, yy, zz;tSDP_RECORD* p_rec = &sdp_cb.server_db.record[0];// 1. 全量删除逻辑if (handle == 0 || sdp_cb.server_db.num_records == 0) {/* Delete all records in the database */sdp_cb.server_db.num_records = 0;/* require new DI record to be created in SDP_SetLocalDiRecord */sdp_cb.server_db.di_primary_handle = 0;return (true);}// 2. 单记录删除逻辑else {/* Find the record in the database */for (xx = 0; xx < sdp_cb.server_db.num_records; xx++, p_rec++) {if (p_rec->record_handle == handle) { // 找到目标记录// 重组记录数组:将后续记录前移/* Found it. Shift everything up one */for (yy = xx; yy < sdp_cb.server_db.num_records - 1; yy++, p_rec++) {*p_rec = *(p_rec + 1); // 复制后续记录/* Adjust the attribute value pointer for each attribute */for (zz = 0; zz < p_rec->num_attributes; zz++)p_rec->attribute[zz].value_ptr -= sizeof(tSDP_RECORD);}sdp_cb.server_db.num_records--;log::verbose("SDP_DeleteRecord ok, num_records:{}",sdp_cb.server_db.num_records);/* if we're deleting the primary DI record, clear the *//* value in the control block */if (sdp_cb.server_db.di_primary_handle == handle) {// 更新主 DI 句柄(若删除的是主记录)sdp_cb.server_db.di_primary_handle = 0;}return (true);}}}return (false);
}
删除单个或所有 SDP 记录。其核心逻辑包括:
-
全量删除处理:当传入句柄为
0
时,清空整个数据库。 -
单记录删除处理:根据句柄查找并删除指定记录,重组记录数组以保持连续性。
-
元数据更新:更新数据库记录数量、主设备信息(DI)句柄等状态。
三、时序图
3.1 SDP 服务初始化流程时序图
3.2 SDP 服务搜索流程时序图
3.3 创建SDP记录流程时序图
3.4 删除SDP记录流程时序图
四、总结
本文围绕蓝牙 SDP 服务记录的生命周期,详细解析了从初始化、创建、搜索到删除的完整流程。核心设计包括:
-
线程安全:通过主线程任务派发与互斥锁确保操作原子性。
-
解耦与抽象:通过接口结构体(
sdp_if
)和回调机制分离底层实现与上层逻辑。 -
协议兼容性:支持旧版协议栈接口(
get_legacy_stack_sdp_api
),并通过类型枚举(bluetooth_sdp_types
)适配多种服务类型。 -
内存管理:深拷贝与动态内存分配确保数据独立,避免悬垂指针。