blob: f273f626549b487aac649497db6dca06a1501f19 [file] [log] [blame]
/******************************************************************************
*
* Copyright 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* this file contains the main GATT client functions
*
******************************************************************************/
#define LOG_TAG "bluetooth"
#include <base/logging.h>
#include <bluetooth/log.h>
#include <string.h>
#include "gatt_int.h"
#include "hardware/bt_gatt_types.h"
#include "include/check.h"
#include "internal_include/bt_target.h"
#include "internal_include/bt_trace.h"
#include "os/log.h"
#include "osi/include/allocator.h"
#include "osi/include/osi.h"
#include "stack/arbiter/acl_arbiter.h"
#include "stack/eatt/eatt.h"
#include "stack/include/bt_types.h"
#include "types/bluetooth/uuid.h"
#define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */
#define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80)
#define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90)
#define GATT_PREP_WRITE_RSP_MIN_LEN 4
#define GATT_NOTIFICATION_MIN_LEN 2
#define GATT_WRITE_RSP_MIN_LEN 2
#define GATT_INFO_RSP_MIN_LEN 1
#define GATT_MTU_RSP_MIN_LEN 2
#define GATT_READ_BY_TYPE_RSP_MIN_LEN 1
#define L2CAP_PKT_OVERHEAD 4
using namespace bluetooth;
using bluetooth::Uuid;
using bluetooth::eatt::EattExtension;
using bluetooth::eatt::EattChannel;
/*******************************************************************************
* G L O B A L G A T T D A T A *
******************************************************************************/
void gatt_send_prepare_write(tGATT_TCB& tcb, tGATT_CLCB* p_clcb);
uint8_t disc_type_to_att_opcode[GATT_DISC_MAX] = {
0,
GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */
GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */
GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */
GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */
GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */
};
uint16_t disc_type_to_uuid[GATT_DISC_MAX] = {
0, /* reserved */
GATT_UUID_PRI_SERVICE, /* <service> DISC_SRVC_ALL */
GATT_UUID_PRI_SERVICE, /* <service> for DISC_SERVC_BY_UUID */
GATT_UUID_INCLUDE_SERVICE, /* <include_service> for DISC_INC_SRVC */
GATT_UUID_CHAR_DECLARE, /* <characteristic> for DISC_CHAR */
0 /* no type filtering for DISC_CHAR_DSCPT */
};
/*******************************************************************************
*
* Function gatt_act_discovery
*
* Description GATT discovery operation.
*
* Returns void.
*
******************************************************************************/
void gatt_act_discovery(tGATT_CLCB* p_clcb) {
uint8_t op_code = disc_type_to_att_opcode[p_clcb->op_subtype];
if (p_clcb->s_handle > p_clcb->e_handle || p_clcb->s_handle == 0) {
log::debug("Completed GATT discovery of all handle ranges");
gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
return;
}
tGATT_CL_MSG cl_req;
memset(&cl_req, 0, sizeof(tGATT_CL_MSG));
cl_req.browse.s_handle = p_clcb->s_handle;
cl_req.browse.e_handle = p_clcb->e_handle;
if (disc_type_to_uuid[p_clcb->op_subtype] != 0) {
cl_req.browse.uuid =
bluetooth::Uuid::From16Bit(disc_type_to_uuid[p_clcb->op_subtype]);
}
if (p_clcb->op_subtype ==
GATT_DISC_SRVC_BY_UUID) /* fill in the FindByTypeValue request info*/
{
cl_req.find_type_value.uuid =
bluetooth::Uuid::From16Bit(disc_type_to_uuid[p_clcb->op_subtype]);
cl_req.find_type_value.s_handle = p_clcb->s_handle;
cl_req.find_type_value.e_handle = p_clcb->e_handle;
size_t size = p_clcb->uuid.GetShortestRepresentationSize();
cl_req.find_type_value.value_len = size;
if (size == Uuid::kNumBytes16) {
uint8_t* p = cl_req.find_type_value.value;
UINT16_TO_STREAM(p, p_clcb->uuid.As16Bit());
} else if (size == Uuid::kNumBytes32) {
/* if service type is 32 bits UUID, convert it now */
memcpy(cl_req.find_type_value.value, p_clcb->uuid.To128BitLE().data(),
Uuid::kNumBytes128);
cl_req.find_type_value.value_len = Uuid::kNumBytes128;
} else
memcpy(cl_req.find_type_value.value, p_clcb->uuid.To128BitLE().data(),
size);
}
tGATT_STATUS st = attp_send_cl_msg(*p_clcb->p_tcb, p_clcb, op_code, &cl_req);
if (st != GATT_SUCCESS && st != GATT_CMD_STARTED) {
log::warn("Unable to send ATT message");
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
}
}
/*******************************************************************************
*
* Function gatt_act_read
*
* Description GATT read operation.
*
* Returns void.
*
******************************************************************************/
void gatt_act_read(tGATT_CLCB* p_clcb, uint16_t offset) {
tGATT_TCB& tcb = *p_clcb->p_tcb;
tGATT_STATUS rt = GATT_INTERNAL_ERROR;
tGATT_CL_MSG msg;
uint8_t op_code = 0;
memset(&msg, 0, sizeof(tGATT_CL_MSG));
switch (p_clcb->op_subtype) {
case GATT_READ_CHAR_VALUE:
case GATT_READ_BY_TYPE:
op_code = GATT_REQ_READ_BY_TYPE;
msg.browse.s_handle = p_clcb->s_handle;
msg.browse.e_handle = p_clcb->e_handle;
if (p_clcb->op_subtype == GATT_READ_BY_TYPE)
msg.browse.uuid = p_clcb->uuid;
else {
msg.browse.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_CHAR_DECLARE);
}
break;
case GATT_READ_CHAR_VALUE_HDL:
case GATT_READ_BY_HANDLE:
if (!p_clcb->counter) {
op_code = GATT_REQ_READ;
msg.handle = p_clcb->s_handle;
} else {
if (!p_clcb->first_read_blob_after_read)
p_clcb->first_read_blob_after_read = true;
else
p_clcb->first_read_blob_after_read = false;
log::verbose("first_read_blob_after_read={}",
p_clcb->first_read_blob_after_read);
op_code = GATT_REQ_READ_BLOB;
msg.read_blob.offset = offset;
msg.read_blob.handle = p_clcb->s_handle;
}
p_clcb->op_subtype &= ~0x80;
break;
case GATT_READ_PARTIAL:
op_code = GATT_REQ_READ_BLOB;
msg.read_blob.handle = p_clcb->s_handle;
msg.read_blob.offset = offset;
break;
case GATT_READ_MULTIPLE:
op_code = GATT_REQ_READ_MULTI;
memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
break;
case GATT_READ_MULTIPLE_VAR_LEN:
op_code = GATT_REQ_READ_MULTI_VAR;
memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
break;
case GATT_READ_INC_SRV_UUID128:
op_code = GATT_REQ_READ;
msg.handle = p_clcb->s_handle;
p_clcb->op_subtype &= ~0x90;
break;
default:
log::error("Unknown read type:{}", p_clcb->op_subtype);
break;
}
if (op_code != 0) rt = attp_send_cl_msg(tcb, p_clcb, op_code, &msg);
if (op_code == 0 || (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)) {
gatt_end_operation(p_clcb, rt, NULL);
}
}
/** GATT write operation */
void gatt_act_write(tGATT_CLCB* p_clcb, uint8_t sec_act) {
tGATT_TCB& tcb = *p_clcb->p_tcb;
CHECK(p_clcb->p_attr_buf);
tGATT_VALUE& attr = *((tGATT_VALUE*)p_clcb->p_attr_buf);
uint16_t payload_size = gatt_tcb_get_payload_size(tcb, p_clcb->cid);
switch (p_clcb->op_subtype) {
case GATT_WRITE_NO_RSP: {
p_clcb->s_handle = attr.handle;
uint8_t op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE
: GATT_CMD_WRITE;
tGATT_STATUS rt = gatt_send_write_msg(tcb, p_clcb, op_code, attr.handle,
attr.len, 0, attr.value);
if (rt != GATT_CMD_STARTED) {
if (rt != GATT_SUCCESS) {
log::error("gatt_act_write() failed op_code=0x{:x} rt={}", op_code,
rt);
}
gatt_end_operation(p_clcb, rt, NULL);
}
return;
}
case GATT_WRITE: {
if (attr.len <= (payload_size - GATT_HDR_SIZE)) {
p_clcb->s_handle = attr.handle;
tGATT_STATUS rt = gatt_send_write_msg(
tcb, p_clcb, GATT_REQ_WRITE, attr.handle, attr.len, 0, attr.value);
if (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED &&
rt != GATT_CONGESTED) {
if (rt != GATT_SUCCESS) {
log::error("gatt_act_write() failed op_code=0x{:x} rt={}",
GATT_REQ_WRITE, rt);
}
gatt_end_operation(p_clcb, rt, NULL);
}
} else {
/* prepare write for long attribute */
gatt_send_prepare_write(tcb, p_clcb);
}
return;
}
case GATT_WRITE_PREPARE:
gatt_send_prepare_write(tcb, p_clcb);
return;
default:
CHECK(false) << "Unknown write type" << p_clcb->op_subtype;
return;
}
}
/*******************************************************************************
*
* Function gatt_send_queue_write_cancel
*
* Description send queue write cancel
*
* Returns void.
*
******************************************************************************/
void gatt_send_queue_write_cancel(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
tGATT_EXEC_FLAG flag) {
tGATT_STATUS rt;
log::verbose("");
tGATT_CL_MSG gatt_cl_msg;
gatt_cl_msg.exec_write = flag;
rt = attp_send_cl_msg(tcb, p_clcb, GATT_REQ_EXEC_WRITE, &gatt_cl_msg);
if (rt != GATT_SUCCESS) {
gatt_end_operation(p_clcb, rt, NULL);
}
}
/*******************************************************************************
*
* Function gatt_check_write_long_terminate
*
* Description To terminate write long or not.
*
* Returns true: write long is terminated; false keep sending.
*
******************************************************************************/
bool gatt_check_write_long_terminate(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
tGATT_VALUE* p_rsp_value) {
tGATT_VALUE* p_attr = (tGATT_VALUE*)p_clcb->p_attr_buf;
bool terminate = false;
tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC;
log::verbose("");
/* check the first write response status */
if (p_rsp_value != NULL) {
if (p_rsp_value->handle != p_attr->handle ||
p_rsp_value->len != p_clcb->counter ||
memcmp(p_rsp_value->value, p_attr->value + p_attr->offset,
p_rsp_value->len)) {
/* data does not match */
p_clcb->status = GATT_ERROR;
flag = GATT_PREP_WRITE_CANCEL;
terminate = true;
} else /* response checking is good */
{
p_clcb->status = GATT_SUCCESS;
/* update write offset and check if end of attribute value */
if ((p_attr->offset += p_rsp_value->len) >= p_attr->len) terminate = true;
}
}
if (terminate && p_clcb->op_subtype != GATT_WRITE_PREPARE) {
gatt_send_queue_write_cancel(tcb, p_clcb, flag);
}
return terminate;
}
/** Send prepare write */
void gatt_send_prepare_write(tGATT_TCB& tcb, tGATT_CLCB* p_clcb) {
tGATT_VALUE* p_attr = (tGATT_VALUE*)p_clcb->p_attr_buf;
uint8_t type = p_clcb->op_subtype;
log::verbose("type=0x{:x}", type);
uint16_t to_send = p_attr->len - p_attr->offset;
uint16_t payload_size = gatt_tcb_get_payload_size(tcb, p_clcb->cid);
if (to_send > (payload_size -
GATT_WRITE_LONG_HDR_SIZE)) /* 2 = uint16_t offset bytes */
to_send = payload_size - GATT_WRITE_LONG_HDR_SIZE;
p_clcb->s_handle = p_attr->handle;
uint16_t offset = p_attr->offset;
if (type == GATT_WRITE_PREPARE) {
offset += p_clcb->start_offset;
}
log::verbose("offset =0x{:x} len={}", offset, to_send);
tGATT_STATUS rt = gatt_send_write_msg(
tcb, p_clcb, GATT_REQ_PREPARE_WRITE, p_attr->handle, to_send, /* length */
offset, /* used as offset */
p_attr->value + p_attr->offset); /* data */
/* remember the write long attribute length */
p_clcb->counter = to_send;
if (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) {
gatt_end_operation(p_clcb, rt, NULL);
}
}
/*******************************************************************************
*
* Function gatt_process_find_type_value_rsp
*
* Description This function handles the find by type value response.
*
*
* Returns void
*
******************************************************************************/
void gatt_process_find_type_value_rsp(UNUSED_ATTR tGATT_TCB& tcb,
tGATT_CLCB* p_clcb, uint16_t len,
uint8_t* p_data) {
tGATT_DISC_RES result;
uint8_t* p = p_data;
log::verbose("");
/* unexpected response */
if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY ||
p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID)
return;
memset(&result, 0, sizeof(tGATT_DISC_RES));
result.type = bluetooth::Uuid::From16Bit(GATT_UUID_PRI_SERVICE);
/* returns a series of handle ranges */
while (len >= 4) {
STREAM_TO_UINT16(result.handle, p);
STREAM_TO_UINT16(result.value.group_value.e_handle, p);
result.value.group_value.service_type = p_clcb->uuid;
len -= 4;
if (p_clcb->p_reg->app_cb.p_disc_res_cb)
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(
p_clcb->conn_id, static_cast<tGATT_DISC_TYPE>(p_clcb->op_subtype),
&result);
}
/* last handle + 1 */
p_clcb->s_handle = (result.value.group_value.e_handle == 0)
? 0
: (result.value.group_value.e_handle + 1);
/* initiate another request */
gatt_act_discovery(p_clcb);
}
/*******************************************************************************
*
* Function gatt_process_read_info_rsp
*
* Description This function is called to handle the read information
* response.
*
*
* Returns void
*
******************************************************************************/
void gatt_process_read_info_rsp(UNUSED_ATTR tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
UNUSED_ATTR uint8_t op_code, uint16_t len,
uint8_t* p_data) {
tGATT_DISC_RES result;
uint8_t *p = p_data, uuid_len = 0, type;
if (len < GATT_INFO_RSP_MIN_LEN) {
log::error("invalid Info Response PDU received, discard.");
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
return;
}
/* unexpected response */
if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY ||
p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT)
return;
STREAM_TO_UINT8(type, p);
len -= 1;
if (type == GATT_INFO_TYPE_PAIR_16)
uuid_len = Uuid::kNumBytes16;
else if (type == GATT_INFO_TYPE_PAIR_128)
uuid_len = Uuid::kNumBytes128;
while (len >= uuid_len + 2) {
STREAM_TO_UINT16(result.handle, p);
if (uuid_len > 0) {
if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) break;
} else
result.type = p_clcb->uuid;
len -= (uuid_len + 2);
if (p_clcb->p_reg->app_cb.p_disc_res_cb)
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(
p_clcb->conn_id, static_cast<tGATT_DISC_TYPE>(p_clcb->op_subtype),
&result);
}
p_clcb->s_handle = (result.handle == 0) ? 0 : (result.handle + 1);
/* initiate another request */
gatt_act_discovery(p_clcb);
}
/*******************************************************************************
*
* Function gatt_proc_disc_error_rsp
*
* Description Process the read by type response and send another request
* if needed.
*
* Returns void.
*
******************************************************************************/
void gatt_proc_disc_error_rsp(UNUSED_ATTR tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
uint8_t opcode, UNUSED_ATTR uint16_t handle,
uint8_t reason) {
tGATT_STATUS status = (tGATT_STATUS)reason;
log::verbose("reason: {:02x} cmd_code {:04x}", reason, opcode);
switch (opcode) {
case GATT_REQ_READ_BY_GRP_TYPE:
case GATT_REQ_FIND_TYPE_VALUE:
case GATT_REQ_READ_BY_TYPE:
case GATT_REQ_FIND_INFO:
if (reason == GATT_NOT_FOUND) {
status = GATT_SUCCESS;
log::verbose("Discovery completed");
}
break;
default:
log::error("Incorrect discovery opcode {:04x}", opcode);
break;
}
gatt_end_operation(p_clcb, status, NULL);
}
/*******************************************************************************
*
* Function gatt_process_error_rsp
*
* Description This function is called to handle the error response
*
*
* Returns void
*
******************************************************************************/
void gatt_process_error_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
UNUSED_ATTR uint8_t op_code,
UNUSED_ATTR uint16_t len, uint8_t* p_data) {
uint8_t opcode, *p = p_data;
uint8_t reason;
uint16_t handle;
tGATT_VALUE* p_attr = (tGATT_VALUE*)p_clcb->p_attr_buf;
log::verbose("");
if (len < 4) {
log::error("Error response too short");
// Specification does not clearly define what should happen if error
// response is too short. General rule in BT Spec 5.0 Vol 3, Part F 3.4.1.1
// is: "If an error code is received in the Error Response that is not
// understood by the client, for example an error code that was reserved for
// future use that is now being used in a future version of this
// specification, then the Error Response shall still be considered to state
// that the given request cannot be performed for an unknown reason."
opcode = handle = 0;
reason = static_cast<tGATT_STATUS>(0x7f);
} else {
STREAM_TO_UINT8(opcode, p);
STREAM_TO_UINT16(handle, p);
STREAM_TO_UINT8(reason, p);
}
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) {
gatt_proc_disc_error_rsp(tcb, p_clcb, opcode, handle,
static_cast<tGATT_STATUS>(reason));
} else {
if ((p_clcb->operation == GATTC_OPTYPE_WRITE) &&
(p_clcb->op_subtype == GATT_WRITE) &&
(opcode == GATT_REQ_PREPARE_WRITE) && (p_attr) &&
(handle == p_attr->handle)) {
p_clcb->status = static_cast<tGATT_STATUS>(reason);
gatt_send_queue_write_cancel(tcb, p_clcb, GATT_PREP_WRITE_CANCEL);
} else if ((p_clcb->operation == GATTC_OPTYPE_READ) &&
((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) ||
(p_clcb->op_subtype == GATT_READ_BY_HANDLE)) &&
(opcode == GATT_REQ_READ_BLOB) &&
p_clcb->first_read_blob_after_read &&
(reason == GATT_NOT_LONG)) {
gatt_end_operation(p_clcb, GATT_SUCCESS, (void*)p_clcb->p_attr_buf);
} else
gatt_end_operation(p_clcb, static_cast<tGATT_STATUS>(reason), NULL);
}
}
/*******************************************************************************
*
* Function gatt_process_prep_write_rsp
*
* Description This function is called to handle the read response
*
*
* Returns void
*
******************************************************************************/
void gatt_process_prep_write_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
uint8_t op_code, uint16_t len,
uint8_t* p_data) {
uint8_t* p = p_data;
tGATT_VALUE value = {
.conn_id = p_clcb->conn_id, .auth_req = GATT_AUTH_REQ_NONE,
};
log::verbose("value resp op_code = {} len = {}", gatt_dbg_op_name(op_code),
len);
if (len < GATT_PREP_WRITE_RSP_MIN_LEN ||
len > GATT_PREP_WRITE_RSP_MIN_LEN + sizeof(value.value)) {
log::error("illegal prepare write response length, discard");
gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value);
return;
}
STREAM_TO_UINT16(value.handle, p);
STREAM_TO_UINT16(value.offset, p);
value.len = len - GATT_PREP_WRITE_RSP_MIN_LEN;
memcpy(value.value, p, value.len);
bool subtype_is_write_prepare = (p_clcb->op_subtype == GATT_WRITE_PREPARE);
if (!gatt_check_write_long_terminate(tcb, p_clcb, &value)) {
gatt_send_prepare_write(tcb, p_clcb);
return;
}
// We now know that we have not terminated, or else we would have returned
// early. We free the buffer only if the subtype is not equal to
// GATT_WRITE_PREPARE, so checking here is adequate to prevent UAF.
if (subtype_is_write_prepare) {
/* application should verify handle offset
and value are matched or not */
gatt_end_operation(p_clcb, p_clcb->status, &value);
}
}
/*******************************************************************************
*
* Function gatt_process_notification
*
* Description Handle the handle value indication/notification.
*
* Returns void
*
******************************************************************************/
void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
uint16_t len, uint8_t* p_data) {
tGATT_VALUE value = {};
tGATT_REG* p_reg;
uint16_t conn_id;
tGATT_STATUS encrypt_status = {};
uint8_t* p = p_data;
uint8_t i;
tGATTC_OPTYPE event = (op_code == GATT_HANDLE_VALUE_IND)
? GATTC_OPTYPE_INDICATION
: GATTC_OPTYPE_NOTIFICATION;
log::verbose("");
// Ensure our packet has enough data (2 bytes)
if (len < GATT_NOTIFICATION_MIN_LEN) {
log::error("illegal notification PDU length, discard");
return;
}
// Get 2 byte handle
STREAM_TO_UINT16(value.handle, p);
// Fail early if the GATT handle is not valid
if (!GATT_HANDLE_IS_VALID(value.handle)) {
/* illegal handle, send ack now */
if (op_code == GATT_HANDLE_VALUE_IND)
attp_send_cl_confirmation_msg(tcb, cid);
return;
}
// Calculate value length based on opcode
if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) {
// Ensure our packet has enough data; MIN + 2 more bytes for len value
if (len < GATT_NOTIFICATION_MIN_LEN + 2) {
log::error("illegal notification PDU length, discard");
return;
}
// Allow multi value opcode to set value len from the packet
STREAM_TO_UINT16(value.len, p);
if (value.len > len - 4) {
log::error("value.len ({}) greater than length ({})", value.len,
(len - 4));
return;
}
} else {
// For single value, just use the passed in len minus opcode length (2)
value.len = len - 2;
}
// Verify the new calculated length
if (value.len > GATT_MAX_ATTR_LEN) {
log::error("value.len larger than GATT_MAX_ATTR_LEN, discard");
return;
}
// Handle indications differently
if (event == GATTC_OPTYPE_INDICATION) {
if (tcb.ind_count) {
/* this is an error case that receiving an indication but we
still has an indication not being acked yet.
For now, just log the error reset the counter.
Later we need to disconnect the link unconditionally.
*/
log::error("rcv Ind. but ind_count={} (will reset ind_count)",
tcb.ind_count);
}
// Zero out the ind_count
tcb.ind_count = 0;
// Notify all registered clients with the handle value
// notification/indication
// Note: need to do the indication count and start timer first then do
// callback
for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) tcb.ind_count++;
}
/* start a timer for app confirmation */
if (tcb.ind_count > 0) {
gatt_start_ind_ack_timer(tcb, cid);
} else { /* no app to indicate, or invalid handle */
attp_send_cl_confirmation_msg(tcb, cid);
}
}
encrypt_status = gatt_get_link_encrypt_status(tcb);
STREAM_TO_ARRAY(value.value, p, value.len);
tGATT_CL_COMPLETE gatt_cl_complete;
gatt_cl_complete.att_value = value;
gatt_cl_complete.cid = cid;
for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) {
conn_id = GATT_CREATE_CONN_ID(tcb.tcb_idx, p_reg->gatt_if);
(*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status,
&gatt_cl_complete);
}
}
// If this is single value, then nothing is left to do
if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) return;
// Need a signed type to check if the value is below 0
// as uint16_t doesn't have negatives so the negatives register as a number
// thus anything less than zero won't trigger the conditional and it is not
// always 0
// when done looping as value.len is arbitrary.
int16_t rem_len = (int16_t)len - (4 /* octets */ + value.len);
// Already streamed the first value and sent it, lets send the rest
while (rem_len > 4 /* octets */) {
// 2
STREAM_TO_UINT16(value.handle, p);
// + 2 = 4
STREAM_TO_UINT16(value.len, p);
// Accounting
rem_len -= 4;
// Make sure we don't read past the remaining data even if the length says
// we can Also need to watch comparing the int16_t with the uint16_t
value.len = std::min((uint16_t)rem_len, value.len);
if (value.len > sizeof(value.value)) {
log::error("Unexpected value.len (>GATT_MAX_ATTR_LEN), stop");
return ;
}
STREAM_TO_ARRAY(value.value, p, value.len);
// Accounting
rem_len -= value.len;
gatt_cl_complete.att_value = value;
gatt_cl_complete.cid = cid;
for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) {
conn_id = GATT_CREATE_CONN_ID(tcb.tcb_idx, p_reg->gatt_if);
(*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status,
&gatt_cl_complete);
}
}
}
}
/*******************************************************************************
*
* Function gatt_process_read_by_type_rsp
*
* Description This function is called to handle the read by type response.
* read by type can be used for discovery, or read by type or
* read characteristic value.
*
* Returns void
*
******************************************************************************/
void gatt_process_read_by_type_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
uint8_t op_code, uint16_t len,
uint8_t* p_data) {
tGATT_DISC_RES result;
tGATT_DISC_VALUE record_value;
uint8_t *p = p_data, value_len, handle_len = 2;
uint16_t handle = 0;
/* discovery procedure and no callback function registered */
if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) &&
(p_clcb->operation == GATTC_OPTYPE_DISCOVERY))
return;
if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) {
log::error("Illegal ReadByType/ReadByGroupType Response length, discard");
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
return;
}
STREAM_TO_UINT8(value_len, p);
uint16_t payload_size = gatt_tcb_get_payload_size(tcb, p_clcb->cid);
if ((value_len > (payload_size - 2)) || (value_len > (len - 1))) {
/* this is an error case that server's response containing a value length
which is larger than MTU-2
or value_len > message total length -1 */
log::error(
"Discard response op_code={} vale_len={} > (MTU-2={} or msg_len-1={})",
op_code, value_len, (payload_size - 2), (len - 1));
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
return;
}
if (op_code == GATT_RSP_READ_BY_GRP_TYPE) handle_len = 4;
value_len -= handle_len; /* subtract the handle pairs bytes */
len -= 1;
while (len >= (handle_len + value_len)) {
STREAM_TO_UINT16(handle, p);
if (!GATT_HANDLE_IS_VALID(handle)) {
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
return;
}
memset(&result, 0, sizeof(tGATT_DISC_RES));
memset(&record_value, 0, sizeof(tGATT_DISC_VALUE));
result.handle = handle;
result.type =
bluetooth::Uuid::From16Bit(disc_type_to_uuid[p_clcb->op_subtype]);
/* discover all services */
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
p_clcb->op_subtype == GATT_DISC_SRVC_ALL &&
op_code == GATT_RSP_READ_BY_GRP_TYPE) {
STREAM_TO_UINT16(handle, p);
if (!GATT_HANDLE_IS_VALID(handle)) {
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
return;
} else {
record_value.group_value.e_handle = handle;
if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type,
value_len, &p)) {
log::error("discover all service response parsing failure");
break;
}
}
}
/* discover included service */
else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
p_clcb->op_subtype == GATT_DISC_INC_SRVC) {
if (value_len < 4) {
log::error("Illegal Response length, must be at least 4.");
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
return;
}
STREAM_TO_UINT16(record_value.incl_service.s_handle, p);
STREAM_TO_UINT16(record_value.incl_service.e_handle, p);
if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) ||
!GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle)) {
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
return;
}
if (value_len == 6) {
uint16_t tmp;
STREAM_TO_UINT16(tmp, p);
record_value.incl_service.service_type =
bluetooth::Uuid::From16Bit(tmp);
} else if (value_len == 4) {
p_clcb->s_handle = record_value.incl_service.s_handle;
p_clcb->read_uuid128.wait_for_read_rsp = true;
p_clcb->read_uuid128.next_disc_start_hdl = handle + 1;
memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result));
memcpy(&p_clcb->read_uuid128.result.value, &record_value,
sizeof(result.value));
p_clcb->op_subtype |= 0x90;
gatt_act_read(p_clcb, 0);
return;
} else {
log::error("INCL_SRVC failed with invalid data value_len={}",
value_len);
gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void*)p);
return;
}
}
/* read by type */
else if (p_clcb->operation == GATTC_OPTYPE_READ &&
p_clcb->op_subtype == GATT_READ_BY_TYPE) {
p_clcb->counter = len - 2;
p_clcb->s_handle = handle;
if (p_clcb->counter == (payload_size - 4)) {
p_clcb->op_subtype = GATT_READ_BY_HANDLE;
if (!p_clcb->p_attr_buf)
p_clcb->p_attr_buf = (uint8_t*)osi_malloc(GATT_MAX_ATTR_LEN);
if (p_clcb->counter <= GATT_MAX_ATTR_LEN) {
memcpy(p_clcb->p_attr_buf, p, p_clcb->counter);
gatt_act_read(p_clcb, p_clcb->counter);
} else {
gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void*)p);
}
} else {
gatt_end_operation(p_clcb, GATT_SUCCESS, (void*)p);
}
return;
} else /* discover characteristic */
{
if (value_len < 3) {
log::error("Illegal Response length, must be at least 3.");
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
return;
}
STREAM_TO_UINT8(record_value.dclr_value.char_prop, p);
STREAM_TO_UINT16(record_value.dclr_value.val_handle, p);
if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) {
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
return;
}
if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid,
(uint16_t)(value_len - 3), &p)) {
gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
/* invalid format, and skip the result */
return;
}
/* UUID not matching */
if (!p_clcb->uuid.IsEmpty() &&
!record_value.dclr_value.char_uuid.IsEmpty() &&
record_value.dclr_value.char_uuid != p_clcb->uuid) {
len -= (value_len + 2);
continue; /* skip the result, and look for next one */
}
if (p_clcb->operation == GATTC_OPTYPE_READ)
/* UUID match for read characteristic value */
{
/* only read the first matching UUID characteristic value, and
discard the rest results */
p_clcb->s_handle = record_value.dclr_value.val_handle;
p_clcb->op_subtype |= 0x80;
gatt_act_read(p_clcb, 0);
return;
}
}
len -= (value_len + handle_len);
/* result is (handle, 16bits UUID) pairs */
memcpy(&result.value, &record_value, sizeof(result.value));
/* send callback if is discover procedure */
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
p_clcb->p_reg->app_cb.p_disc_res_cb)
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(
p_clcb->conn_id, static_cast<tGATT_DISC_TYPE>(p_clcb->op_subtype),
&result);
}
p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1);
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) {
/* initiate another request */
gatt_act_discovery(p_clcb);
} else /* read characteristic value */
{
gatt_act_read(p_clcb, 0);
}
}
/*******************************************************************************
*
* Function gatt_process_read_rsp
*
* Description This function is called to handle the read BLOB response
*
*
* Returns void
*
******************************************************************************/
void gatt_process_read_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
UNUSED_ATTR uint8_t op_code, uint16_t len,
uint8_t* p_data) {
uint16_t offset = p_clcb->counter;
uint8_t* p = p_data;
uint16_t payload_size = gatt_tcb_get_payload_size(tcb, p_clcb->cid);
if (p_clcb->operation == GATTC_OPTYPE_READ) {
if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) {
p_clcb->counter = len;
gatt_end_operation(p_clcb, GATT_SUCCESS, (void*)p);
} else {
/* allocate GKI buffer holding up long attribute value */
if (!p_clcb->p_attr_buf)
p_clcb->p_attr_buf = (uint8_t*)osi_malloc(GATT_MAX_ATTR_LEN);
/* copy attribute value into cb buffer */
if (offset < GATT_MAX_ATTR_LEN) {
if ((len + offset) > GATT_MAX_ATTR_LEN)
len = GATT_MAX_ATTR_LEN - offset;
p_clcb->counter += len;
memcpy(p_clcb->p_attr_buf + offset, p, len);
/* full packet for read or read blob rsp */
bool packet_is_full;
if (payload_size == p_clcb->read_req_current_mtu) {
packet_is_full = (len == (payload_size - 1));
} else {
packet_is_full = (len == (p_clcb->read_req_current_mtu - 1) ||
len == (payload_size - 1));
p_clcb->read_req_current_mtu = payload_size;
}
/* send next request if needed */
if (packet_is_full && (len + offset < GATT_MAX_ATTR_LEN)) {
log::verbose(
"full pkt issue read blob for remaining bytes old offset={} "
"len={} new offset={}",
offset, len, p_clcb->counter);
gatt_act_read(p_clcb, p_clcb->counter);
} else /* end of request, send callback */
{
gatt_end_operation(p_clcb, GATT_SUCCESS, (void*)p_clcb->p_attr_buf);
}
} else /* exception, should not happen */
{
log::error("attr offset = {} p_attr_buf = {}", offset,
fmt::ptr(p_clcb->p_attr_buf));
gatt_end_operation(p_clcb, GATT_NO_RESOURCES,
(void*)p_clcb->p_attr_buf);
}
}
} else {
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
p_clcb->op_subtype == GATT_DISC_INC_SRVC &&
p_clcb->read_uuid128.wait_for_read_rsp) {
p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl;
p_clcb->read_uuid128.wait_for_read_rsp = false;
if (len == Uuid::kNumBytes128) {
p_clcb->read_uuid128.result.value.incl_service.service_type =
bluetooth::Uuid::From128BitLE(p);
if (p_clcb->p_reg->app_cb.p_disc_res_cb)
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(
p_clcb->conn_id, static_cast<tGATT_DISC_TYPE>(p_clcb->op_subtype),
&p_clcb->read_uuid128.result);
gatt_act_discovery(p_clcb);
} else {
gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void*)p);
}
}
}
}
/*******************************************************************************
*
* Function gatt_process_handle_rsp
*
* Description This function is called to handle the write response
*
*
* Returns void
*
******************************************************************************/
void gatt_process_handle_rsp(tGATT_CLCB* p_clcb) {
gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
}
/*******************************************************************************
*
* Function gatt_process_mtu_rsp
*
* Description Process the configure MTU response.
*
*
* Returns void
*
******************************************************************************/
void gatt_process_mtu_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint16_t len,
uint8_t* p_data) {
uint16_t mtu;
tGATT_STATUS status = GATT_SUCCESS;
if (len < GATT_MTU_RSP_MIN_LEN) {
log::error("invalid MTU response PDU received, discard.");
status = GATT_INVALID_PDU;
} else {
STREAM_TO_UINT16(mtu, p_data);
log::info("Local pending MTU {}, Remote ({}) MTU {}",
tcb.pending_user_mtu_exchange_value,
ADDRESS_TO_LOGGABLE_STR(tcb.peer_bda), mtu);
/* Aim for default as we did in the request */
if (mtu < GATT_DEF_BLE_MTU_SIZE) {
tcb.payload_size = GATT_DEF_BLE_MTU_SIZE;
} else {
tcb.payload_size = std::min(mtu, (uint16_t)(gatt_get_local_mtu()));
}
bluetooth::shim::arbiter::GetArbiter().OnIncomingMtuResp(tcb.tcb_idx,
tcb.payload_size);
/* This is just to track the biggest MTU requested by the user.
* This value will be used in the BTM_SetBleDataLength */
if (tcb.pending_user_mtu_exchange_value > tcb.max_user_mtu) {
tcb.max_user_mtu =
std::min(tcb.pending_user_mtu_exchange_value, tcb.payload_size);
} else if (tcb.pending_user_mtu_exchange_value == 0) {
tcb.max_user_mtu = tcb.payload_size;
}
tcb.pending_user_mtu_exchange_value = 0;
log::info("MTU Exchange resulted in: {}", tcb.payload_size);
BTM_SetBleDataLength(tcb.peer_bda, tcb.max_user_mtu + L2CAP_PKT_OVERHEAD);
}
gatt_end_operation(p_clcb, status, NULL);
}
/*******************************************************************************
*
* Function gatt_cmd_to_rsp_code
*
* Description Convert an ATT command op code into the corresponding
* response code assume no error occurs.
*
* Returns response code.
*
******************************************************************************/
uint8_t gatt_cmd_to_rsp_code(uint8_t cmd_code) {
uint8_t rsp_code = 0;
if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) {
rsp_code = cmd_code + 1;
}
return rsp_code;
}
/** Find next command in queue and sent to server */
bool gatt_cl_send_next_cmd_inq(tGATT_TCB& tcb) {
std::deque<tGATT_CMD_Q>* cl_cmd_q = nullptr;
while (
gatt_is_outstanding_msg_in_att_send_queue(tcb) ||
EattExtension::GetInstance()->IsOutstandingMsgInSendQueue(tcb.peer_bda)) {
if (gatt_is_outstanding_msg_in_att_send_queue(tcb)) {
cl_cmd_q = &tcb.cl_cmd_q;
} else {
EattChannel* channel =
EattExtension::GetInstance()->GetChannelWithQueuedDataToSend(
tcb.peer_bda);
cl_cmd_q = &channel->cl_cmd_q_;
}
tGATT_CMD_Q& cmd = cl_cmd_q->front();
if (!cmd.to_send || cmd.p_cmd == NULL) {
return false;
}
tGATT_STATUS att_ret;
att_ret = attp_send_msg_to_l2cap(tcb, cmd.cid, cmd.p_cmd);
if (att_ret != GATT_SUCCESS && att_ret != GATT_CONGESTED) {
log::error("L2CAP sent error");
cl_cmd_q->pop_front();
continue;
}
cmd.to_send = false;
cmd.p_cmd = NULL;
if (cmd.op_code == GATT_CMD_WRITE || cmd.op_code == GATT_SIGN_CMD_WRITE) {
/* dequeue the request if is write command or sign write */
uint8_t rsp_code;
tGATT_CLCB* p_clcb = gatt_cmd_dequeue(tcb, cmd.cid, &rsp_code);
/* send command complete callback here */
gatt_end_operation(p_clcb, att_ret, NULL);
/* if no ack needed, keep sending */
if (att_ret == GATT_SUCCESS) continue;
return true;
}
gatt_start_rsp_timer(cmd.p_clcb);
return true;
}
return false;
}
/** This function is called to handle the server response to client */
void gatt_client_handle_server_rsp(tGATT_TCB& tcb, uint16_t cid,
uint8_t op_code, uint16_t len,
uint8_t* p_data) {
log::verbose("opcode: {} cid{}", loghex(op_code), cid);
uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid);
if (op_code == GATT_HANDLE_VALUE_IND || op_code == GATT_HANDLE_VALUE_NOTIF ||
op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) {
if (len >= payload_size) {
log::error("invalid indicate pkt size: {}, PDU size: {}", len + 1,
payload_size);
return;
}
gatt_process_notification(tcb, cid, op_code, len, p_data);
return;
}
uint8_t cmd_code = 0;
tGATT_CLCB* p_clcb = gatt_cmd_dequeue(tcb, cid, &cmd_code);
if (!p_clcb) {
log::warn("ATT - clcb already not in use, ignoring response");
gatt_cl_send_next_cmd_inq(tcb);
return;
}
uint8_t rsp_code = gatt_cmd_to_rsp_code(cmd_code);
if (!p_clcb) {
log::warn("ATT - clcb already not in use, ignoring response");
gatt_cl_send_next_cmd_inq(tcb);
return;
}
if (rsp_code != op_code && op_code != GATT_RSP_ERROR) {
log::warn(
"ATT - Ignore wrong response. Receives ({:02x}) Request({:02x}) "
"Ignored",
op_code, rsp_code);
return;
}
gatt_stop_rsp_timer(p_clcb);
p_clcb->retry_count = 0;
/* the size of the message may not be bigger than the local max PDU size*/
/* The message has to be smaller than the agreed MTU, len does not count
* op_code */
if (len >= payload_size) {
log::error("invalid response pkt size: {}, PDU size: {}", len + 1,
payload_size);
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
} else {
switch (op_code) {
case GATT_RSP_ERROR:
gatt_process_error_rsp(tcb, p_clcb, op_code, len, p_data);
break;
case GATT_RSP_MTU: /* 2 bytes mtu */
gatt_process_mtu_rsp(tcb, p_clcb, len, p_data);
break;
case GATT_RSP_FIND_INFO:
gatt_process_read_info_rsp(tcb, p_clcb, op_code, len, p_data);
break;
case GATT_RSP_READ_BY_TYPE:
case GATT_RSP_READ_BY_GRP_TYPE:
gatt_process_read_by_type_rsp(tcb, p_clcb, op_code, len, p_data);
break;
case GATT_RSP_READ:
case GATT_RSP_READ_BLOB:
case GATT_RSP_READ_MULTI:
case GATT_RSP_READ_MULTI_VAR:
gatt_process_read_rsp(tcb, p_clcb, op_code, len, p_data);
break;
case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */
gatt_process_find_type_value_rsp(tcb, p_clcb, len, p_data);
break;
case GATT_RSP_WRITE:
gatt_process_handle_rsp(p_clcb);
break;
case GATT_RSP_PREPARE_WRITE:
gatt_process_prep_write_rsp(tcb, p_clcb, op_code, len, p_data);
break;
case GATT_RSP_EXEC_WRITE:
gatt_end_operation(p_clcb, p_clcb->status, NULL);
break;
default:
log::error("Unknown opcode = {:x}", op_code);
break;
}
}
gatt_cl_send_next_cmd_inq(tcb);
}