温馨提示:
服务发现的相关流程请看上一篇博客Android Ble discoverServices分析-CSDN博客 本文主要解析Bluedroid bta_gattc_start_discover源码。
1. 前言
当BLE设备作为GATT客户端(GATT Client)连接到GATT服务器(GATT Server)后,它通常需要执行发现过程以了解服务器的GATT数据库结构。这包括服务(Services)、特征(Characteristics)和描述符等。bta_gattc_start_discover
函数正是用于启动这一过程的。
2. 工作流程
- 连接建立:首先,GATT客户端需要与GATT服务器建立连接。通过调用BlueDroid
BTA_GATTC_Open
来完成。 - 启动发现:连接建立后,GATT客户端会调用
bta_gattc_start_discover
来启动发现过程。这个函数可能会触发一系列的GATT请求,如“Read By Group Type Request”来请求主服务(Primary Services)的列表。 - 处理响应:当GATT服务器响应这些请求时,客户端会收到相应的GATT响应。这些响应由BlueDroid堆栈中的函数(如
gatt_client_handle_server_rsp
)处理,并根据需要进行进一步的操作。 - 缓存结果:发现过程中获取的信息(如服务列表、特征列表等)会被缓存起来,以便客户端后续使用。
- 完成发现:当所有必要的发现操作都完成后,会触发一个完成回调(如
bta_gattc_disc_cmpl_cback
),表示发现过程已经结束。
3. 源码分析
3.1. GATT实例
为能更直观理解,引入一个gatt实例。
[Service]
UUID: 0x1801
PRIMARY SERVICE
[Unknown Characteristic]
UUID: 0x2a05
Properties: INDICATE
---
[Service]
UUID: 0x1800
PRIMARY SERVICE
[Unknown Characteristic]
UUID: 0x2a00
Properties: READ
[Unknown Characteristic]
UUID: 0x2a01
Properties: READ
[Unknown Characteristic]
UUID: 0x2aa6
Properties: READ
---
[Service]
UUID: 0x5356
PRIMARY SERVICE
[Unknown Characteristic]
UUID: 0xfd02
Properties: READ
[Unknown Characteristic]
UUID: 0xfd03
Properties: NOTIFY
Descriptors
[Client Characteristic Configuration]
UUID: 0x2902
Properties: NOTIFY
[Unknown Characteristic]
UUID: 0xfd01
Properties: READ, WRITE, NOTIFY
对应这gatt,当发现结束后,bta_gattc_explore_srvc_finished(...)中bta_gattc_display_cache_server()输出的logcat。
<================Start Server Cache =============>
Service: handle=0x0001, end_handle=0x0005, uuid=00001801-0000-1000-8000-00805f9b34fb
Characteristic: declaration_handle=0x0002, value_handle=0x0003, uuid=00002a05-0000-1000-8000-00805f9b34fb, prop=0x20
Service: handle=0x0014, end_handle=0x001c, uuid=00001800-0000-1000-8000-00805f9b34fb
Characteristic: declaration_handle=0x0015, value_handle=0x0016, uuid=00002a00-0000-1000-8000-00805f9b34fb, prop=0x02
Characteristic: declaration_handle=0x0017, value_handle=0x0018, uuid=00002a01-0000-1000-8000-00805f9b34fb, prop=0x02
Characteristic: declaration_handle=0x0019, value_handle=0x001a, uuid=00002aa6-0000-1000-8000-00805f9b34fb, prop=0x02
Service: handle=0x0028, end_handle=0xffff, uuid=00005356-0000-1000-8000-00805f9b34fb
Characteristic: declaration_handle=0x0029, value_handle=0x002a, uuid=0000fd02-0000-1000-8000-00805f9b34fb, prop=0x02
Characteristic: declaration_handle=0x002b, value_handle=0x002c, uuid=0000fd03-0000-1000-8000-00805f9b34fb, prop=0x10
Descriptor: handle=0x002d, uuid=00002902-0000-1000-8000-00805f9b34fb
Characteristic: declaration_handle=0x002e, value_handle=0x002f, uuid=0000fd01-0000-1000-8000-00805f9b34fb, prop=0x1a
<================End Server Cache =============>
-
进入discover时的bta状态是BTA_GATTC_DISCOVER_ST,经过discover后,进入BTA_GATTC_CONN_ST。
-
discover任务是从ble peripheral获取service、characteristic、descriptor,存储在BLE HAL内存:p_clcb->p_srcb。过程中不会回调JNI函数。
-
discover流程:
-
一次req/resp搜索出所有primary service,然后对每个service依次进行单一service发现流程。
-
单一service发现。一次Req/Resp搜索include service;然后一次Req/Resp搜出所有Characteristic;
-
对每个char,一次Req/Resp搜出Descriptors。
-
-
执行完一步操作后,会调用gatt_end_operation。该函数的主要功能是触发下一步操作,而这是通过回调bta_gattc_disc_cmpl_cback。
-
会把discover到的gatt db存储到cache文件,像/data/misc/bluetooth/gatt_cache_XXX,前提是配对了该peripheral,即条件“btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)”返回true。以目前常见的手机连接ble peripernal,是不配对的,即不会生成cache文件。
对示例,1 + (2+1) + (2+3) + (2+3) = 14,须14个packet req/resp。以下是每次req/resp对应的功能,最后一列是gatt_client_handle_server_rsp中执行的函数。
搜出3个service,每个service有s_handle、e_handle、uuid。gatt_client_handle_server_rsp中执行的是gatt_process_error_rsp。
[#0单一service发现]
搜索include service,结果没有。gatt_process_error_rsp。
搜索所有Characteristic,有1个。gatt_process_read_by_type_rsp。
搜索#0号Char中的descriptor,结果没有。gatt_process_read_by_type_rsp。
[#1单一service发现]
搜索include service,结果没有。gatt_process_error_rsp。
[#1单一service发现]搜索所有Characteristic,有3个。gatt_process_read_by_type_rsp。
[#1单一service发现]搜索#0号Char中的descriptor,结果没有。gatt_process_error_rsp。
[#1单一service发现]搜索#1号Char中的descriptor,结果没有。gatt_process_error_rsp。
[#1单一service发现]搜索#2号Char中的descriptor,结果没有。gatt_process_error_rsp。
[#2单一service发现]
搜索include service,结果没有。gatt_process_error_rsp。
[#2单一service发现]搜索所有Characteristic,有3个。gatt_process_read_by_type_rsp。
[#2单一service发现]搜索#0号Char中的descriptor,结果没有。gatt_process_error_rsp。
[#2单一service发现]搜索#1号Char中的descriptor,有一个。gatt_process_read_info_rsp。
[#2单一service发现]搜索#2号Char中的descriptor,结果没有。gatt_process_error_rsp。
接下开始bta_gattc_start_discover过程解析。
3.2. bta_gattc_start_discover
/packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
/** Start a discovery on server */
void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb,
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
log::verbose("conn_id:{} p_clcb->p_srcb->state:{}",
loghex(p_clcb->bta_conn_id), p_clcb->p_srcb->state);
if (((p_clcb->p_q_cmd == NULL ||
p_clcb->auto_update == BTA_GATTC_REQ_WAITING) &&
p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) ||
p_clcb->p_srcb->state == BTA_GATTC_SERV_DISC)
/* no pending operation, start discovery right away */
{
p_clcb->auto_update = BTA_GATTC_NO_SCHEDULE; // 表示没有自动更新的计划
if (p_clcb->p_srcb == NULL) {
log::error("unknown device, can not start discovery");
return;
}
/* set all srcb related clcb into discovery ST */
// 复位一些标记位和数据, 包括把p_clcb->state改为BTA_GATTC_DISCOVER_ST
bta_gattc_set_discover_st(p_clcb->p_srcb);
// Before clear mask, set is_svc_chg to
// 1. true, invoked by service changed indication
// 2. false, invoked by connect API
bool is_svc_chg = p_clcb->p_srcb->srvc_hdl_chg; //这个值决定了服务发现是由服务变更指示触发的,还是由连接API触发的
/* clear the service change mask */
p_clcb->p_srcb->srvc_hdl_chg = false;
p_clcb->p_srcb->update_count = 0;
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC_ACT;
p_clcb->p_srcb->disc_blocked_waiting_on_version = false;
auto cache_support =
GetRobustCachingSupport(p_clcb, p_clcb->p_srcb->gatt_database);
if (cache_support == RobustCachingSupport::W4_REMOTE_VERSION) {
log::info(
"Pausing service discovery till remote version is read conn_id:{}",
p_clcb->bta_conn_id);
p_clcb->p_srcb->disc_blocked_waiting_on_version = true;
p_clcb->p_srcb->blocked_conn_id = p_clcb->bta_conn_id;
return;
}
bta_gattc_continue_with_version_and_cache_known(p_clcb, cache_support,
is_svc_chg);
}
/* pending operation, wait until it finishes */
else {
p_clcb->auto_update = BTA_GATTC_DISC_WAITING;
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE)
p_clcb->state = BTA_GATTC_CONN_ST; /* set clcb state */
}
}
bta_gattc_start_discoverBLE是客户端在建立与服务器的连接后,用于启动启动对远端蓝牙设备的服务发现过程。根据当前的状态和条件决定是否立即开始服务发现,或者等待之前的操作完成后再进行。同时,它也处理了与服务变更和缓存支持相关的逻辑。
继续服务发现流程:如果不需要等待对端版本信息,则调用bta_gattc_continue_with_version_and_cache_known继续服务发现流程,根据缓存支持和是否由服务变更触发来决定后续操作。
3.3.bta_gattc_continue_with_version_and_cache_known
/packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
void bta_gattc_continue_with_version_and_cache_known(
tBTA_GATTC_CLCB* p_clcb, RobustCachingSupport cache_support,
bool is_svc_chg) {
if (cache_support == RobustCachingSupport::UNSUPPORTED ||
(IS_FLAG_ENABLED(skip_unknown_robust_caching) &&
cache_support == RobustCachingSupport::UNKNOWN)) {
// Skip initial DB hash read if no DB hash is known, or if
// we have strong reason (due to interop,
// or a prior discovery) to believe that it is unsupported.
p_clcb->p_srcb->srvc_hdl_db_hash = false; //表示不需要处理数据库哈希
}
/* read db hash if db hash characteristic exists */
if (bta_gattc_is_robust_caching_enabled() &&
p_clcb->p_srcb->srvc_hdl_db_hash &&
bta_gattc_read_db_hash(p_clcb, is_svc_chg)) {
log::info("pending service discovery, read db hash first conn_id:{}",
loghex(p_clcb->bta_conn_id));
p_clcb->p_srcb->srvc_hdl_db_hash = false;
return;
}
bta_gattc_start_discover_internal(p_clcb);
}
-
鲁棒缓存是一种优化机制,允许GATTC在重新连接时快速恢复之前的服务发现结果,而无需重新发现所有服务。但是,这需要GATT服务器支持该特性,并提供数据库哈希值以便验证缓存的有效性。
-
读取数据库哈希是可选的,取决于对端GATT服务器的支持情况、配置选项以及是否检测到服务变更。
-
如果决定读取数据库哈希,则服务发现过程会暂时挂起,直到数据库哈希读取完成并验证缓存的有效性。
3.4.bta_gattc_start_discover_internal
packages/modules/Bluetooth/system/bta/gatt/bta_gattc_act.cc
void bta_gattc_start_discover_internal(tBTA_GATTC_CLCB* p_clcb) {
if (p_clcb->transport == BT_TRANSPORT_LE)
L2CA_LockBleConnParamsForServiceDiscovery(p_clcb->p_srcb->server_bda, true);//锁定连接参数,以便在服务发现期间保持稳定的连接质量。这个步骤是可选的,取决于蓝牙堆栈的实现和配置
//初始化与远程服务器相关的缓存。这个缓存用于存储服务发现的结果,以便在后续的连接中快速恢复。
bta_gattc_init_cache(p_clcb->p_srcb);
p_clcb->status = bta_gattc_discover_pri_service(
p_clcb->bta_conn_id, p_clcb->p_srcb, GATT_DISC_SRVC_ALL); //启动主要服务的发现过程
if (p_clcb->status != GATT_SUCCESS) {
log::error("discovery on server failed");
bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status);
} else
p_clcb->disc_active = true; //表示当前正在进行服务发现
}
最终执行了bta_gattc_discover_pri_service,其中参数disc_type是GATT_DISC_SRVC_ALL,指示GATTC启动一个过程来发现远程 GATTS上所有的主要服务(Primary Services)。
在BLE中,服务是数据的集合,这些数据通过一组特征(Characteristics)来暴露给 GATTC。主要服务是指那些可以在服务发现过程中被直接发现的服务,而次要服务(Secondary Services)则通常被包含在某个主要服务内部,并需要额外的步骤来发现。
GATT_DISC_SRVC_ALL 是一个发现类型,它告诉 GATTC使用 GATT Discover All Primary Services by UUID 过程(如果 UUID 设置为 NULL,则表示不特定于任何 UUID)来查找远程服务器上的所有主要服务。这个过程通常涉及发送一个包含特定 PDU的GATT请求给对端服务器,然后等待服务器响应包含服务列表的响应。
一旦 GATTC收到这些服务的列表,它就可以进一步发现每个服务的特征(Characteristics)和描述符(Descriptors),从而能够读取、写入或订阅这些服务的特定数据。
注意,尽管 GATT_DISC_SRVC_ALL 指示发现所有服务,但远程服务器可能不包含任何服务,或者出于安全或隐私原因,某些服务可能对特定的 GATTC不可见。
3.5.bta_gattc_discover_pri_service
/packages/modules/Bluetooth/system/bta/gatt/bta_gattc_cache.cc
/** Start primary service discovery */
tGATT_STATUS bta_gattc_discover_pri_service(uint16_t conn_id,
tBTA_GATTC_SERV* p_server_cb,
tGATT_DISC_TYPE disc_type) {
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
if (!p_clcb) return GATT_ERROR;
if (p_clcb->transport == BT_TRANSPORT_LE) {
return GATTC_Discover(conn_id, disc_