Bluedroid bta_gattc_start_discover源码剖析

温馨提示:

服务发现的相关流程请看上一篇博客Android Ble discoverServices分析-CSDN博客 本文主要解析Bluedroid bta_gattc_start_discover源码。

一、 前言

当BLE设备作为GATT客户端(GATT Client)连接到GATT服务器(GATT Server)后,它通常需要执行发现过程以了解服务器的GATT数据库结构。这包括服务(Services)、特征(Characteristics)和描述符等。bta_gattc_start_discover 函数正是用于启动这一过程的。

二、工作流程

  1. 连接建立:首先,GATT客户端需要与GATT服务器建立连接。通过调用BlueDroid BTA_GATTC_Open来完成。
  2. 启动发现:连接建立后,GATT客户端会调用bta_gattc_start_discover来启动发现过程。这个函数可能会触发一系列的GATT请求,如“Read By Group Type Request”来请求主服务(Primary Services)的列表。
  3. 处理响应:当GATT服务器响应这些请求时,客户端会收到相应的GATT响应。这些响应由BlueDroid堆栈中的函数(如gatt_client_handle_server_rsp)处理,并根据需要进行进一步的操作。
  4. 缓存结果:发现过程中获取的信息(如服务列表、特征列表等)会被缓存起来,以便客户端后续使用。
  5. 完成发现:当所有必要的发现操作都完成后,会触发一个完成回调(如bta_gattc_disc_cmpl_cback),表示发现过程已经结束。

三、源码分析

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中执行的函数。

  1. 搜出3个service,每个service有s_handle、e_handle、uuid。gatt_client_handle_server_rsp中执行的是gatt_process_error_rsp。

  2. [#0单一service发现]

    1. 搜索include service,结果没有。gatt_process_error_rsp。

    2. 搜索所有Characteristic,有1个。gatt_process_read_by_type_rsp。

    3. 搜索#0号Char中的descriptor,结果没有。gatt_process_read_by_type_rsp。

  3. [#1单一service发现]

    1. 搜索include service,结果没有。gatt_process_error_rsp。

    2. [#1单一service发现]搜索所有Characteristic,有3个。gatt_process_read_by_type_rsp。

    3. [#1单一service发现]搜索#0号Char中的descriptor,结果没有。gatt_process_error_rsp。

    4. [#1单一service发现]搜索#1号Char中的descriptor,结果没有。gatt_process_error_rsp。

    5. [#1单一service发现]搜索#2号Char中的descriptor,结果没有。gatt_process_error_rsp。

  4. [#2单一service发现]

    1. 搜索include service,结果没有。gatt_process_error_rsp。

    2. [#2单一service发现]搜索所有Characteristic,有3个。gatt_process_read_by_type_rsp。

    3. [#2单一service发现]搜索#0号Char中的descriptor,结果没有。gatt_process_error_rsp。

    4. [#2单一service发现]搜索#1号Char中的descriptor,有一个。gatt_process_read_info_rsp。

    5. [#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_type, 0x0001, 0xFFFF);
  }

  // only for C
根据提供的错误信息,"android][error:bta_gattc_act.cc(252)] bta_gattc_process_api_open: failed, u",这是一个Android平台上的错误。这个错误信息来自bta_gattc_act.cc文件的第252行,是在处理GATT client API的打开操作时发生的错误。 这个错误可能有多种原因。首先,它可能是由于网络连接问题导致的。GATT(Generic Attribute Profile)是蓝牙低功耗设备之间进行通信的一种协议,如果设备之间的网络连接不稳定或者有丢包现象,可能会导致打开操作失败。解决这个问题的办法是检查设备之间的网络连接,尝试重新连接设备或者重启设备。 其次,这个错误可能是由于GATT client API的调用参数错误导致的。在调用bta_gattc_process_api_open函数时,可能传入了错误的参数,或者参数的格式不正确,导致函数无法正常执行。解决这个问题的办法是检查调用该函数的代码,确保传入正确的参数,并且参数的值符合函数的要求。 此外,这个错误也可能是由于设备驱动问题或者硬件兼容性问题引起的。如果设备驱动不正确或者设备硬件与Android平台不兼容,就可能导致GATT client API无法正常工作。解决这个问题的办法是更新设备驱动程序或者尝试使用其他兼容性更好的设备。 总之,要解决这个错误,需要查找并修复问题的具体原因。根据具体情况,可以尝试检查网络连接、调整API参数、更新驱动程序或者更换设备,以解决GATT client API打开操作失败的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值