You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1280 lines
43 KiB
1280 lines
43 KiB
/******************************************************************************
|
|
*
|
|
* 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 <string.h>
|
|
|
|
#include "bt_common.h"
|
|
#include "bt_target.h"
|
|
#include "bt_utils.h"
|
|
#include "gatt_int.h"
|
|
#include "l2c_api.h"
|
|
#include "osi/include/log.h"
|
|
#include "osi/include/osi.h"
|
|
#include "stack/eatt/eatt.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
|
|
|
|
using base::StringPrintf;
|
|
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;
|
|
|
|
VLOG(1) << __func__ << ": 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_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_tx(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) << StringPrintf(
|
|
"gatt_act_write() failed op_code=0x%x rt=%d", 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) << StringPrintf(
|
|
"gatt_act_write() failed op_code=0x%x rt=%d", 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;
|
|
|
|
VLOG(1) << __func__;
|
|
|
|
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;
|
|
|
|
VLOG(1) << __func__;
|
|
/* 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;
|
|
|
|
VLOG(1) << __func__ << StringPrintf(" type=0x%x", type);
|
|
uint16_t to_send = p_attr->len - p_attr->offset;
|
|
|
|
uint16_t payload_size = gatt_tcb_get_payload_size_tx(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;
|
|
}
|
|
|
|
VLOG(1) << StringPrintf("offset =0x%x len=%d", 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;
|
|
|
|
VLOG(1) << __func__;
|
|
/* 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;
|
|
|
|
VLOG(1) << __func__
|
|
<< StringPrintf("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;
|
|
VLOG(1) << "Discovery completed";
|
|
}
|
|
break;
|
|
default:
|
|
LOG(ERROR) << StringPrintf("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;
|
|
|
|
VLOG(1) << __func__;
|
|
|
|
if (len < 4) {
|
|
android_errorWriteLog(0x534e4554, "79591688");
|
|
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,
|
|
};
|
|
|
|
VLOG(1) << StringPrintf("value resp op_code = %s len = %d",
|
|
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;
|
|
|
|
VLOG(1) << __func__;
|
|
|
|
// 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 (" << value.len << ") greater than length ("
|
|
<< (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) << __func__ << " rcv Ind. but ind_count=" << tcb.ind_count
|
|
<< " (will reset 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_rx(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) << __func__
|
|
<< StringPrintf(
|
|
": Discard response op_code=%d "
|
|
"vale_len=%d > (MTU-2=%d or msg_len-1=%d)",
|
|
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; /* substract 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) {
|
|
android_errorWriteLog(0x534e4554, "158833854");
|
|
LOG(ERROR) << __func__ << " 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) << __func__
|
|
<< ": 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 characterisitic */
|
|
{
|
|
if (value_len < 3) {
|
|
android_errorWriteLog(0x534e4554, "158778659");
|
|
LOG(ERROR) << __func__ << " 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_rx(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 attrobute 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)) {
|
|
VLOG(1) << StringPrintf(
|
|
"full pkt issue read blob for remianing bytes old offset=%d "
|
|
"len=%d new offset=%d",
|
|
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 = " << +offset
|
|
<< " p_attr_buf = " << 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);
|
|
|
|
if (mtu < tcb.payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE)
|
|
tcb.payload_size = mtu;
|
|
}
|
|
|
|
BTM_SetBleDataLength(tcb.peer_bda, tcb.payload_size);
|
|
|
|
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::queue<tGATT_CMD_Q>* cl_cmd_q;
|
|
|
|
while (!tcb.cl_cmd_q.empty() ||
|
|
EattExtension::GetInstance()->IsOutstandingMsgInSendQueue(tcb.peer_bda)) {
|
|
if (!tcb.cl_cmd_q.empty()) {
|
|
cl_cmd_q = &tcb.cl_cmd_q;
|
|
} else {
|
|
EattChannel* channel =
|
|
EattExtension::GetInstance()->GetChannelWithQueuedData(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) << __func__ << ": L2CAP sent error";
|
|
cl_cmd_q->pop();
|
|
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) {
|
|
VLOG(1) << __func__ << " opcode: " << loghex(op_code);
|
|
|
|
uint16_t payload_size = gatt_tcb_get_payload_size_rx(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) << StringPrintf(
|
|
"%s: invalid indicate pkt size: %d, PDU size: %d", __func__, 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);
|
|
uint8_t rsp_code = gatt_cmd_to_rsp_code(cmd_code);
|
|
if (!p_clcb || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) {
|
|
LOG(WARNING) << StringPrintf(
|
|
"ATT - Ignore wrong response. Receives (%02x) Request(%02x) Ignored",
|
|
op_code, rsp_code);
|
|
return;
|
|
}
|
|
|
|
if (!p_clcb->in_use) {
|
|
LOG(WARNING) << "ATT - clcb already not in use, ignoring response";
|
|
gatt_cl_send_next_cmd_inq(tcb);
|
|
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) << StringPrintf(
|
|
"%s: invalid response pkt size: %d, PDU size: %d", __func__, 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) << __func__ << ": Unknown opcode = " << std::hex << op_code;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gatt_cl_send_next_cmd_inq(tcb);
|
|
}
|