Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4096633a authored by HsingYuan Lo's avatar HsingYuan Lo
Browse files

Add more logic for robust caching

1. After successful service discovery, save the hash and database
2. Before service discovery, read db first
  - if an existing database has the same hash value, directly
    apply the database, and back to connected state
  - otherwise, start service discovery immediately
3. Point addr file to hash file by hard link
4. For bonded devices, if there is no database hash on them,
   directly load existing cache. (Backward compatibility)

Bug: 154056389
Test: compile & verify gatt client behavior
Tag: #feature
Change-Id: I0a7655b3734906ae47adb66890683c9c68ff728a
parent e3efb32b
Loading
Loading
Loading
Loading
+18 −2
Original line number Original line Diff line number Diff line
@@ -501,13 +501,23 @@ void bta_gattc_conn(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
      p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
      p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
    if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
    if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
      p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
      p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
      // Consider the case that if GATT Server is changed, but no service
      // changed indication is received, the database might be out of date. So
      // if robust caching is enabled, any time when connection is established,
      // always check the db hash first, not just load the stored database.
      gatt::Database db = bta_gattc_cache_load(p_clcb->p_srcb->server_bda);
      gatt::Database db = bta_gattc_cache_load(p_clcb->p_srcb->server_bda);
      if (!db.IsEmpty()) {
      if (!bta_gattc_is_robust_caching_enabled() && !db.IsEmpty()) {
        p_clcb->p_srcb->gatt_database = db;
        p_clcb->p_srcb->gatt_database = db;
        p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
        p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
        bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
        bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
      } else {
      } else {
        p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
        p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;

        /* set true to read database hash before service discovery */
        if (bta_gattc_is_robust_caching_enabled()) {
          p_clcb->p_srcb->srvc_hdl_db_hash = true;
        }

        /* cache load failure, start discovery */
        /* cache load failure, start discovery */
        bta_gattc_start_discover(p_clcb, NULL);
        bta_gattc_start_discover(p_clcb, NULL);
      }
      }
@@ -707,6 +717,11 @@ void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb,
      /* set all srcb related clcb into discovery ST */
      /* set all srcb related clcb into discovery ST */
      bta_gattc_set_discover_st(p_clcb->p_srcb);
      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;

      /* clear the service change mask */
      /* clear the service change mask */
      p_clcb->p_srcb->srvc_hdl_chg = false;
      p_clcb->p_srcb->srvc_hdl_chg = false;
      p_clcb->p_srcb->update_count = 0;
      p_clcb->p_srcb->update_count = 0;
@@ -714,7 +729,8 @@ void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb,


      /* read db hash if db hash characteristic exists */
      /* read db hash if db hash characteristic exists */
      if (bta_gattc_is_robust_caching_enabled() &&
      if (bta_gattc_is_robust_caching_enabled() &&
          p_clcb->p_srcb->srvc_hdl_db_hash && bta_gattc_read_db_hash(p_clcb)) {
          p_clcb->p_srcb->srvc_hdl_db_hash &&
          bta_gattc_read_db_hash(p_clcb, is_svc_chg)) {
        LOG(INFO) << __func__
        LOG(INFO) << __func__
                  << ": pending service discovery, read db hash first";
                  << ": pending service discovery, read db hash first";
        p_clcb->p_srcb->srvc_hdl_db_hash = false;
        p_clcb->p_srcb->srvc_hdl_db_hash = false;
+85 −21
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@
#define LOG_TAG "bt_bta_gattc"
#define LOG_TAG "bt_bta_gattc"


#include <base/logging.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/strings/stringprintf.h>


#include <cstdint>
#include <cstdint>
@@ -61,7 +62,8 @@ static void bta_gattc_explore_srvc_finished(uint16_t conn_id,
                                            tBTA_GATTC_SERV* p_srvc_cb);
                                            tBTA_GATTC_SERV* p_srvc_cb);


static void bta_gattc_read_db_hash_cmpl(tBTA_GATTC_CLCB* p_clcb,
static void bta_gattc_read_db_hash_cmpl(tBTA_GATTC_CLCB* p_clcb,
                                        const tBTA_GATTC_OP_CMPL* p_data);
                                        const tBTA_GATTC_OP_CMPL* p_data,
                                        bool is_svc_chg);


static void bta_gattc_read_ext_prop_desc_cmpl(tBTA_GATTC_CLCB* p_clcb,
static void bta_gattc_read_ext_prop_desc_cmpl(tBTA_GATTC_CLCB* p_clcb,
                                              const tBTA_GATTC_OP_CMPL* p_data);
                                              const tBTA_GATTC_OP_CMPL* p_data);
@@ -216,16 +218,25 @@ static void bta_gattc_explore_srvc_finished(uint16_t conn_id,
  /* save cache to NV */
  /* save cache to NV */
  p_clcb->p_srcb->state = BTA_GATTC_SERV_SAVE;
  p_clcb->p_srcb->state = BTA_GATTC_SERV_SAVE;


  // If robust caching is not enabled, use original design
  if (!bta_gattc_is_robust_caching_enabled()) {
    if (btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)) {
    if (btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)) {
      bta_gattc_cache_write(p_clcb->p_srcb->server_bda,
      bta_gattc_cache_write(p_clcb->p_srcb->server_bda,
                            p_clcb->p_srcb->gatt_database);
                            p_clcb->p_srcb->gatt_database);
    }
    }
  } else {
    // If robust caching is enabled, do something optimized
    Octet16 hash = p_clcb->p_srcb->gatt_database.Hash();
    bool success = bta_gattc_hash_write(hash, p_clcb->p_srcb->gatt_database);

    // If the device is trusted, link the addr file to hash file
    if (success && btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)) {
      bta_gattc_cache_link(p_clcb->p_srcb->server_bda, hash);
    }


    // After success, reset the count.
    // After success, reset the count.
  if (bta_gattc_is_robust_caching_enabled()) {
    LOG_DEBUG("service discovery succeed, reset count to zero, conn_id=0x%04x",
    LOG(INFO) << __func__
              conn_id);
              << ": service discovery succeed, reset count to zero, conn_id="
              << loghex(conn_id);
    p_srvc_cb->srvc_disc_count = 0;
    p_srvc_cb->srvc_disc_count = 0;
  }
  }


