diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gatts_co.c b/components/bt/host/bluedroid/bta/gatt/bta_gatts_co.c index 2130005a11..0fdf789bb2 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gatts_co.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gatts_co.c @@ -24,6 +24,8 @@ #include #include #include "bta/bta_gatts_co.h" +#include "btc/btc_storage.h" +#include "btc/btc_ble_storage.h" // #include "btif_util.h" #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) @@ -159,5 +161,89 @@ BOOLEAN bta_gatts_co_load_handle_range(UINT8 index, return FALSE; } + +/******************************************************************************* +** +** Function bta_gatts_co_cl_feat_save +** +** Description This callout function is executed by GATTS when GATT server +** client support feature is requested to write to NV. +** +** Parameter remote_addr - remote device address +** feature - pointer of client support feature +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_cl_feat_save(BD_ADDR remote_addr, UINT8 *feature) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_set_gatt_cl_supp_feat(&bd_addr, feature, 1); +} + +/******************************************************************************* +** +** Function bta_gatts_co_db_hash_save +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to write to NV. +** +** Parameter remote_addr - remote device address +** db_hash - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_db_hash_save(BD_ADDR remote_addr, BT_OCTET16 db_hash) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_set_gatt_db_hash(&bd_addr, db_hash, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function bta_gatts_co_cl_feat_load +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to load from NV. +** +** Parameter remote_addr - remote device address +** feature - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_cl_feat_load(BD_ADDR remote_addr, UINT8 *feature) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_get_gatt_cl_supp_feat(&bd_addr, feature, 1); +} + +/******************************************************************************* +** +** Function bta_gatts_co_db_hash_load +** +** Description This callout function is executed by GATTS when GATT server +** client status is requested to load from NV. +** +** Parameter remote_addr - remote device address +** db_hash - pointer of GATT service datebase hash +** +** Returns void. +** +*******************************************************************************/ +void bta_gatts_co_db_hash_load(BD_ADDR remote_addr, BT_OCTET16 db_hash) +{ + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, remote_addr, BD_ADDR_LEN); + btc_storage_get_gatt_db_hash(&bd_addr, db_hash, BT_OCTET16_LEN); +} #endif #endif diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h b/components/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h index 9d81d6461d..66178c76fc 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_gatts_co.h @@ -77,5 +77,12 @@ extern BOOLEAN bta_gatts_co_srv_chg(tBTA_GATTS_SRV_CHG_CMD cmd, extern BOOLEAN bta_gatts_co_load_handle_range(UINT8 index, tBTA_GATTS_HNDL_RANGE *p_handle); +extern void bta_gatts_co_cl_feat_save(BD_ADDR remote_addr, UINT8 *feature); + +extern void bta_gatts_co_db_hash_save(BD_ADDR remote_addr, BT_OCTET16 db_hash); + +extern void bta_gatts_co_cl_feat_load(BD_ADDR remote_addr, UINT8 *feature); + +extern void bta_gatts_co_db_hash_load(BD_ADDR remote_addr, BT_OCTET16 db_hash); #endif /* BTA_GATTS_CO_H */ diff --git a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c index bff6da0d2b..a188991b9b 100644 --- a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c @@ -47,7 +47,9 @@ static void _btc_storage_save(void) !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PID_STR) && !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_PCSRK_STR) && !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LENC_STR) && - !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR)) { + !btc_config_exist(section, BTC_BLE_STORAGE_LE_KEY_LCSRK_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR) && + !btc_config_exist(section, BTC_BLE_STORAGE_GATT_DB_HASH_STR)) { iter = btc_config_section_next(iter); btc_config_remove_section(section); continue; @@ -932,3 +934,79 @@ int btc_storage_get_num_ble_bond_devices(void) } #endif ///BLE_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) +bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t *)&len); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t)len); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_get_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t *)&len); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_set_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) +{ + int ret; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t)len); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr) +{ + bool ret = true; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +bt_status_t btc_storage_remove_gatt_db_hash(bt_bdaddr_t *remote_bd_addr) +{ + bool ret = true; + bdstr_t bdstr; + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR); + if (ret == false) { + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/btc/core/btc_dm.c b/components/bt/host/bluedroid/btc/core/btc_dm.c index 26fc65d9ac..f270576517 100644 --- a/components/bt/host/bluedroid/btc/core/btc_dm.c +++ b/components/bt/host/bluedroid/btc/core/btc_dm.c @@ -181,6 +181,8 @@ static void btc_dm_remove_ble_bonding_keys(void) bdcpy(bd_addr.address, btc_dm_cb.pairing_cb.bd_addr); + btc_storage_remove_gatt_cl_supp_feat(&bd_addr); + btc_storage_remove_gatt_db_hash(&bd_addr); btc_storage_remove_remote_addr_type(&bd_addr, false); btc_storage_remove_ble_dev_auth_mode(&bd_addr, false); btc_storage_remove_ble_dev_type(&bd_addr, false); @@ -817,6 +819,8 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) if (p_data->link_down.status == HCI_SUCCESS) { //remove the bonded key in the config and nvs flash. + btc_storage_remove_gatt_cl_supp_feat(&bd_addr); + btc_storage_remove_gatt_db_hash(&bd_addr); btc_storage_remove_ble_dev_type(&bd_addr, false); btc_storage_remove_remote_addr_type(&bd_addr, false); btc_storage_remove_ble_dev_auth_mode(&bd_addr, false); diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h b/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h index 0b8751f895..490e97fd5f 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h @@ -88,6 +88,20 @@ bt_status_t btc_storage_load_bonded_ble_devices(void); bt_status_t btc_storage_get_bonded_ble_devices_list(esp_ble_bond_dev_t *bond_dev, int dev_num); int btc_storage_get_num_ble_bond_devices(void); - #endif ///SMP_INCLUDED == TRUE + +#define BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR "GATT_CL_SUPP_FEAT" +#define BTC_BLE_STORAGE_GATT_DB_HASH_STR "GATT_DB_HASH" + +bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_remove_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr); + +bt_status_t btc_storage_get_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_set_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len); + +bt_status_t btc_storage_remove_gatt_db_hash(bt_bdaddr_t *remote_bd_addr); #endif ///__BTC_BLE_STORAGE_H__ diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_api.c b/components/bt/host/bluedroid/stack/gatt/gatt_api.c index 1d60319033..6f1e5537d4 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_api.c @@ -122,6 +122,20 @@ BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info) return status; } +static void gatt_update_for_database_change(void) +{ + UINT8 i; + + gatts_calculate_datebase_hash(gatt_cb.database_hash); + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i++) { + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(i); + if (p_tcb && p_tcb->in_use) { + gatt_sr_update_cl_status(p_tcb, false); + } + } +} + /******************************************************************************* ** ** Function GATTS_CreateService @@ -398,6 +412,7 @@ BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_ GATT_TRACE_DEBUG ("Delete a new service changed item - the service has not yet started"); osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); } else { + gatt_update_for_database_change(); if (GATTS_SEND_SERVICE_CHANGE_MODE == GATTS_SEND_SERVICE_CHANGE_AUTO) { gatt_proc_srv_chg(); } @@ -510,6 +525,7 @@ tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, &p_list->asgn_range.svc_uuid, p_list->asgn_range.svc_inst)) != NULL) { + gatt_update_for_database_change(); if (GATTS_SEND_SERVICE_CHANGE_MODE == GATTS_SEND_SERVICE_CHANGE_AUTO) { gatt_proc_srv_chg(); } diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_attr.c b/components/bt/host/bluedroid/stack/gatt/gatt_attr.c index 1360fbaaca..7205558023 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_attr.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_attr.c @@ -29,11 +29,18 @@ #include "stack/gatt_api.h" #include "gatt_int.h" #include "stack/sdpdefs.h" +#include "bta/bta_gatts_co.h" #if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) +#define BLE_GATT_SR_SUPP_FEAT_EATT_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_EATT_BITMASK 0x02 +#define BLE_GATT_CL_SUPP_FEAT_MULTI_NOTIF_BITMASK 0x04 +#define BLE_GATT_CL_SUPP_FEAT_BITMASK 0x07 + #define GATTP_MAX_NUM_INC_SVR 0 -#define GATTP_MAX_CHAR_NUM 2 +#define GATTP_MAX_CHAR_NUM 4 #define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1) #define GATTP_MAX_CHAR_VALUE_SIZE 50 @@ -180,30 +187,104 @@ void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb) ** Returns GATT_SUCCESS if successfully sent; otherwise error code. ** *******************************************************************************/ -tGATT_STATUS gatt_proc_read (tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) +tGATT_STATUS gatt_proc_read (UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) { tGATT_STATUS status = GATT_NO_RESOURCES; + UINT16 len = 0; + UINT8 *value; UNUSED(type); + GATT_TRACE_DEBUG("%s handle %x", __func__, p_data->handle); + + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *tcb = gatt_get_tcb_by_idx(tcb_idx); + if (p_data->is_long) { p_rsp->attr_value.offset = p_data->offset; } p_rsp->attr_value.handle = p_data->handle; - UINT16 len = 0; - uint8_t *value; - status = GATTS_GetAttributeValue(p_data->handle, &len, &value); - if(status == GATT_SUCCESS && len > 0 && value) { - if(len > GATT_MAX_ATTR_LEN) { - len = GATT_MAX_ATTR_LEN; + + /* handle request for reading service changed */ + if (p_data->handle == gatt_cb.handle_of_h_r) { + status = GATTS_GetAttributeValue(p_data->handle, &len, &value); + if(status == GATT_SUCCESS && len > 0 && value) { + if(len > GATT_MAX_ATTR_LEN) { + len = GATT_MAX_ATTR_LEN; + } + p_rsp->attr_value.len = len; + memcpy(p_rsp->attr_value.value, value, len); } - p_rsp->attr_value.len = len; - memcpy(p_rsp->attr_value.value, value, len); + } + + /* handle request for reading client supported features */ + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &tcb->cl_supp_feat, 1); + status = GATT_SUCCESS; + } + + /* handle request for reading database hash */ + if (p_data->handle == gatt_cb.handle_of_database_hash) { + p_rsp->attr_value.len = BT_OCTET16_LEN; + memcpy(p_rsp->attr_value.value, gatt_cb.database_hash, BT_OCTET16_LEN); + gatt_sr_update_cl_status(tcb, true); + status = GATT_SUCCESS; + } + + /* handle request for reading server supported features */ + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &gatt_cb.gatt_sr_supported_feat_mask, 1); + status = GATT_SUCCESS; } return status; } +static tGATT_STATUS gatt_sr_write_cl_supp_feat(UINT16 conn_id, tGATT_WRITE_REQ *p_data) +{ + UINT8 val_new; + UINT8 val_old; + UINT8 val_xor; + UINT8 val_and; + UINT8 *p = p_data->value; + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_DEBUG("%s len %u, feat %x", __func__, p_data->len, *p); + + if (p_tcb == NULL) { + GATT_TRACE_ERROR("%s no conn", __func__); + return GATT_NOT_FOUND; + } + + if (p_data->len != 1) { + GATT_TRACE_ERROR("%s len %u", __func__, p_data->len); + return GATT_INVALID_PDU; + } + + STREAM_TO_UINT8(val_new, p); + val_new = (val_new & BLE_GATT_CL_SUPP_FEAT_BITMASK); + + if (val_new == 0) { + GATT_TRACE_ERROR("%s bit cannot be all zero", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + val_old = p_tcb->cl_supp_feat; + val_xor = val_old ^ val_new; + val_and = val_xor & val_new; + if (val_and != val_xor) { + GATT_TRACE_ERROR("%s bit cannot be reset", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + p_tcb->cl_supp_feat = val_new; + bta_gatts_co_cl_feat_save(p_tcb->peer_bda, &p_tcb->cl_supp_feat); + return GATT_SUCCESS; +} + /****************************************************************************** ** ** Function gatt_proc_write_req @@ -213,12 +294,29 @@ tGATT_STATUS gatt_proc_read (tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATT ** Returns GATT_SUCCESS if successfully sent; otherwise error code. ** *******************************************************************************/ -tGATT_STATUS gatt_proc_write_req( tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) +tGATT_STATUS gatt_proc_write_req(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) { if(p_data->len > GATT_MAX_ATTR_LEN) { p_data->len = GATT_MAX_ATTR_LEN; } - return GATTS_SetAttributeValue(p_data->handle, + + if (p_data->handle == gatt_cb.handle_of_h_r) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + return gatt_sr_write_cl_supp_feat(conn_id, p_data); + } + + if (p_data->handle == gatt_cb.handle_of_database_hash) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + return GATT_WRITE_NOT_PERMIT; + } + + return GATTS_SetAttributeValue(p_data->handle, p_data->len, p_data->value); @@ -244,14 +342,14 @@ static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE switch (type) { case GATTS_REQ_TYPE_READ: - status = gatt_proc_read(type, &p_data->read_req, &rsp_msg); + status = gatt_proc_read(conn_id, type, &p_data->read_req, &rsp_msg); break; case GATTS_REQ_TYPE_WRITE: if (!p_data->write_req.need_rsp) { ignore = TRUE; } - status = gatt_proc_write_req(type, &p_data->write_req); + status = gatt_proc_write_req(conn_id, type, &p_data->write_req); break; case GATTS_REQ_TYPE_WRITE_EXEC: @@ -370,8 +468,21 @@ void gatt_profile_db_init (void) }; GATTS_AddCharDescriptor (service_handle, GATT_PERM_READ | GATT_PERM_WRITE , &descr_uuid, &attr_val, NULL); - /* start service - */ + + /* add Client Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_CLIENT_SUP_FEAT; + gatt_cb.handle_of_cl_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE, NULL, NULL); + + /* add Database Hash characteristic */ + uuid.uu.uuid16 = GATT_UUID_GATT_DATABASE_HASH; + gatt_cb.handle_of_database_hash = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); + + /* add Server Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_SERVER_SUP_FEAT; + gatt_cb.handle_of_sr_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); + + /* start service */ status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); #if (CONFIG_BT_STACK_NO_LOG) @@ -576,4 +687,100 @@ void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSP gatt_cl_start_config_ccc(p_clcb); } +/******************************************************************************* +** +** 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 BOOLEAN gatt_sr_is_cl_robust_caching_supported(tGATT_TCB *p_tcb) +{ + // Server robust caching not enabled + if (!GATTS_ROBUST_CACHING_ENABLED) { + return FALSE; + } + + return (p_tcb->cl_supp_feat & BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK); +} + +/******************************************************************************* +** +** Function gatt_sr_is_cl_change_aware +** +** Description Check if the connection is change-aware +** +** Returns true if change aware, otherwise false +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb) +{ + // If robust caching is not supported, should always return true by default + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return true; + } + + return p_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 *p_tcb) +{ + bta_gatts_co_cl_feat_load(p_tcb->peer_bda, &p_tcb->cl_supp_feat); + + // This is used to reset bit when robust caching is disabled + if (!GATTS_ROBUST_CACHING_ENABLED) { + p_tcb->cl_supp_feat &= ~BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK; + } + + if (gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + BT_OCTET16 stored_hash = {0}; + bta_gatts_co_db_hash_load(p_tcb->peer_bda, stored_hash); + p_tcb->is_robust_cache_change_aware = (memcmp(stored_hash, gatt_cb.database_hash, BT_OCTET16_LEN) == 0); + } else { + p_tcb->is_robust_cache_change_aware = true; + } + + GATT_TRACE_DEBUG("%s feat %x aware %d", __func__, p_tcb->cl_supp_feat, p_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 *p_tcb, BOOLEAN chg_aware) +{ + if (p_tcb == NULL) { + return; + } + + // if robust caching is not supported, do nothing + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return; + } + + // only when client status is changed from unaware to aware, we should store database hash + if (!p_tcb->is_robust_cache_change_aware && chg_aware) { + bta_gatts_co_db_hash_save(p_tcb->peer_bda, gatt_cb.database_hash); + } + + p_tcb->is_robust_cache_change_aware = chg_aware; + + GATT_TRACE_DEBUG("%s status %d", __func__, chg_aware); +} #endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c index 837e7d7157..907b5c9aa4 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -1563,6 +1563,9 @@ static BOOLEAN gatts_proc_ind_ack(tGATT_TCB *p_tcb, UINT16 ack_handle) gatts_proc_srv_chg_ind_ack(p_tcb); /* there is no need to inform the application since srv chg is handled internally by GATT */ continue_processing = FALSE; + + /* after receiving ack of svc_chg_ind, reset client status */ + gatt_sr_update_cl_status(p_tcb, true); } gatts_chk_pending_ind(p_tcb); @@ -1609,6 +1612,85 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) } } +static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + if (gatt_sr_is_cl_change_aware(p_tcb)) { + return false; + } + + bool should_ignore = true; + bool should_rsp = true; + + switch (op_code) { + case GATT_REQ_READ_BY_TYPE: + { + tBT_UUID uuid; + UINT16 s_hdl = 0; + UINT16 e_hdl = 0; + UINT16 db_hash_handle = gatt_cb.handle_of_database_hash; + tGATT_STATUS reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + if (reason == GATT_SUCCESS && + (s_hdl <= db_hash_handle && db_hash_handle <= e_hdl) && + (uuid.uu.uuid16 == GATT_UUID_GATT_DATABASE_HASH)) { + should_ignore = false; + } + break; + } + case GATT_REQ_READ: + // for pts don't process read request + #if 0 + { + UINT16 handle = 0; + UINT8 *p = p_data; + tGATT_STATUS status = GATT_SUCCESS; + + if (len < 2) { + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(handle, p); + len -= 2; + } + + if (status == GATT_SUCCESS && handle == gatt_cb.handle_of_database_hash) { + should_ignore = false; + } + break; + } + #endif + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BLOB: + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + case GATT_REQ_WRITE: + case GATT_REQ_PREPARE_WRITE: + break; + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + should_rsp = false; + break; + case GATT_REQ_MTU: + case GATT_REQ_EXEC_WRITE: + case GATT_HANDLE_VALUE_CONF: + default: + should_ignore = false; + break; + } + + if (should_ignore) { + if (should_rsp) { + gatt_send_error_rsp(p_tcb, GATT_DATABASE_OUT_OF_SYNC, op_code, 0x0000, false); + } + + GATT_TRACE_ERROR("database out of sync op_code %x, should_rsp %d", op_code, should_rsp); + gatt_sr_update_cl_status(p_tcb, should_rsp); + } + + return should_ignore; +} + /******************************************************************************* ** ** Function gatt_server_handle_client_req @@ -1640,6 +1722,11 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, } /* otherwise, ignore the pkt */ } else { + // handle database out of sync + if (gatts_handle_db_out_of_sync(p_tcb, op_code, len, p_data)) { + return; + } + switch (op_code) { case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c index a7b823cd8b..ab11d96b4d 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -1090,6 +1090,7 @@ tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) p_tcb->transport = transport; } memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN); + gatt_sr_init_cl_status(p_tcb); } return p_tcb; } diff --git a/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h b/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h index 05174ee638..4583eda447 100644 --- a/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h @@ -405,6 +405,13 @@ typedef struct { UINT8 pending_cl_req; UINT8 next_slot_inq; /* index of next available slot in queue */ + /* client supported feature */ + UINT8 cl_supp_feat; + /* server supported feature */ + UINT8 sr_supp_feat; + /* if false, should handle database out of sync */ + BOOLEAN is_robust_cache_change_aware; + BOOLEAN in_use; UINT8 tcb_idx; tGATT_PREPARE_WRITE_RECORD prepare_write_record; /* prepare write packets record */ @@ -532,6 +539,12 @@ typedef struct { tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS]; #endif ///GATTS_INCLUDED == TRUE UINT16 handle_of_h_r; /* Handle of the handles reused characteristic value */ + UINT16 handle_of_database_hash; + UINT16 handle_of_cl_supported_feat; + UINT16 handle_of_sr_supported_feat; + BT_OCTET16 database_hash; + UINT8 gatt_sr_supported_feat_mask; + UINT8 gatt_cl_supported_feat_mask; tGATT_APPL_INFO cb_info; @@ -759,4 +772,9 @@ extern BOOLEAN gatt_check_connection_state_by_tcb(tGATT_TCB *p_tcb); extern void gatt_reset_bgdev_list(void); extern uint16_t gatt_get_local_mtu(void); extern void gatt_set_local_mtu(uint16_t mtu); + +extern tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash); +extern BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb); +extern void gatt_sr_init_cl_status(tGATT_TCB *p_tcb); +extern void gatt_sr_update_cl_status(tGATT_TCB *tcb, BOOLEAN chg_aware); #endif diff --git a/components/bt/host/bluedroid/stack/include/stack/gatt_api.h b/components/bt/host/bluedroid/stack/include/stack/gatt_api.h index 7bf1c03694..f8a3143c76 100644 --- a/components/bt/host/bluedroid/stack/include/stack/gatt_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/gatt_api.h @@ -44,6 +44,8 @@ #define GATT_INSUF_ENCRYPTION 0x0f #define GATT_UNSUPPORT_GRP_TYPE 0x10 #define GATT_INSUF_RESOURCE 0x11 +#define GATT_DATABASE_OUT_OF_SYNC 0x12 +#define GATT_VALUE_NOT_ALLOWED 0x13 #define GATT_NO_RESOURCES 0x80 diff --git a/components/bt/host/bluedroid/stack/include/stack/gattdefs.h b/components/bt/host/bluedroid/stack/include/stack/gattdefs.h index 9380e2e9c6..d8e4cb591e 100644 --- a/components/bt/host/bluedroid/stack/include/stack/gattdefs.h +++ b/components/bt/host/bluedroid/stack/include/stack/gattdefs.h @@ -121,4 +121,8 @@ #define GATT_UUID_SCAN_INT_WINDOW 0x2A4F #define GATT_UUID_SCAN_REFRESH 0x2A31 +#define GATT_UUID_CLIENT_SUP_FEAT 0x2B29 +#define GATT_UUID_GATT_DATABASE_HASH 0x2B2A +#define GATT_UUID_SERVER_SUP_FEAT 0x2B3A + #endif