diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c index eb6bb9b9bc7..b145e2daec7 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -73,6 +73,48 @@ tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len) return status; } +/******************************************************************************* +** +** Function gatt_sr_next_trans_id +** +** Description Allocate the next transaction ID in [1, GATT_TRANS_ID_MAX - 1]. +** Zero is reserved for enqueue failure (sr_cmd busy). +** +*******************************************************************************/ +static UINT32 gatt_sr_next_trans_id(tGATT_TCB *p_tcb) +{ + UINT32 trans_id = p_tcb->trans_id % GATT_TRANS_ID_MAX; + + trans_id = (trans_id + 1) % GATT_TRANS_ID_MAX; + if (trans_id == 0) { + trans_id = 1; + } + p_tcb->trans_id = trans_id; + return trans_id; +} + +/******************************************************************************* +** +** Function gatt_sr_busy_error_code +** +** Description Pick an ATT error code for a request received while another +** server procedure is pending. Common profile codes (0xFE) are +** not valid for all request types per ATT Table 3.44. +** +*******************************************************************************/ +static UINT8 gatt_sr_busy_error_code(UINT8 op_code) +{ + switch (op_code) { + case GATT_REQ_FIND_TYPE_VALUE: + return GATT_REQ_NOT_SUPPORTED; + case GATT_REQ_FIND_INFO: + /* ATT Table 3.44: only Invalid Handle (0x01) and Not Found (0x0A) are valid. */ + return GATT_NOT_FOUND; + default: + return GATT_PRC_IN_PROGRESS; + } +} + /******************************************************************************* ** ** Function gatt_sr_enqueue_cmd @@ -88,21 +130,20 @@ UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle) tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd; UINT32 trans_id = 0; - if ( (p_cmd->op_code == 0) || - (op_code == GATT_HANDLE_VALUE_CONF)) { /* no pending request */ - if (op_code == GATT_CMD_WRITE || - op_code == GATT_SIGN_CMD_WRITE || - op_code == GATT_REQ_MTU || - op_code == GATT_HANDLE_VALUE_CONF) { - trans_id = ++p_tcb->trans_id; - } else { - p_cmd->trans_id = ++p_tcb->trans_id; - p_cmd->op_code = op_code; - p_cmd->handle = handle; - p_cmd->status = GATT_NOT_FOUND; - p_tcb->trans_id %= GATT_TRANS_ID_MAX; - trans_id = p_cmd->trans_id; - } + /* No-tracking ops do not occupy sr_cmd and may arrive while a request is pending. */ + if (op_code == GATT_CMD_WRITE || + op_code == GATT_SIGN_CMD_WRITE || + op_code == GATT_REQ_MTU || + op_code == GATT_HANDLE_VALUE_CONF) { + return gatt_sr_next_trans_id(p_tcb); + } + + if (p_cmd->op_code == 0) { + p_cmd->trans_id = gatt_sr_next_trans_id(p_tcb); + p_cmd->op_code = op_code; + p_cmd->handle = handle; + p_cmd->status = GATT_NOT_FOUND; + trans_id = p_cmd->trans_id; } return trans_id; @@ -564,6 +605,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U BOOLEAN sr_cmd_already_dequeued = FALSE; tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + tGATTS_DATA sr_data = {0}; /* Fix: Validate minimum length (flags: 1 byte) */ if (len < 1) { @@ -587,6 +629,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U /* mask the flag */ flag &= GATT_PREP_WRITE_EXEC; + sr_data.exec_write = flag; prepare_record = &(p_tcb->prepare_write_record); queue_num = fixed_queue_length(prepare_record->queue); @@ -663,7 +706,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE_EXEC, - (tGATTS_DATA *)&flag); + &sr_data); p_tcb->prep_cnt[i] = 0; } } @@ -745,7 +788,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE_EXEC, - (tGATTS_DATA *)&flag); + &sr_data); p_tcb->prep_cnt[i] = 0; } } @@ -773,6 +816,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U tGATT_STATUS err = GATT_SUCCESS; UINT8 sec_flag, key_size; tGATTS_RSP *p_msg; + BOOLEAN sr_cmd_enqueued = FALSE; GATT_TRACE_DEBUG("gatt_process_read_multi_req" ); p_tcb->sr_cmd.multi_req.num_handles = 0; @@ -831,6 +875,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U if (err == GATT_SUCCESS) { if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) { + sr_cmd_enqueued = TRUE; gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/ for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) { @@ -854,12 +899,16 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U if (err == GATT_SUCCESS || err == GATT_STACK_RSP) { gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if , trans_id, op_code, GATT_SUCCESS, p_msg); + } else if (err != GATT_PENDING && err != GATT_BUSY) { + osi_free(p_msg); + break; } /* either not using or done using the buffer, release it now */ osi_free(p_msg); } else { err = GATT_NO_RESOURCES; gatt_dequeue_sr_cmd(p_tcb); + sr_cmd_enqueued = FALSE; break; } } @@ -869,7 +918,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U } /* in theroy BUSY is not possible(should already been checked), protected check */ if (err != GATT_SUCCESS && err != GATT_STACK_RSP && err != GATT_PENDING && err != GATT_BUSY) { - gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE); + gatt_send_error_rsp(p_tcb, err, op_code, handle, sr_cmd_enqueued); } } @@ -909,7 +958,7 @@ static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_ p_rcb->type == GATT_UUID_PRI_SERVICE) { if ((p_uuid = gatts_get_service_uuid (p_rcb->p_db)) != NULL) { if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { - handle_len = 4 + p_uuid->len; + handle_len = 4 + gatt_get_uuid_stream_len(*p_uuid); } /* get the length byte in the response */ @@ -1189,11 +1238,15 @@ static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, if (p_rcb->in_use && !(p_rcb->s_hdl > e_hdl || p_rcb->e_hdl < s_hdl)) { - reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl); - if (reason == GATT_NO_RESOURCES) { + tGATT_STATUS build_status = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl); + if (build_status == GATT_SUCCESS) { + reason = GATT_SUCCESS; + } else if (build_status == GATT_NO_RESOURCES) { reason = GATT_SUCCESS; break; } + /* GATT_NOT_FOUND for this service: keep reason (do not discard + * attributes already added from other services). */ } p_srv = p_srv->p_next; } @@ -1230,6 +1283,7 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) UINT8 *p = p_data, i; BT_HDR *p_buf; UINT16 conn_id; + tGATTS_DATA sr_data = {0}; #if (BLE_EATT_INCLUDED == TRUE) /* Exchange MTU applies to Legacy ATT bearer only (Core Spec Vol 3 Part G 5.3). */ @@ -1267,11 +1321,12 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) /* Notify all registered application with new MTU size. Us a transaction ID */ /* of 0, as no response is allowed from applications */ + sr_data.mtu = p_tcb->payload_size; for (i = 0; i < GATT_MAX_APPS; i ++) { if (gatt_cb.cl_rcb[i].in_use ) { conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if); gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, - (tGATTS_DATA *)&p_tcb->payload_size); + &sr_data); } } @@ -1379,7 +1434,13 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, } p_srv = p_srv->p_next; } - *p = (UINT8)p_msg->offset; + /* Defensive: Read By Type response record length is a 1-octet field (<= 255). */ + if (p_msg->offset > UINT8_MAX) { + GATT_TRACE_ERROR("%s: invalid ReadByType pair_len=%u (>255)", __func__, p_msg->offset); + reason = GATT_INVALID_PDU; + } else { + *p = (UINT8)p_msg->offset; + } p_msg->offset = L2CAP_MIN_OFFSET; } } @@ -1831,7 +1892,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, ** Returns void ** *******************************************************************************/ -static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb ) +void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb ) { tGATTS_SRV_CHG_REQ req; tGATTS_SRV_CHG *p_buf = NULL; @@ -1904,6 +1965,10 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) { if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) { trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + if (trans_id == 0) { + GATT_TRACE_ERROR("%s: no trans_id for handle conf 0x%04x", __func__, handle); + continue; + } conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if); tGATTS_DATA p_data = {0}; p_data.handle = handle; @@ -1912,6 +1977,15 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) } } } + + /* Retry Service Changed if a previous attempt was deferred (GATT_BUSY). */ + { + tGATTS_SRV_CHG *p_srv_chg_clt; + + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { + gatt_chk_srv_chg(p_srv_chg_clt); + } + } } else { GATT_TRACE_ERROR("unexpected handle value confirmation"); } @@ -2012,10 +2086,14 @@ static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code, void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) { - /* there is pending command, discard this one */ - if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) { - GATT_TRACE_WARNING("%s discard command opcode=%02x", __func__, op_code); - return; + if (!gatt_sr_cmd_empty(p_tcb)) { + if (op_code == GATT_CMD_WRITE || op_code == GATT_SIGN_CMD_WRITE) { + /* ATT commands have no flow control and may arrive while a request is pending. */ + } else if (op_code != GATT_HANDLE_VALUE_CONF && op_code != GATT_REQ_MTU) { + GATT_TRACE_WARNING("%s reject opcode=%02x, procedure in progress", __func__, op_code); + gatt_send_error_rsp(p_tcb, gatt_sr_busy_error_code(op_code), op_code, 0, FALSE); + return; + } } /* the size of the message may not be bigger than the local max PDU size*/