From e7f6bf115ec3a0af643a6ae247fd4b2ad92a7f16 Mon Sep 17 00:00:00 2001 From: Zhi Wei Jian Date: Tue, 14 Jul 2026 12:04:04 +0800 Subject: [PATCH] fix(ble/bluedroid): fix GATT teardown and service-change flow (cherry picked from commit 0645ba469d26c93f3d267481be7899f1700b44d3) Co-authored-by: zhiweijian --- .../bt/host/bluedroid/stack/gatt/gatt_main.c | 73 +++++++++++++------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_main.c b/components/bt/host/bluedroid/stack/gatt/gatt_main.c index ecf4f3bcb81..87df03a3cb8 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_main.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_main.c @@ -173,7 +173,7 @@ void gatt_free(void) { GATT_TRACE_DEBUG("gatt_free()"); #if (GATTS_INCLUDED == TRUE) - fixed_queue_free(gatt_cb.srv_chg_clt_q, NULL); + fixed_queue_free(gatt_cb.srv_chg_clt_q, osi_free_func); gatt_cb.srv_chg_clt_q = NULL; fixed_queue_free(gatt_cb.pending_new_srv_start_q, osi_free_func); gatt_cb.pending_new_srv_start_q = NULL; @@ -185,30 +185,42 @@ void gatt_free(void) * would dereference the already-freed l2c_cb_ptr. */ list_node_t *p_node = NULL; + list_node_t *p_next = NULL; tGATT_TCB *p_tcb = NULL; - for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { - p_tcb = list_node(p_node); + tGATT_CLCB *p_clcb = NULL; + + for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); #if (SMP_INCLUDED == TRUE) - fixed_queue_free(p_tcb->pending_enc_clcb, NULL); - p_tcb->pending_enc_clcb = NULL; + gatt_free_pending_enc_queue(p_tcb); #endif // (SMP_INCLUDED == TRUE) #if (GATTS_INCLUDED == TRUE) + gatt_free_pending_prepare_write_queue(p_tcb); btu_free_timer(&p_tcb->conf_timer_ent); memset(&p_tcb->conf_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + gatt_dequeue_sr_cmd(p_tcb); #endif // (GATTS_INCLUDED == TRUE) #if (GATTC_INCLUDED == TRUE) btu_free_timer(&p_tcb->ind_ack_timer_ent); memset(&p_tcb->ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT)); -#endif // #if (GATTC_INCLUDED == TRUE) +#endif // (GATTC_INCLUDED == TRUE) -#if (GATTS_INCLUDED == TRUE) - fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); - p_tcb->sr_cmd.multi_rsp_q = NULL; -#endif /* #if (GATTS_INCLUDED == TRUE) */ UNUSED(p_tcb); } list_free(gatt_cb.p_tcb_list); + + for (p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = p_next) { + p_clcb = list_node(p_node); + p_next = list_next(p_node); + if (p_clcb->p_attr_buf) { + osi_free(p_clcb->p_attr_buf); + p_clcb->p_attr_buf = NULL; + } + btu_free_timer(&p_clcb->rsp_timer_ent); + memset(&p_clcb->rsp_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + list_remove(gatt_cb.p_clcb_list, p_clcb); + } list_free(gatt_cb.p_clcb_list); #if (GATTS_INCLUDED == TRUE) @@ -425,11 +437,9 @@ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, // p_tcb, p_tcb->pending_enc_clcb, and p_tcb->pending_ind_q have been freed in gatt_cleanup_upon_disc(), // but here p_tcb is get from gatt_allocate_tcb_by_bdaddr(), is too old, so we get p_tcb again p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); - if(p_tcb != NULL) { + if (p_tcb != NULL) { #if (SMP_INCLUDED == TRUE) - if(p_tcb->pending_enc_clcb != NULL) { - fixed_queue_free(p_tcb->pending_enc_clcb, NULL); - } + gatt_free_pending_enc_queue(p_tcb); #endif // (SMP_INCLUDED == TRUE) gatt_tcb_free(p_tcb); } @@ -947,6 +957,7 @@ static void gatt_l2cif_congest_cback (UINT16 lcid, BOOLEAN congested) static void gatt_send_conn_cback(tGATT_TCB *p_tcb) { UINT8 i; + UINT8 tcb_idx = p_tcb->tcb_idx; tGATT_REG *p_reg; #if (GATT_BG_CONN_DEV == TRUE) tGATT_BG_CONN_DEV *p_bg_dev = NULL; @@ -959,6 +970,9 @@ static void gatt_send_conn_cback(tGATT_TCB *p_tcb) /* notifying all applications for the connection up event */ for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) { + if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) { + return; + } if (p_reg->in_use) { #if (GATT_BG_CONN_DEV == TRUE) if (p_bg_dev && gatt_is_bg_dev_for_app(p_bg_dev, p_reg->gatt_if)) { @@ -966,14 +980,19 @@ static void gatt_send_conn_cback(tGATT_TCB *p_tcb) } #endif // #if (GATT_BG_CONN_DEV == TRUE) if (p_reg->app_cb.p_conn_cb) { - conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + conn_id = GATT_CREATE_CONN_ID(tcb_idx, p_reg->gatt_if); (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, TRUE, 0, p_tcb->transport); + if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) { + return; + } } } } - + if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) { + return; + } if (gatt_num_apps_hold_link(p_tcb) && p_tcb->att_lcid == L2CAP_ATT_CID ) { /* disable idle timeout if one or more clients are holding the link disable the idle timer */ GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport); @@ -1119,10 +1138,19 @@ tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda) *******************************************************************************/ void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt) { + tGATT_STATUS status; + GATT_TRACE_DEBUG("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed ); - if (p_srv_chg_clt->srv_changed) { - gatt_send_srv_chg_ind(p_srv_chg_clt->bda); + if (!p_srv_chg_clt->srv_changed) { + return; + } + + status = gatt_send_srv_chg_ind(p_srv_chg_clt->bda); + if (status == GATT_BUSY || status == GATT_CONGESTED) { + GATT_TRACE_DEBUG("gatt_chk_srv_chg: defer srv chg ind (status=0x%02x)", status); + } else if (status != GATT_SUCCESS && status != GATT_PENDING) { + GATT_TRACE_WARNING("gatt_chk_srv_chg: send srv chg ind failed (status=0x%02x)", status); } } #endif ///GATTS_INCLUDED == TRUE @@ -1183,7 +1211,6 @@ void gatt_init_srv_chg (void) #if (GATTS_INCLUDED == TRUE) void gatt_proc_srv_chg (void) { - BOOLEAN srv_chg_ind_pending = FALSE; tGATT_TCB *p_tcb; list_node_t *p_node = NULL; @@ -1195,11 +1222,11 @@ void gatt_proc_srv_chg (void) for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { p_tcb = list_node(p_node); if (p_tcb->in_use && p_tcb->ch_state == GATT_CH_OPEN) { - srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); - if (!srv_chg_ind_pending) { - gatt_send_srv_chg_ind(p_tcb->peer_bda); + if (gatt_is_srv_chg_ind_pending(p_tcb) || + GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { + GATT_TRACE_DEBUG ("defer srv chg - indication slot busy"); } else { - GATT_TRACE_DEBUG ("discard srv chg - already has one in the queue"); + gatt_send_srv_chg_ind(p_tcb->peer_bda); } } }