@@ -366,8 +377,11 @@ void bta_gattc_op_cmpl_during_discovery(tBTA_GATTC_CLCB* p_clcb,
      bta_gattc_read_ext_prop_desc_cmpl(p_clcb, &p_data->op_cmpl);
      bta_gattc_read_ext_prop_desc_cmpl(p_clcb, &p_data->op_cmpl);
      break;
      break;
    case BTA_GATTC_DISCOVER_REQ_READ_DB_HASH:
    case BTA_GATTC_DISCOVER_REQ_READ_DB_HASH:
    case BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG:
      if (bta_gattc_is_robust_caching_enabled()) {
      if (bta_gattc_is_robust_caching_enabled()) {
        bta_gattc_read_db_hash_cmpl(p_clcb, &p_data->op_cmpl);
        bool is_svc_chg = (p_clcb->request_during_discovery ==
                           BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG);
        bta_gattc_read_db_hash_cmpl(p_clcb, &p_data->op_cmpl, is_svc_chg);
      } else {
      } else {
        // it is not possible here if flag is off, but just in case
        // it is not possible here if flag is off, but just in case
        p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_NONE;
        p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_NONE;
@@ -614,7 +628,7 @@ const Characteristic* bta_gattc_get_owning_characteristic(uint16_t conn_id,
}
}


/* request reading database hash */
/* request reading database hash */
bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb) {
bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb, bool is_svc_chg) {
  tGATT_READ_PARAM read_param;
  tGATT_READ_PARAM read_param;
  memset(&read_param, 0, sizeof(tGATT_READ_BY_TYPE));
  memset(&read_param, 0, sizeof(tGATT_READ_BY_TYPE));


@@ -626,14 +640,21 @@ bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb) {
      GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_TYPE, &read_param);
      GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_TYPE, &read_param);


  if (status != GATT_SUCCESS) return false;
  if (status != GATT_SUCCESS) return false;

  if (is_svc_chg) {
    p_clcb->request_during_discovery =
        BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG;
  } else {
    p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_READ_DB_HASH;
    p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_READ_DB_HASH;
  }


  return true;
  return true;
}
}


