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

Commit 169219ef authored by HsingYuan Lo's avatar HsingYuan Lo Committed by Gerrit Code Review
Browse files

Merge "Implement robust caching (server)"

parents e03ce043 a3acd5cc
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -239,6 +239,18 @@ void btif_storage_set_gatt_cl_supp_feat(const RawAddress& bd_addr,
/** Get client supported features */
uint8_t btif_storage_get_gatt_cl_supp_feat(const RawAddress& bd_addr);

/** Remove client supported features */
void btif_storage_remove_gatt_cl_supp_feat(const RawAddress& bd_addr);

/** Store last server database hash for remote client */
void btif_storage_set_gatt_cl_db_hash(const RawAddress& bd_addr, Octet16 hash);

/** Get last server database hash for remote client */
Octet16 btif_storage_get_gatt_cl_db_hash(const RawAddress& bd_addr);

/** Remove last server database hash for remote client */
void btif_storage_remove_gatt_cl_db_hash(const RawAddress& bd_addr);

/** Get the hearing aid device properties. */
bool btif_storage_get_hearing_aid_prop(
    const RawAddress& address, uint8_t* capabilities, uint64_t* hi_sync_id,
+62 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ using bluetooth::Uuid;
#define BTIF_STORAGE_KEY_LOCAL_IO_CAPS_BLE "LocalIOCapsBLE"
#define BTIF_STORAGE_KEY_ADAPTER_DISC_TIMEOUT "DiscoveryTimeout"
#define BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED "GattClientSupportedFeatures"
#define BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH "GattClientDatabaseHash"

/* This is a local property to add a device found */
#define BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP 0xFF
@@ -852,6 +853,9 @@ bt_status_t btif_storage_remove_bonded_device(
  if (btif_config_exist(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED)) {
    ret &= btif_config_remove(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED);
  }
  if (btif_config_exist(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH)) {
    ret &= btif_config_remove(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH);
  }

  /* write bonded info immediately */
  btif_config_flush();
@@ -1783,3 +1787,61 @@ uint8_t btif_storage_get_gatt_cl_supp_feat(const RawAddress& bd_addr) {

  return value;
}

/** Remove client supported features */
void btif_storage_remove_gatt_cl_supp_feat(const RawAddress& bd_addr) {
  do_in_jni_thread(
      FROM_HERE, Bind(
                     [](const RawAddress& bd_addr) {
                       auto bdstr = bd_addr.ToString();
                       if (btif_config_exist(
                               bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED)) {
                         btif_config_remove(
                             bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED);
                         btif_config_save();
                       }
                     },
                     bd_addr));
}

/** Store last server database hash for remote client */
void btif_storage_set_gatt_cl_db_hash(const RawAddress& bd_addr, Octet16 hash) {
  do_in_jni_thread(FROM_HERE, Bind(
                                  [](const RawAddress& bd_addr, Octet16 hash) {
                                    auto bdstr = bd_addr.ToString();
                                    btif_config_set_bin(
                                        bdstr,
                                        BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH,
                                        hash.data(), hash.size());
                                    btif_config_save();
                                  },
                                  bd_addr, hash));
}

/** Get last server database hash for remote client */
Octet16 btif_storage_get_gatt_cl_db_hash(const RawAddress& bd_addr) {
  auto bdstr = bd_addr.ToString();

  Octet16 hash;
  size_t size = hash.size();
  btif_config_get_bin(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH, hash.data(),
                      &size);

  return hash;
}

/** Remove las server database hash for remote client */
void btif_storage_remove_gatt_cl_db_hash(const RawAddress& bd_addr) {
  do_in_jni_thread(FROM_HERE,
                   Bind(
                       [](const RawAddress& bd_addr) {
                         auto bdstr = bd_addr.ToString();
                         if (btif_config_exist(
                                 bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH)) {
                           btif_config_remove(
                               bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_DB_HASH);
                           btif_config_save();
                         }
                       },
                       bd_addr));
}
+15 −2
Original line number Diff line number Diff line
@@ -140,6 +140,17 @@ static void gatt_update_last_srv_info() {
  }
}

/** Update database hash and client status */
static void gatt_update_for_database_change() {
  gatt_cb.database_hash = gatts_calculate_database_hash(gatt_cb.srv_list_info);

  uint8_t i = 0;
  for (i = 0; i < GATT_MAX_PHY_CHANNEL; i++) {
    tGATT_TCB& tcb = gatt_cb.tcb[i];
    if (tcb.in_use) gatt_sr_update_cl_status(tcb, /* chg_aware= */ false);
  }
}

/*******************************************************************************
 *
 * Function         GATTS_AddService
@@ -309,6 +320,7 @@ tGATT_STATUS GATTS_AddService(tGATT_IF gatt_if, btgatt_db_element_t* service,
          << ", e_hdl=" << loghex(elem.e_hdl) << ", type=" << loghex(elem.type)
          << ", sdp_hdl=" << loghex(elem.sdp_handle);

  gatt_update_for_database_change();
  gatt_proc_srv_chg();

  return GATT_SERVICE_STARTED;
@@ -359,12 +371,13 @@ bool GATTS_DeleteService(tGATT_IF gatt_if, Uuid* p_svc_uuid,
    return false;
  }

  gatt_proc_srv_chg();

  if (is_active_service(p_reg->app_uuid128, p_svc_uuid, svc_inst)) {
    GATTS_StopService(it->asgn_range.s_handle);
  }

  gatt_update_for_database_change();
  gatt_proc_srv_chg();

  VLOG(1) << "released handles s_hdl=" << loghex(it->asgn_range.s_handle)
          << ", e_hdl=" << loghex(it->asgn_range.e_handle);

+252 −30
Original line number Diff line number Diff line
@@ -29,9 +29,9 @@
#include "bt_target.h"
#include "bt_utils.h"
#include "btif/include/btif_storage.h"

#include "gatt_api.h"
#include "gatt_int.h"
#include "gd/common/init_flags.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"

@@ -72,6 +72,15 @@ static void gatt_cl_op_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,

static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB* p_clcb);

static bool gatt_sr_is_robust_caching_enabled();

static tGATT_STATUS gatt_sr_read_db_hash(uint16_t conn_id,
                                         tGATT_VALUE* p_value);
static tGATT_STATUS gatt_sr_read_cl_supp_feat(uint16_t conn_id,
                                              tGATT_VALUE* p_value);
static tGATT_STATUS gatt_sr_write_cl_supp_feat(uint16_t conn_id,
                                               tGATT_WRITE_REQ* p_data);

static tGATT_CBACK gatt_profile_cback = {gatt_connect_cback,
                                         gatt_cl_op_cmpl_cback,
                                         gatt_disc_res_cback,
@@ -211,18 +220,14 @@ tGATT_STATUS read_attr_value(uint16_t conn_id, uint16_t handle,
    /*GATT_UUID_CLIENT_SUP_FEAT */
    if (is_long) return GATT_NOT_LONG;

    tGATT_PROFILE_CLCB* p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
    if (!p_clcb) {
      LOG(ERROR) << __func__ << " Context does not exist anymore for "
                 << int(conn_id);
      return GATT_ERR_UNLIKELY;
    return gatt_sr_read_cl_supp_feat(conn_id, p_value);
  }

    uint8_t cl_gatt_supp_feat = btif_storage_get_gatt_cl_supp_feat(p_clcb->bda);
    UINT8_TO_STREAM(p, cl_gatt_supp_feat);
  if (handle == gatt_cb.handle_of_database_hash) {
    /* GATT_UUID_DATABASE_HASH */
    if (is_long) return GATT_NOT_LONG;

    p_value->len = 1;
    return GATT_SUCCESS;
    return gatt_sr_read_db_hash(conn_id, p_value);
  }

  if (handle == gatt_cb.handle_of_h_r) {
@@ -247,31 +252,20 @@ tGATT_STATUS proc_read_req(uint16_t conn_id, tGATTS_REQ_TYPE,
/** GAP ATT server process a write request */
tGATT_STATUS proc_write_req(uint16_t conn_id, tGATTS_REQ_TYPE,
                            tGATT_WRITE_REQ* p_data) {
  uint16_t handle = p_data->handle;

  /* GATT_UUID_SERVER_SUP_FEAT*/
  if (p_data->handle == gatt_cb.handle_sr_supported_feat)
    return GATT_WRITE_NOT_PERMIT;
  if (handle == gatt_cb.handle_sr_supported_feat) return GATT_WRITE_NOT_PERMIT;

  /* GATT_UUID_CLIENT_SUP_FEAT*/
  if (p_data->handle == gatt_cb.handle_cl_supported_feat) {
    /* We store the value set by the peer but we don't use it */
    tGATT_PROFILE_CLCB* p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
    if (!p_clcb) {
      LOG(ERROR) << __func__ << " Context does not exist anymore for "
                 << int(conn_id);
      return GATT_ERR_UNLIKELY;
    }

    uint8_t* p = p_data->value;
  if (handle == gatt_cb.handle_cl_supported_feat)
    return gatt_sr_write_cl_supp_feat(conn_id, p_data);

    uint8_t cl_gatt_supp_feat;
    STREAM_TO_UINT8(cl_gatt_supp_feat, p);

    btif_storage_set_gatt_cl_supp_feat(p_clcb->bda, cl_gatt_supp_feat);
    return GATT_SUCCESS;
  }
  /* GATT_UUID_DATABASE_HASH */
  if (handle == gatt_cb.handle_of_database_hash) return GATT_WRITE_NOT_PERMIT;

  /* GATT_UUID_GATT_SRV_CHGD */
  if (p_data->handle == gatt_cb.handle_of_h_r) return GATT_WRITE_NOT_PERMIT;
  if (handle == gatt_cb.handle_of_h_r) return GATT_WRITE_NOT_PERMIT;

  return GATT_NOT_FOUND;
}
@@ -337,6 +331,13 @@ static void gatt_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,
  VLOG(1) << __func__ << ": from " << bda << " connected: " << connected
          << ", conn_id: " << loghex(conn_id);

  // if the device is not trusted, remove data when the link is disconnected
  if (!connected && !btm_sec_is_a_bonded_dev(bda)) {
    LOG(INFO) << __func__ << ": remove untrusted client status, bda=" << bda;
    btif_storage_remove_gatt_cl_supp_feat(bda);
    btif_storage_remove_gatt_cl_db_hash(bda);
  }

  tGATT_PROFILE_CLCB* p_clcb =
      gatt_profile_find_clcb_by_bd_addr(bda, transport);
  if (p_clcb == NULL) return;
@@ -378,6 +379,7 @@ void gatt_profile_db_init(void) {
  Uuid srv_changed_char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
  Uuid svr_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
  Uuid cl_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_CLIENT_SUP_FEAT);
  Uuid database_hash_uuid = Uuid::From16Bit(GATT_UUID_DATABASE_HASH);

  btgatt_db_element_t service[] = {
      {
@@ -401,6 +403,12 @@ void gatt_profile_db_init(void) {
          .uuid = cl_sup_feat_uuid,
          .properties = GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE,
          .permissions = GATT_PERM_READ | GATT_PERM_WRITE,
      },
      {
          .uuid = database_hash_uuid,
          .type = BTGATT_DB_CHARACTERISTIC,
          .properties = GATT_CHAR_PROP_BIT_READ,
          .permissions = GATT_PERM_READ,
      }};

  GATTS_AddService(gatt_cb.gatt_if, service,
@@ -410,10 +418,14 @@ void gatt_profile_db_init(void) {
  gatt_cb.handle_of_h_r = service[1].attribute_handle;
  gatt_cb.handle_sr_supported_feat = service[2].attribute_handle;
  gatt_cb.handle_cl_supported_feat = service[3].attribute_handle;
  gatt_cb.handle_of_database_hash = service[4].attribute_handle;

  gatt_cb.gatt_svr_supported_feat_mask |= BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK;
  gatt_cb.gatt_cl_supported_feat_mask |= BLE_GATT_CL_ANDROID_SUP_FEAT;

  if (gatt_sr_is_robust_caching_enabled())
    gatt_cb.gatt_cl_supported_feat_mask |= BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK;

  VLOG(1) << __func__ << ": gatt_if=" << gatt_cb.gatt_if << " EATT supported";
}

@@ -781,3 +793,213 @@ bool gatt_profile_get_eatt_support(

  return gatt_svc_read_supp_feat_req(remote_bda, conn_id, std::move(cb));
}

/*******************************************************************************
 *
 * Function         gatt_sr_is_robust_caching_enabled
 *
 * Description      Check if Robust Caching is enabled on server side.
 *
 * Returns          true if enabled in gd flag, otherwise false
 *
 ******************************************************************************/
static bool gatt_sr_is_robust_caching_enabled() {
  return bluetooth::common::init_flags::gatt_robust_caching_is_enabled();
}

/*******************************************************************************
 *
 * Function         gatt_sr_is_cl_robust_caching_supported
 *
 * Description      Check if Robust Caching is supported for the connection
 *
 * Returns          true if enabled by client side, otherwise false
 *
 ******************************************************************************/
static bool gatt_sr_is_cl_robust_caching_supported(tGATT_TCB& tcb) {
  // if robust caching is not enabled, should always return false
  if (!gatt_sr_is_robust_caching_enabled()) return false;
  return (tcb.cl_supp_feat & BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK);
}

/*******************************************************************************
 *
 * Function         gatt_sr_is_cl_change_aware
 *
 * Description      Check if the connection is change-aware
 *
 * Returns          true if change aware, otherwise false
 *
 ******************************************************************************/
bool gatt_sr_is_cl_change_aware(tGATT_TCB& tcb) {
  // if robust caching is not supported, should always return true by default
  if (!gatt_sr_is_cl_robust_caching_supported(tcb)) return true;
  return tcb.is_robust_cache_change_aware;
}

/*******************************************************************************
 *
 * Function         gatt_sr_init_cl_status
 *
 * Description      Restore status for trusted device
 *
 * Returns          none
 *
 ******************************************************************************/
void gatt_sr_init_cl_status(tGATT_TCB& tcb) {
  tcb.cl_supp_feat = btif_storage_get_gatt_cl_supp_feat(tcb.peer_bda);
  // This is used to reset bit when robust caching is disabled
  if (!gatt_sr_is_robust_caching_enabled()) {
    tcb.cl_supp_feat &= ~BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK;
  }

  if (gatt_sr_is_cl_robust_caching_supported(tcb)) {
    Octet16 stored_hash = btif_storage_get_gatt_cl_db_hash(tcb.peer_bda);
    tcb.is_robust_cache_change_aware = (stored_hash == gatt_cb.database_hash);
  } else {
    // set default value for untrusted device
    tcb.is_robust_cache_change_aware = true;
  }

  LOG(INFO) << __func__ << ": bda=" << tcb.peer_bda
            << ", cl_supp_feat=" << loghex(tcb.cl_supp_feat)
            << ", aware=" << tcb.is_robust_cache_change_aware;
}

/*******************************************************************************
 *
 * Function         gatt_sr_update_cl_status
 *
 * Description      Update change-aware status for the remote device
 *
 * Returns          none
 *
 ******************************************************************************/
void gatt_sr_update_cl_status(tGATT_TCB& tcb, bool chg_aware) {
  // if robust caching is not supported, do nothing
  if (!gatt_sr_is_cl_robust_caching_supported(tcb)) return;

  // only when client status is changed from change-unaware to change-aware, we
  // can then store database hash into btif_storage
  if (!tcb.is_robust_cache_change_aware && chg_aware) {
    btif_storage_set_gatt_cl_db_hash(tcb.peer_bda, gatt_cb.database_hash);
  }

  // only when the status is changed, print the log
  if (tcb.is_robust_cache_change_aware != chg_aware) {
    LOG(INFO) << __func__ << ": bda=" << tcb.peer_bda
              << ", chg_aware=" << chg_aware;
  }

  tcb.is_robust_cache_change_aware = chg_aware;
}

/* handle request for reading database hash */
static tGATT_STATUS gatt_sr_read_db_hash(uint16_t conn_id,
                                         tGATT_VALUE* p_value) {
  LOG(INFO) << __func__ << ": conn_id=" << loghex(conn_id);

  uint8_t* p = p_value->value;
  Octet16& db_hash = gatt_cb.database_hash;
  ARRAY_TO_STREAM(p, db_hash.data(), (uint16_t)db_hash.size());
  p_value->len = (uint16_t)db_hash.size();

  // Every time when database hash is requested, reset flag.
  uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
  gatt_sr_update_cl_status(gatt_cb.tcb[tcb_idx], /* chg_aware= */ true);
  return GATT_SUCCESS;
}

/* handle request for reading client supported features */
static tGATT_STATUS gatt_sr_read_cl_supp_feat(uint16_t conn_id,
                                              tGATT_VALUE* p_value) {
  // Get tcb info
  uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
  tGATT_TCB& tcb = gatt_cb.tcb[tcb_idx];

  uint8_t* p = p_value->value;
  UINT8_TO_STREAM(p, tcb.cl_supp_feat);
  p_value->len = 1;

  return GATT_SUCCESS;
}

/* handle request for writing client supported features */
static tGATT_STATUS gatt_sr_write_cl_supp_feat(uint16_t conn_id,
                                               tGATT_WRITE_REQ* p_data) {
  std::list<uint8_t> tmp;
  uint16_t len = p_data->len;
  uint8_t value, *p = p_data->value;
  // Read all octets into list
  while (len > 0) {
    STREAM_TO_UINT8(value, p);
    tmp.push_back(value);
    len--;
  }
  // Remove trailing zero octets
  while (!tmp.empty()) {
    if (tmp.back() != 0x00) break;
    tmp.pop_back();
  }

  // Get tcb info
  uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
  tGATT_TCB& tcb = gatt_cb.tcb[tcb_idx];

  std::list<uint8_t> feature_list;
  feature_list.push_back(tcb.cl_supp_feat);

  // If input length is zero, return value_not_allowed
  if (tmp.empty()) {
    LOG(INFO) << __func__ << ": zero length, conn_id=" << loghex(conn_id)
              << ", bda=" << tcb.peer_bda;
    return GATT_VALUE_NOT_ALLOWED;
  }
  // if original length is longer than new one, it must be the bit reset case.
  if (feature_list.size() > tmp.size()) {
    LOG(INFO) << __func__ << ": shorter length, conn_id=" << loghex(conn_id)
              << ", bda=" << tcb.peer_bda;
    return GATT_VALUE_NOT_ALLOWED;
  }
  // new length is longer or equals to the original, need to check bits
  // one by one. Here we use bit-wise operation.
  // 1. Use XOR to locate the change bit, val_xor is the change bit mask
  // 2. Use AND for val_xor and *it_new to get val_and
  // 3. If val_and != val_xor, it means the change is from 1 to 0
  auto it_old = feature_list.cbegin();
  auto it_new = tmp.cbegin();
  for (; it_old != feature_list.cend(); it_old++, it_new++) {
    uint8_t val_xor = *it_old ^ *it_new;
    uint8_t val_and = val_xor & *it_new;
    if (val_and != val_xor) {
      LOG(INFO) << __func__
                << ": bit cannot be reset, conn_id=" << loghex(conn_id)
                << ", bda=" << tcb.peer_bda;
      return GATT_VALUE_NOT_ALLOWED;
    }
  }

  // get current robust caching status before setting new one
  bool curr_caching_state = gatt_sr_is_cl_robust_caching_supported(tcb);

  tcb.cl_supp_feat = tmp.front();
  if (!gatt_sr_is_robust_caching_enabled()) {
    // remove robust caching bit
    tcb.cl_supp_feat &= ~BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK;
    LOG(INFO) << __func__
              << ": reset robust caching bit, conn_id=" << loghex(conn_id)
              << ", bda=" << tcb.peer_bda;
  }
  // TODO(hylo): save data as byte array
  btif_storage_set_gatt_cl_supp_feat(tcb.peer_bda, tcb.cl_supp_feat);

  // get new robust caching status after setting new one
  bool new_caching_state = gatt_sr_is_cl_robust_caching_supported(tcb);
  // only when the first time robust caching request, print the log
  if (!curr_caching_state && new_caching_state) {
    LOG(INFO) << __func__ << ": robust caching enabled by client"
              << ", conn_id=" << loghex(conn_id);
  }

  return GATT_SUCCESS;
}
+13 −0
Original line number Diff line number Diff line
@@ -303,6 +303,12 @@ typedef struct {
  std::queue<tGATT_CMD_Q> cl_cmd_q;
  alarm_t* ind_ack_timer; /* local app confirm to indication timer */

  // TODO(hylo): support byte array data
  /* Client supported feature*/
  uint8_t cl_supp_feat;
  /* Use for server. if false, should handle database out of sync. */
  bool is_robust_cache_change_aware;

  bool in_use;
  uint8_t tcb_idx;
} tGATT_TCB;
@@ -401,6 +407,9 @@ typedef struct {
   */
  uint8_t gatt_cl_supported_feat_mask;

  uint16_t handle_of_database_hash;
  Octet16 database_hash;

  tGATT_APPL_INFO cb_info;

  tGATT_HDL_CFG hdl_cfg;
@@ -443,6 +452,10 @@ extern bool gatt_profile_get_eatt_support(
    const RawAddress& remote_bda,
    base::OnceCallback<void(const RawAddress&, bool)> cb);

extern bool gatt_sr_is_cl_change_aware(tGATT_TCB& tcb);
extern void gatt_sr_init_cl_status(tGATT_TCB& tcb);
extern void gatt_sr_update_cl_status(tGATT_TCB& tcb, bool chg_unaware);

/* Functions provided by att_protocol.cc */
extern tGATT_STATUS attp_send_cl_confirmation_msg(tGATT_TCB& tcb, uint16_t cid);
extern tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
Loading