From 51f1d1329534f4e8075138fb0501ae45b0cce2ef Mon Sep 17 00:00:00 2001 From: Zhi Wei Jian Date: Tue, 14 Jul 2026 12:03:41 +0800 Subject: [PATCH] fix(ble/bluedroid): add GATT resource-cleanup helpers (cherry picked from commit b83327f3caa8f614e9d4b86fc80cc05182d0d883) Co-authored-by: zhiweijian --- .../bt/host/bluedroid/stack/gatt/gatt_utils.c | 205 ++++++++++++++++-- .../bluedroid/stack/gatt/include/gatt_int.h | 10 + 2 files changed, 199 insertions(+), 16 deletions(-) diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c index 9fd50c5d2e2..dcd04530e15 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -134,6 +134,119 @@ void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) p_tcb->prepare_write_record.total_num = 0; p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS; } + +/******************************************************************************* +** +** Function gatt_attr_in_svc_db +** +** Description Return TRUE if p_attr belongs to the given service database. +** +*******************************************************************************/ +static BOOLEAN gatt_attr_in_svc_db(tGATT_SVC_DB *p_db, tGATT_ATTR16 *p_attr) +{ + tGATT_ATTR16 *p; + + if (p_db == NULL || p_attr == NULL || p_db->p_attr_list == NULL) { + return FALSE; + } + + for (p = (tGATT_ATTR16 *)p_db->p_attr_list; p != NULL; p = p->p_next) { + if (p == p_attr) { + return TRUE; + } + } + return FALSE; +} + +/******************************************************************************* +** +** Function gatt_purge_prepare_write_for_svc_db +** +** Description Remove queued prepare-write entries that reference attributes +** in p_db. Must be called before freeing that service database. +** +*******************************************************************************/ +static void gatt_purge_prepare_write_for_svc_db(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db) +{ + tGATT_PREPARE_WRITE_QUEUE_DATA *queue_data; + tGATT_PREPARE_WRITE_RECORD *prepare_record; + fixed_queue_t *old_queue; + fixed_queue_t *new_queue; + UINT16 purged = 0; + + if (p_tcb == NULL || p_db == NULL || !p_tcb->in_use) { + return; + } + + prepare_record = &p_tcb->prepare_write_record; + old_queue = prepare_record->queue; + if (old_queue == NULL || fixed_queue_is_empty(old_queue)) { + return; + } + + new_queue = fixed_queue_new(QUEUE_SIZE_MAX); + if (new_queue == NULL) { + GATT_TRACE_ERROR("%s: failed to allocate queue, dropping all prepare writes", __func__); + gatt_free_pending_prepare_write_queue(p_tcb); + return; + } + + while (!fixed_queue_is_empty(old_queue)) { + queue_data = fixed_queue_dequeue(old_queue, FIXED_QUEUE_MAX_TIMEOUT); + if (gatt_attr_in_svc_db(p_db, queue_data->p_attr)) { + osi_free(queue_data); + purged++; + } else { + if (!fixed_queue_enqueue(new_queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT)) { + GATT_TRACE_ERROR("%s: failed to re-queue prepare write entry", __func__); + osi_free(queue_data); + purged++; + } + } + } + + fixed_queue_free(old_queue, NULL); + if (fixed_queue_is_empty(new_queue)) { + fixed_queue_free(new_queue, NULL); + prepare_record->queue = NULL; + } else { + prepare_record->queue = new_queue; + } + + if (purged > 0) { + if (prepare_record->total_num >= purged) { + prepare_record->total_num -= purged; + } else { + prepare_record->total_num = 0; + } + if (prepare_record->queue == NULL && prepare_record->total_num == 0) { + prepare_record->error_code_app = GATT_SUCCESS; + } + } +} + +/******************************************************************************* +** +** Function gatt_purge_prepare_write_before_free_db +** +** Description Purge prepare-write queue entries for p_db on all active TCBs. +** Call before freeing a service database. +** +*******************************************************************************/ +void gatt_purge_prepare_write_before_free_db(tGATT_SVC_DB *p_db) +{ + list_node_t *p_node; + list_node_t *p_next; + tGATT_TCB *p_tcb; + + for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = p_next) { + p_tcb = list_node(p_node); + p_next = list_next(p_node); + if (p_tcb->in_use) { + gatt_purge_prepare_write_for_svc_db(p_tcb, p_db); + } + } +} #endif // (GATTS_INCLUDED == TRUE) #if (GATTS_INCLUDED == TRUE) @@ -413,6 +526,7 @@ void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p) p_value = p_attr->p_value; if ((p_value != NULL) && (p_value->attr_val.attr_val != NULL)){ osi_free(p_value->attr_val.attr_val); + p_value->attr_val.attr_val = NULL; } } p_attr = p_attr->p_next; @@ -460,6 +574,8 @@ void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id) if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) { /* Remove from linked list first */ gatt_remove_an_item_from_list(p_list_info, p_elem); + /* Drop prepare-write queue entries pointing into this DB before free */ + gatt_purge_prepare_write_before_free_db(&p_elem->svc_db); /* Free attribute value buffers */ gatt_free_attr_value_buffer(p_elem); /* Free the handle buffer completely (including svc_buffer and setting in_use = FALSE) */ @@ -490,7 +606,7 @@ BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM p_svc_uuid = gatts_get_service_uuid (p_rcb->p_db); - if (gatt_uuid_compare(value, *p_svc_uuid)) { + if (p_svc_uuid && gatt_uuid_compare(value, *p_svc_uuid)) { is_last_attribute = FALSE; break; @@ -1129,6 +1245,23 @@ BOOLEAN gatt_uuid_compare (tBT_UUID src, tBT_UUID tar) return (memcmp(ps, pt, LEN_UUID_128) == 0); } +/******************************************************************************* +** +** Function gatt_get_uuid_stream_len +** +** Description Get the number of bytes gatt_build_uuid_to_stream writes. +** +** Returns UUID stream length. +** +*******************************************************************************/ +UINT8 gatt_get_uuid_stream_len(tBT_UUID uuid) +{ + if (uuid.len == LEN_UUID_32) { + return LEN_UUID_128; + } + return uuid.len; +} + /******************************************************************************* ** ** Function gatt_build_uuid_to_stream @@ -1259,9 +1392,40 @@ void gatt_start_rsp_timer(UINT16 clcb_idx) void gatt_start_conf_timer(tGATT_TCB *p_tcb) { p_tcb->conf_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; - btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_CONF, GATT_WAIT_FOR_RSP_TOUT); } + +/******************************************************************************* +** +** Function gatt_conf_timeout +** +** Description Called when GATT wait for indication confirmation timer expires +** +** Returns void +** +*******************************************************************************/ +void gatt_conf_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param; + + if (p_tcb == NULL || gatt_get_tcb_by_idx(p_tcb->tcb_idx) != p_tcb) { + GATT_TRACE_WARNING("gatt_conf_timeout tcb is already deleted"); + return; + } + + if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) { + /* Server-only remotes may ignore Service Changed indication; do not disconnect. */ + GATT_TRACE_WARNING("gatt_conf_timeout Service Changed indication timed out, not disconnecting"); + p_tcb->indicate_handle = 0; + gatts_proc_srv_chg_ind_ack(p_tcb); + return; + } + + GATT_TRACE_WARNING("gatt_conf_timeout handle=%u disconnecting...", p_tcb->indicate_handle); + p_tcb->indicate_handle = 0; + gatt_disconnect(p_tcb); +} #endif // (GATTS_INCLUDED == TRUE) #if (GATTC_INCLUDED == TRUE) @@ -1347,23 +1511,24 @@ void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle) { tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param; - GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now"); - - if (p_tcb != NULL) { - p_tcb->ind_count = 0; - } - -#if (BLE_EATT_INCLUDED == TRUE) - if (p_tcb != NULL) { - /* Auto-ack on the bearer the indication arrived on (0 == legacy ATT). */ - p_tcb->eatt_tx_bearer = p_tcb->eatt_ind_bearer; - attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); - p_tcb->eatt_tx_bearer = 0; - p_tcb->eatt_ind_bearer = 0; + if (p_tcb == NULL || gatt_get_tcb_by_idx(p_tcb->tcb_idx) != p_tcb) { + GATT_TRACE_WARNING("gatt_ind_ack_timeout tcb is already deleted"); return; } + + GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now"); + + p_tcb->ind_count = 0; + +#if (BLE_EATT_INCLUDED == TRUE) + /* Auto-ack on the bearer the indication arrived on (0 == legacy ATT). */ + p_tcb->eatt_tx_bearer = p_tcb->eatt_ind_bearer; + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + p_tcb->eatt_tx_bearer = 0; + p_tcb->eatt_ind_bearer = 0; + return; #endif - attp_send_cl_msg(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL); + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); } #endif // (GATTC_INCLUDED == TRUE) @@ -2704,6 +2869,7 @@ BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN for (j = i + 1; j < GATT_MAX_APPS; j ++) { p_dev->gatt_if[j - 1] = p_dev->gatt_if[j]; } + p_dev->gatt_if[GATT_MAX_APPS - 1] = 0; if (p_dev->gatt_if[0] == 0) { ret = BTM_BleUpdateBgConnDev(FALSE, p_dev->remote_bda); @@ -2721,6 +2887,7 @@ BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN for (j = i + 1; j < GATT_MAX_APPS; j ++) { p_dev->listen_gif[j - 1] = p_dev->listen_gif[j]; } + p_dev->listen_gif[GATT_MAX_APPS - 1] = 0; if (p_dev->listen_gif[0] == 0) { // To check, we do not support background connection, code will not be called here @@ -2766,6 +2933,7 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if) for (k = j + 1; k < GATT_MAX_APPS; k ++) { p_dev_list->gatt_if[k - 1] = p_dev_list->gatt_if[k]; } + p_dev_list->gatt_if[GATT_MAX_APPS - 1] = 0; if (p_dev_list->gatt_if[0] == 0) { BTM_BleUpdateBgConnDev(FALSE, p_dev_list->remote_bda); @@ -2783,6 +2951,7 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if) for (k = j + 1; k < GATT_MAX_APPS; k ++) { p_dev_list->listen_gif[k - 1] = p_dev_list->listen_gif[k]; } + p_dev_list->listen_gif[GATT_MAX_APPS - 1] = 0; if (p_dev_list->listen_gif[0] == 0) { // To check, we do not support background connection, code will not be called here @@ -2790,6 +2959,10 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if) } } } + + if (p_dev_list->gatt_if[0] == 0 && p_dev_list->listen_gif[0] == 0) { + memset(p_dev_list, 0, sizeof(tGATT_BG_CONN_DEV)); + } } } } 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 b6fe0e6b207..bc61b08d882 100644 --- a/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/host/bluedroid/stack/gatt/include/gatt_int.h @@ -687,6 +687,7 @@ extern UINT8 *gatt_dbg_op_name(UINT8 op_code); extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl); #endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE extern BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid, UINT16 len, UINT8 **p_data); +extern UINT8 gatt_get_uuid_stream_len(tBT_UUID uuid); extern UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid); extern BOOLEAN gatt_uuid_compare(tBT_UUID src, tBT_UUID tar); extern void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32); @@ -694,6 +695,8 @@ extern char *gatt_uuid_to_str(const tBT_UUID *uuid); extern void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size); extern void gatt_start_rsp_timer(UINT16 clcb_idx); extern void gatt_start_conf_timer(tGATT_TCB *p_tcb); +extern void gatt_conf_timeout(TIMER_LIST_ENT *p_tle); +extern void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb); extern void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle); extern void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle); extern void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb); @@ -719,6 +722,7 @@ extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_hand extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); extern void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p); +extern void gatt_purge_prepare_write_before_free_db(tGATT_SVC_DB *p_db); extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); extern void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list); extern BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new); @@ -784,6 +788,12 @@ extern void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb); extern UINT8 gatt_send_write_msg(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle, UINT16 len, UINT16 offset, UINT8 *p_data); extern void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport); +#if (SMP_INCLUDED == TRUE) +extern void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb); +#endif // (SMP_INCLUDED == TRUE) +#if (GATTS_INCLUDED == TRUE) +extern void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb); +#endif // (GATTS_INCLUDED == TRUE) extern void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data); extern void gatt_act_discovery(tGATT_CLCB *p_clcb);