/* handle response of reading database hash */
/* handle response of reading database hash */
static void bta_gattc_read_db_hash_cmpl(tBTA_GATTC_CLCB* p_clcb,
static void bta_gattc_read_db_hash_cmpl(tBTA_GATTC_CLCB* p_clcb,
                                        const tBTA_GATTC_OP_CMPL* p_data) {
                                        const tBTA_GATTC_OP_CMPL* p_data,
                                        bool is_svc_chg) {
  uint8_t op = (uint8_t)p_data->op_code;
  uint8_t op = (uint8_t)p_data->op_code;
  if (op != GATTC_OPTYPE_READ) {
  if (op != GATTC_OPTYPE_READ) {
    VLOG(1) << __func__ << ": op = " << +p_data->hdr.layer_specific;
    VLOG(1) << __func__ << ": op = " << +p_data->hdr.layer_specific;
@@ -643,31 +664,74 @@ static void bta_gattc_read_db_hash_cmpl(tBTA_GATTC_CLCB* p_clcb,


  // run match flow only if the status is success
  // run match flow only if the status is success
  bool matched = false;
  bool matched = false;
  bool found = false;
  if (p_data->status == GATT_SUCCESS) {
  if (p_data->status == GATT_SUCCESS) {
    // start to compare local hash and remote hash
    // start to compare local hash and remote hash
    uint16_t len = p_data->p_cmpl->att_value.len;
    uint16_t len = p_data->p_cmpl->att_value.len;
    uint8_t* data = p_data->p_cmpl->att_value.value;
    uint8_t* data = p_data->p_cmpl->att_value.value;


    Octet16 remote_hash;
    Octet16 remote_hash;
    if (len == remote_hash.size()) {
    if (len == remote_hash.max_size()) {
      uint8_t idx = 0;
      std::copy(data, data + len, remote_hash.begin());
      auto it = remote_hash.begin();
      for (; idx < len; idx++, data++, it++) *it = *data;


      Octet16 local_hash = p_clcb->p_srcb->gatt_database.Hash();
      Octet16 local_hash = p_clcb->p_srcb->gatt_database.Hash();
      matched = (local_hash == remote_hash);
      matched = (local_hash == remote_hash);

      LOG_DEBUG("lhash=%s",
                base::HexEncode(local_hash.data(), local_hash.size()).c_str());
      LOG_DEBUG(
          "rhash=%s",
          base::HexEncode(remote_hash.data(), remote_hash.size()).c_str());

      if (!matched) {
        gatt::Database db = bta_gattc_hash_load(remote_hash);
        if (!db.IsEmpty()) {
          p_clcb->p_srcb->gatt_database = db;
          found = true;
        }
        // If the device is trusted, link addr file to correct hash file
        if (found && (btm_sec_is_a_bonded_dev(p_clcb->p_srcb->server_bda))) {
          bta_gattc_cache_link(p_clcb->p_srcb->server_bda, remote_hash);
        }
      }
    }
  } else {
    // Only load cache for trusted device if no database hash on server side.
    // If is_svc_chg is true, do not read the existing cache.
    bool is_a_bonded_dev = btm_sec_is_a_bonded_dev(p_clcb->p_srcb->server_bda);
    if (!is_svc_chg && is_a_bonded_dev) {
      gatt::Database db = bta_gattc_cache_load(p_clcb->p_srcb->server_bda);
      if (!db.IsEmpty()) {
        p_clcb->p_srcb->gatt_database = db;
        found = true;
      }
      LOG_DEBUG("load cache directly, result=%d", found);
    } else {
      LOG_DEBUG("skip read cache, is_svc_chg=%d, is_a_bonded_dev=%d",
                is_svc_chg, is_a_bonded_dev);
    }
    }
  }
  }


  if (matched) {
  if (matched) {
    LOG(INFO) << __func__ << ": hash is the same, skip service discovery";
    LOG_DEBUG("hash is the same, skip service discovery");
    p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
    bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
  } else {
    if (found) {
      LOG_DEBUG("hash found in cache, skip service discovery");

#if (BTA_GATT_DEBUG == TRUE)
      bta_gattc_display_cache_server(p_clcb->p_srcb->gatt_database);
#endif

      p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
      p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
      bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
      bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
    } else {
    } else {
    LOG(INFO) << __func__ << ": hash is not the same, start service discovery";
      LOG_DEBUG("hash is not the same, start service discovery");
      bta_gattc_start_discover_internal(p_clcb);
      bta_gattc_start_discover_internal(p_clcb);
    }
    }
  }
  }
}


/* handle response of reading extended properties descriptor */
/* handle response of reading extended properties descriptor */
static void bta_gattc_read_ext_prop_desc_cmpl(
static void bta_gattc_read_ext_prop_desc_cmpl(
+4 −2
Original line number Original line Diff line number Diff line
@@ -258,6 +258,7 @@ typedef struct {
#define BTA_GATTC_DISCOVER_REQ_NONE 0
#define BTA_GATTC_DISCOVER_REQ_NONE 0
#define BTA_GATTC_DISCOVER_REQ_READ_EXT_PROP_DESC 1
#define BTA_GATTC_DISCOVER_REQ_READ_EXT_PROP_DESC 1
#define BTA_GATTC_DISCOVER_REQ_READ_DB_HASH 2
#define BTA_GATTC_DISCOVER_REQ_READ_DB_HASH 2
#define BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG 3


  uint8_t request_during_discovery; /* request during discover state */
  uint8_t request_during_discovery; /* request during discover state */


@@ -477,11 +478,12 @@ extern tBTA_GATTC_CONN* bta_gattc_conn_find_alloc(const RawAddress& remote_bda);
extern bool bta_gattc_conn_dealloc(const RawAddress& remote_bda);
extern bool bta_gattc_conn_dealloc(const RawAddress& remote_bda);


/* bta_gattc_cache */
/* bta_gattc_cache */
extern bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb);
extern bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb, bool is_svc_chg);


/* bta_gattc_db_storage */
/* bta_gattc_db_storage */
extern gatt::Database bta_gattc_hash_load(const Octet16& hash);
extern gatt::Database bta_gattc_hash_load(const Octet16& hash);
extern bool bta_gattc_hash_write(const Octet16& hash, const gatt::Database& database);
extern bool bta_gattc_hash_write(const Octet16& hash,
                                 const gatt::Database& database);
extern gatt::Database bta_gattc_cache_load(const RawAddress& server_bda);
extern gatt::Database bta_gattc_cache_load(const RawAddress& server_bda);
extern void bta_gattc_cache_write(const RawAddress& server_bda,
extern void bta_gattc_cache_write(const RawAddress& server_bda,
                                  const gatt::Database& database);
                                  const gatt::Database& database);