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.
1451 lines
50 KiB
1451 lines
50 KiB
/******************************************************************************
|
|
*
|
|
* Copyright 2003-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 GATT client action functions for the state
|
|
* machine.
|
|
*
|
|
******************************************************************************/
|
|
|
|
#define LOG_TAG "bt_bta_gattc"
|
|
|
|
#include <base/bind.h>
|
|
#include <base/strings/stringprintf.h>
|
|
|
|
#include "bt_target.h" // Must be first to define build configuration
|
|
|
|
#include "bta/gatt/bta_gattc_int.h"
|
|
#include "bta/hh/bta_hh_int.h"
|
|
#include "btif/include/btif_debug_conn.h"
|
|
#include "device/include/controller.h"
|
|
#include "osi/include/log.h"
|
|
#include "osi/include/osi.h" // UNUSED_ATTR
|
|
#include "stack/include/btm_ble_api_types.h"
|
|
#include "stack/include/btu.h" // do_in_main_thread
|
|
#include "stack/include/l2c_api.h"
|
|
#include "types/bluetooth/uuid.h"
|
|
#include "types/raw_address.h"
|
|
|
|
using base::StringPrintf;
|
|
using bluetooth::Uuid;
|
|
|
|
/*****************************************************************************
|
|
* Constants
|
|
****************************************************************************/
|
|
static void bta_gattc_conn_cback(tGATT_IF gattc_if, const RawAddress& bda,
|
|
uint16_t conn_id, bool connected,
|
|
tGATT_DISCONN_REASON reason,
|
|
tBT_TRANSPORT transport);
|
|
|
|
static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
|
|
tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE* p_data);
|
|
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
|
|
tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE* p_data);
|
|
|
|
static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB* p_clreg);
|
|
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, const RawAddress& bda);
|
|
static void bta_gattc_cong_cback(uint16_t conn_id, bool congested);
|
|
static void bta_gattc_phy_update_cback(tGATT_IF gatt_if, uint16_t conn_id,
|
|
uint8_t tx_phy, uint8_t rx_phy,
|
|
tGATT_STATUS status);
|
|
static void bta_gattc_conn_update_cback(tGATT_IF gatt_if, uint16_t conn_id,
|
|
uint16_t interval, uint16_t latency,
|
|
uint16_t timeout, tGATT_STATUS status);
|
|
|
|
static tGATT_CBACK bta_gattc_cl_cback = {
|
|
.p_conn_cb = bta_gattc_conn_cback,
|
|
.p_cmpl_cb = bta_gattc_cmpl_cback,
|
|
.p_disc_res_cb = bta_gattc_disc_res_cback,
|
|
.p_disc_cmpl_cb = bta_gattc_disc_cmpl_cback,
|
|
.p_req_cb = nullptr,
|
|
.p_enc_cmpl_cb = bta_gattc_enc_cmpl_cback,
|
|
.p_congestion_cb = bta_gattc_cong_cback,
|
|
.p_phy_update_cb = bta_gattc_phy_update_cback,
|
|
.p_conn_update_cb = bta_gattc_conn_update_cback};
|
|
|
|
/* opcode(tGATTC_OPTYPE) order has to be comply with internal event order */
|
|
static uint16_t bta_gattc_opcode_to_int_evt[] = {
|
|
/* Skip: GATTC_OPTYPE_NONE */
|
|
/* Skip: GATTC_OPTYPE_DISCOVERY */
|
|
BTA_GATTC_API_READ_EVT, /* GATTC_OPTYPE_READ */
|
|
BTA_GATTC_API_WRITE_EVT, /* GATTC_OPTYPE_WRITE */
|
|
BTA_GATTC_API_EXEC_EVT, /* GATTC_OPTYPE_EXE_WRITE */
|
|
BTA_GATTC_API_CFG_MTU_EVT /* GATTC_OPTYPE_CONFIG */
|
|
};
|
|
|
|
static const char* bta_gattc_op_code_name[] = {
|
|
"Unknown", /* GATTC_OPTYPE_NONE */
|
|
"Discovery", /* GATTC_OPTYPE_DISCOVERY */
|
|
"Read", /* GATTC_OPTYPE_READ */
|
|
"Write", /* GATTC_OPTYPE_WRITE */
|
|
"Exec", /* GATTC_OPTYPE_EXE_WRITE */
|
|
"Config", /* GATTC_OPTYPE_CONFIG */
|
|
"Notification", /* GATTC_OPTYPE_NOTIFICATION */
|
|
"Indication" /* GATTC_OPTYPE_INDICATION */
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Action Functions
|
|
****************************************************************************/
|
|
|
|
void bta_gattc_reset_discover_st(tBTA_GATTC_SERV* p_srcb, tGATT_STATUS status);
|
|
|
|
/** Enables GATTC module */
|
|
static void bta_gattc_enable() {
|
|
VLOG(1) << __func__;
|
|
|
|
if (bta_gattc_cb.state == BTA_GATTC_STATE_DISABLED) {
|
|
/* initialize control block */
|
|
bta_gattc_cb = tBTA_GATTC_CB();
|
|
bta_gattc_cb.state = BTA_GATTC_STATE_ENABLED;
|
|
} else {
|
|
VLOG(1) << "GATTC is already enabled";
|
|
}
|
|
}
|
|
|
|
/** Disable GATTC module by cleaning up all active connections and deregister
|
|
* all application */
|
|
void bta_gattc_disable() {
|
|
uint8_t i;
|
|
|
|
VLOG(1) << __func__;
|
|
|
|
if (bta_gattc_cb.state != BTA_GATTC_STATE_ENABLED) {
|
|
LOG(ERROR) << "not enabled, or disabled in progress";
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < BTA_GATTC_CL_MAX; i++) {
|
|
if (!bta_gattc_cb.cl_rcb[i].in_use) continue;
|
|
|
|
bta_gattc_cb.state = BTA_GATTC_STATE_DISABLING;
|
|
/* don't deregister HH GATT IF */
|
|
/* HH GATT IF will be deregistered by bta_hh_le_deregister when disable HH */
|
|
if (!bta_hh_le_is_hh_gatt_if(bta_gattc_cb.cl_rcb[i].client_if)) {
|
|
bta_gattc_deregister(&bta_gattc_cb.cl_rcb[i]);
|
|
}
|
|
}
|
|
|
|
/* no registered apps, indicate disable completed */
|
|
if (bta_gattc_cb.state != BTA_GATTC_STATE_DISABLING) {
|
|
bta_gattc_cb = tBTA_GATTC_CB();
|
|
bta_gattc_cb.state = BTA_GATTC_STATE_DISABLED;
|
|
}
|
|
}
|
|
|
|
/** start an application interface */
|
|
void bta_gattc_start_if(uint8_t client_if) {
|
|
if (!bta_gattc_cl_get_regcb(client_if)) {
|
|
LOG(ERROR) << "Unable to start app.: Unknown client_if=" << +client_if;
|
|
return;
|
|
}
|
|
|
|
GATT_StartIf(client_if);
|
|
}
|
|
|
|
/** Register a GATT client application with BTA */
|
|
void bta_gattc_register(const Uuid& app_uuid, tBTA_GATTC_CBACK* p_cback,
|
|
BtaAppRegisterCallback cb, bool eatt_suppport) {
|
|
tGATT_STATUS status = GATT_NO_RESOURCES;
|
|
uint8_t client_if = 0;
|
|
VLOG(1) << __func__ << ": state:" << +bta_gattc_cb.state;
|
|
|
|
/* check if GATTC module is already enabled . Else enable */
|
|
if (bta_gattc_cb.state == BTA_GATTC_STATE_DISABLED) {
|
|
bta_gattc_enable();
|
|
}
|
|
/* todo need to check duplicate uuid */
|
|
for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i++) {
|
|
if (!bta_gattc_cb.cl_rcb[i].in_use) {
|
|
if ((bta_gattc_cb.cl_rcb[i].client_if = GATT_Register(
|
|
app_uuid, "GattClient", &bta_gattc_cl_cback, eatt_suppport)) ==
|
|
0) {
|
|
LOG(ERROR) << "Register with GATT stack failed.";
|
|
status = GATT_ERROR;
|
|
} else {
|
|
bta_gattc_cb.cl_rcb[i].in_use = true;
|
|
bta_gattc_cb.cl_rcb[i].p_cback = p_cback;
|
|
bta_gattc_cb.cl_rcb[i].app_uuid = app_uuid;
|
|
|
|
/* BTA use the same client interface as BTE GATT statck */
|
|
client_if = bta_gattc_cb.cl_rcb[i].client_if;
|
|
|
|
do_in_main_thread(FROM_HERE,
|
|
base::Bind(&bta_gattc_start_if, client_if));
|
|
|
|
status = GATT_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cb.is_null()) cb.Run(client_if, status);
|
|
}
|
|
|
|
/** De-Register a GATT client application with BTA */
|
|
void bta_gattc_deregister(tBTA_GATTC_RCB* p_clreg) {
|
|
if (!p_clreg) {
|
|
LOG(ERROR) << __func__ << ": Deregister Failed unknown client cif";
|
|
bta_hh_cleanup_disable(BTA_HH_OK);
|
|
return;
|
|
}
|
|
|
|
uint8_t accept_list_size = 0;
|
|
if (controller_get_interface()->supports_ble()) {
|
|
accept_list_size = controller_get_interface()->get_ble_acceptlist_size();
|
|
}
|
|
|
|
/* remove bg connection associated with this rcb */
|
|
for (uint8_t i = 0; i < accept_list_size; i++) {
|
|
if (!bta_gattc_cb.bg_track[i].in_use) continue;
|
|
|
|
if (bta_gattc_cb.bg_track[i].cif_mask & (1 << (p_clreg->client_if - 1))) {
|
|
bta_gattc_mark_bg_conn(p_clreg->client_if,
|
|
bta_gattc_cb.bg_track[i].remote_bda, false);
|
|
GATT_CancelConnect(p_clreg->client_if,
|
|
bta_gattc_cb.bg_track[i].remote_bda, false);
|
|
}
|
|
}
|
|
|
|
if (p_clreg->num_clcb == 0) {
|
|
bta_gattc_deregister_cmpl(p_clreg);
|
|
return;
|
|
}
|
|
|
|
/* close all CLCB related to this app */
|
|
for (uint8_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) {
|
|
if (!bta_gattc_cb.clcb[i].in_use || (bta_gattc_cb.clcb[i].p_rcb != p_clreg))
|
|
continue;
|
|
|
|
p_clreg->dereg_pending = true;
|
|
|
|
BT_HDR_RIGID buf;
|
|
buf.event = BTA_GATTC_API_CLOSE_EVT;
|
|
buf.layer_specific = bta_gattc_cb.clcb[i].bta_conn_id;
|
|
bta_gattc_close(&bta_gattc_cb.clcb[i], (tBTA_GATTC_DATA*)&buf);
|
|
}
|
|
}
|
|
|
|
/** process connect API request */
|
|
void bta_gattc_process_api_open(const tBTA_GATTC_DATA* p_msg) {
|
|
uint16_t event = ((BT_HDR_RIGID*)p_msg)->event;
|
|
|
|
tBTA_GATTC_RCB* p_clreg = bta_gattc_cl_get_regcb(p_msg->api_conn.client_if);
|
|
if (!p_clreg) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed, unknown client_if=" << +p_msg->api_conn.client_if;
|
|
return;
|
|
}
|
|
|
|
if (!p_msg->api_conn.is_direct) {
|
|
bta_gattc_init_bk_conn(&p_msg->api_conn, p_clreg);
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_alloc_clcb(
|
|
p_msg->api_conn.client_if, p_msg->api_conn.remote_bda,
|
|
p_msg->api_conn.transport);
|
|
if (p_clcb != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, event, p_msg);
|
|
} else {
|
|
LOG(ERROR) << "No resources to open a new connection.";
|
|
|
|
bta_gattc_send_open_cback(p_clreg, GATT_NO_RESOURCES,
|
|
p_msg->api_conn.remote_bda, GATT_INVALID_CONN_ID,
|
|
p_msg->api_conn.transport, 0);
|
|
}
|
|
}
|
|
|
|
/** process connect API request */
|
|
void bta_gattc_process_api_open_cancel(const tBTA_GATTC_DATA* p_msg) {
|
|
CHECK(p_msg != nullptr);
|
|
|
|
uint16_t event = ((BT_HDR_RIGID*)p_msg)->event;
|
|
|
|
if (!p_msg->api_cancel_conn.is_direct) {
|
|
LOG_DEBUG("Cancel GATT client background connection");
|
|
bta_gattc_cancel_bk_conn(&p_msg->api_cancel_conn);
|
|
return;
|
|
}
|
|
LOG_DEBUG("Cancel GATT client direct connection");
|
|
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_cif(
|
|
p_msg->api_cancel_conn.client_if, p_msg->api_cancel_conn.remote_bda,
|
|
BT_TRANSPORT_LE);
|
|
if (p_clcb != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, event, p_msg);
|
|
return;
|
|
}
|
|
|
|
LOG(ERROR) << "No such connection need to be cancelled";
|
|
|
|
tBTA_GATTC_RCB* p_clreg =
|
|
bta_gattc_cl_get_regcb(p_msg->api_cancel_conn.client_if);
|
|
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
tBTA_GATTC cb_data;
|
|
cb_data.status = GATT_ERROR;
|
|
(*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
/** process encryption complete message */
|
|
void bta_gattc_process_enc_cmpl(tGATT_IF client_if, const RawAddress& bda) {
|
|
tBTA_GATTC_RCB* p_clreg = bta_gattc_cl_get_regcb(client_if);
|
|
|
|
if (!p_clreg || !p_clreg->p_cback) return;
|
|
|
|
tBTA_GATTC cb_data;
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
|
|
cb_data.enc_cmpl.client_if = client_if;
|
|
cb_data.enc_cmpl.remote_bda = bda;
|
|
|
|
(*p_clreg->p_cback)(BTA_GATTC_ENC_CMPL_CB_EVT, &cb_data);
|
|
}
|
|
|
|
void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC cb_data;
|
|
|
|
cb_data.status = GATT_ERROR;
|
|
|
|
if (p_clcb && p_clcb->p_rcb && p_clcb->p_rcb->p_cback)
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
|
|
void bta_gattc_open_error(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
LOG(ERROR) << "Connection already opened. wrong state";
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb, GATT_SUCCESS, p_clcb->bda,
|
|
p_clcb->bta_conn_id, p_clcb->transport, 0);
|
|
}
|
|
|
|
void bta_gattc_open_fail(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
LOG(WARNING) << __func__ << ": Cannot establish Connection. conn_id="
|
|
<< loghex(p_clcb->bta_conn_id) << ". Return GATT_ERROR("
|
|
<< +GATT_ERROR << ")";
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb, GATT_ERROR, p_clcb->bda,
|
|
p_clcb->bta_conn_id, p_clcb->transport, 0);
|
|
/* open failure, remove clcb */
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
}
|
|
|
|
/** Process API connection function */
|
|
void bta_gattc_open(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC_DATA gattc_data;
|
|
|
|
/* open/hold a connection */
|
|
if (!GATT_Connect(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda, true,
|
|
p_data->api_conn.transport, p_data->api_conn.opportunistic,
|
|
p_data->api_conn.initiating_phys)) {
|
|
LOG(ERROR) << "Connection open failure";
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_OPEN_FAIL_EVT, p_data);
|
|
return;
|
|
}
|
|
|
|
/* a connected remote device */
|
|
if (GATT_GetConnIdIfConnected(
|
|
p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda,
|
|
&p_clcb->bta_conn_id, p_data->api_conn.transport)) {
|
|
gattc_data.int_conn.hdr.layer_specific = p_clcb->bta_conn_id;
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data);
|
|
}
|
|
/* else wait for the callback event */
|
|
}
|
|
|
|
/** Process API Open for a background connection */
|
|
void bta_gattc_init_bk_conn(const tBTA_GATTC_API_OPEN* p_data,
|
|
tBTA_GATTC_RCB* p_clreg) {
|
|
if (!bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, true)) {
|
|
bta_gattc_send_open_cback(p_clreg, GATT_NO_RESOURCES, p_data->remote_bda,
|
|
GATT_INVALID_CONN_ID, BT_TRANSPORT_LE, 0);
|
|
return;
|
|
}
|
|
|
|
/* always call open to hold a connection */
|
|
if (!GATT_Connect(p_data->client_if, p_data->remote_bda, false,
|
|
p_data->transport, false)) {
|
|
LOG(ERROR) << __func__
|
|
<< " unable to connect to remote bd_addr=" << p_data->remote_bda;
|
|
bta_gattc_send_open_cback(p_clreg, GATT_ERROR, p_data->remote_bda,
|
|
GATT_INVALID_CONN_ID, BT_TRANSPORT_LE, 0);
|
|
return;
|
|
}
|
|
|
|
uint16_t conn_id;
|
|
/* if is not a connected remote device */
|
|
if (!GATT_GetConnIdIfConnected(p_data->client_if, p_data->remote_bda,
|
|
&conn_id, p_data->transport)) {
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_alloc_clcb(
|
|
p_data->client_if, p_data->remote_bda, BT_TRANSPORT_LE);
|
|
if (!p_clcb) return;
|
|
|
|
tBTA_GATTC_DATA gattc_data;
|
|
gattc_data.hdr.layer_specific = p_clcb->bta_conn_id = conn_id;
|
|
|
|
/* open connection */
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT,
|
|
static_cast<const tBTA_GATTC_DATA*>(&gattc_data));
|
|
}
|
|
|
|
/** Process API Cancel Open for a background connection */
|
|
void bta_gattc_cancel_bk_conn(const tBTA_GATTC_API_CANCEL_OPEN* p_data) {
|
|
tBTA_GATTC_RCB* p_clreg;
|
|
tBTA_GATTC cb_data;
|
|
cb_data.status = GATT_ERROR;
|
|
|
|
/* remove the device from the bg connection mask */
|
|
if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, false)) {
|
|
if (GATT_CancelConnect(p_data->client_if, p_data->remote_bda, false)) {
|
|
cb_data.status = GATT_SUCCESS;
|
|
} else {
|
|
LOG(ERROR) << __func__ << ": failed";
|
|
}
|
|
}
|
|
p_clreg = bta_gattc_cl_get_regcb(p_data->client_if);
|
|
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
(*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC cb_data;
|
|
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
cb_data.status = GATT_SUCCESS;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
}
|
|
|
|
void bta_gattc_cancel_open(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC cb_data;
|
|
|
|
if (GATT_CancelConnect(p_clcb->p_rcb->client_if,
|
|
p_data->api_cancel_conn.remote_bda, true)) {
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, p_data);
|
|
} else {
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
cb_data.status = GATT_ERROR;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** receive connection callback from stack */
|
|
void bta_gattc_conn(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
tGATT_IF gatt_if;
|
|
VLOG(1) << __func__ << ": server cache state=" << +p_clcb->p_srcb->state;
|
|
|
|
if (p_data != NULL) {
|
|
VLOG(1) << __func__ << ": conn_id=" << loghex(p_data->hdr.layer_specific);
|
|
p_clcb->bta_conn_id = p_data->int_conn.hdr.layer_specific;
|
|
|
|
GATT_GetConnectionInfor(p_data->hdr.layer_specific, &gatt_if, p_clcb->bda,
|
|
&p_clcb->transport);
|
|
}
|
|
|
|
p_clcb->p_srcb->connected = true;
|
|
|
|
if (p_clcb->p_srcb->mtu == 0) p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE;
|
|
|
|
/* start database cache if needed */
|
|
if (p_clcb->p_srcb->gatt_database.IsEmpty() ||
|
|
p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
|
|
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
|
|
if (bta_gattc_cache_load(p_clcb->p_srcb)) {
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
|
|
} else {
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
|
|
/* cache load failure, start discovery */
|
|
bta_gattc_start_discover(p_clcb, NULL);
|
|
}
|
|
} else /* cache is building */
|
|
p_clcb->state = BTA_GATTC_DISCOVER_ST;
|
|
}
|
|
|
|
else {
|
|
/* a pending service handle change indication */
|
|
if (p_clcb->p_srcb->srvc_hdl_chg) {
|
|
p_clcb->p_srcb->srvc_hdl_chg = false;
|
|
|
|
/* set true to read database hash before service discovery */
|
|
if (bta_gattc_is_robust_caching_enabled()) {
|
|
p_clcb->p_srcb->srvc_hdl_db_hash = true;
|
|
}
|
|
|
|
/* start discovery */
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
|
|
if (p_clcb->p_rcb) {
|
|
/* there is no RM for GATT */
|
|
if (p_clcb->transport == BT_TRANSPORT_BR_EDR)
|
|
bta_sys_conn_open(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb, GATT_SUCCESS, p_clcb->bda,
|
|
p_clcb->bta_conn_id, p_clcb->transport,
|
|
p_clcb->p_srcb->mtu);
|
|
}
|
|
}
|
|
|
|
/** close a connection */
|
|
void bta_gattc_close_fail(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC cb_data;
|
|
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
cb_data.close.client_if = p_clcb->p_rcb->client_if;
|
|
cb_data.close.conn_id = p_data->hdr.layer_specific;
|
|
cb_data.close.remote_bda = p_clcb->bda;
|
|
cb_data.close.reason = BTA_GATT_CONN_NONE;
|
|
|
|
LOG(WARNING) << __func__ << ": conn_id=" << loghex(cb_data.close.conn_id)
|
|
<< ". Returns GATT_ERROR(" << +GATT_ERROR << ").";
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CLOSE_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
/** close a GATTC connection */
|
|
void bta_gattc_close(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
tBTA_GATTC_CBACK* p_cback = p_clcb->p_rcb->p_cback;
|
|
tBTA_GATTC_RCB* p_clreg = p_clcb->p_rcb;
|
|
tBTA_GATTC cb_data = {
|
|
.close =
|
|
{
|
|
.client_if = p_clcb->p_rcb->client_if,
|
|
.conn_id = p_clcb->bta_conn_id,
|
|
.reason = GATT_CONN_OK,
|
|
.remote_bda = p_clcb->bda,
|
|
.status = GATT_SUCCESS,
|
|
},
|
|
};
|
|
|
|
if (p_clcb->transport == BT_TRANSPORT_BR_EDR)
|
|
bta_sys_conn_close(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
|
|
if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) {
|
|
GATT_Disconnect(p_data->hdr.layer_specific);
|
|
LOG_DEBUG("Local close event client_if:%hu conn_id:%hu reason:%s",
|
|
cb_data.close.client_if, cb_data.close.conn_id,
|
|
gatt_disconnection_reason_text(
|
|
static_cast<tGATT_DISCONN_REASON>(cb_data.close.reason))
|
|
.c_str());
|
|
} else if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) {
|
|
cb_data.close.reason = p_data->int_conn.reason;
|
|
LOG_DEBUG("Peer close disconnect event client_if:%hu conn_id:%hu reason:%s",
|
|
cb_data.close.client_if, cb_data.close.conn_id,
|
|
gatt_disconnection_reason_text(
|
|
static_cast<tGATT_DISCONN_REASON>(cb_data.close.reason))
|
|
.c_str());
|
|
}
|
|
|
|
if (p_cback) (*p_cback)(BTA_GATTC_CLOSE_EVT, &cb_data);
|
|
|
|
if (p_clreg->num_clcb == 0 && p_clreg->dereg_pending) {
|
|
bta_gattc_deregister_cmpl(p_clreg);
|
|
}
|
|
}
|
|
|
|
/** when a SRCB finished discovery, tell all related clcb */
|
|
void bta_gattc_reset_discover_st(tBTA_GATTC_SERV* p_srcb, tGATT_STATUS status) {
|
|
for (uint8_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) {
|
|
if (bta_gattc_cb.clcb[i].p_srcb == p_srcb) {
|
|
bta_gattc_cb.clcb[i].status = status;
|
|
bta_gattc_sm_execute(&bta_gattc_cb.clcb[i], BTA_GATTC_DISCOVER_CMPL_EVT,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** close a GATTC connection while in discovery state */
|
|
void bta_gattc_disc_close(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_DATA* p_data) {
|
|
VLOG(1) << __func__
|
|
<< ": Discovery cancel conn_id=" << loghex(p_clcb->bta_conn_id);
|
|
|
|
if (p_clcb->disc_active)
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_ERROR);
|
|
else
|
|
p_clcb->state = BTA_GATTC_CONN_ST;
|
|
|
|
// This function only gets called as the result of a BTA_GATTC_API_CLOSE_EVT
|
|
// while in the BTA_GATTC_DISCOVER_ST state. Once the state changes, the
|
|
// connection itself still needs to be closed to resolve the original event.
|
|
if (p_clcb->state == BTA_GATTC_CONN_ST) {
|
|
VLOG(1) << "State is back to BTA_GATTC_CONN_ST. Trigger connection close";
|
|
bta_gattc_close(p_clcb, p_data);
|
|
}
|
|
}
|
|
|
|
/** when a SRCB start discovery, tell all related clcb and set the state */
|
|
void bta_gattc_set_discover_st(tBTA_GATTC_SERV* p_srcb) {
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < BTA_GATTC_CLCB_MAX; i++) {
|
|
if (bta_gattc_cb.clcb[i].p_srcb == p_srcb) {
|
|
bta_gattc_cb.clcb[i].status = GATT_SUCCESS;
|
|
bta_gattc_cb.clcb[i].state = BTA_GATTC_DISCOVER_ST;
|
|
bta_gattc_cb.clcb[i].request_during_discovery =
|
|
BTA_GATTC_DISCOVER_REQ_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** process service change in discovery state, mark up the auto update flag and
|
|
* set status to be discovery cancel for current discovery.
|
|
*/
|
|
void bta_gattc_restart_discover(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
p_clcb->status = GATT_CANCEL;
|
|
p_clcb->auto_update = BTA_GATTC_DISC_WAITING;
|
|
}
|
|
|
|
/** Configure MTU size on the GATT connection */
|
|
void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
if (!bta_gattc_enqueue(p_clcb, p_data)) return;
|
|
|
|
tGATT_STATUS status =
|
|
GATTC_ConfigureMTU(p_clcb->bta_conn_id, p_data->api_mtu.mtu);
|
|
|
|
/* if failed, return callback here */
|
|
if (status != GATT_SUCCESS && status != GATT_CMD_STARTED) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) p_clcb->p_q_cmd = NULL;
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG, status,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
void bta_gattc_start_discover_internal(tBTA_GATTC_CLCB* p_clcb) {
|
|
if (p_clcb->transport == BT_TRANSPORT_LE)
|
|
L2CA_EnableUpdateBleConnParams(p_clcb->p_srcb->server_bda, false);
|
|
|
|
bta_gattc_init_cache(p_clcb->p_srcb);
|
|
p_clcb->status = bta_gattc_discover_pri_service(
|
|
p_clcb->bta_conn_id, p_clcb->p_srcb, GATT_DISC_SRVC_ALL);
|
|
if (p_clcb->status != GATT_SUCCESS) {
|
|
LOG(ERROR) << "discovery on server failed";
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status);
|
|
} else
|
|
p_clcb->disc_active = true;
|
|
}
|
|
|
|
/** Start a discovery on server */
|
|
void bta_gattc_start_discover(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
VLOG(1) << __func__ << ": conn_id:" << loghex(p_clcb->bta_conn_id)
|
|
<< " p_clcb->p_srcb->state:" << +p_clcb->p_srcb->state;
|
|
|
|
if (((p_clcb->p_q_cmd == NULL ||
|
|
p_clcb->auto_update == BTA_GATTC_REQ_WAITING) &&
|
|
p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) ||
|
|
p_clcb->p_srcb->state == BTA_GATTC_SERV_DISC)
|
|
/* no pending operation, start discovery right away */
|
|
{
|
|
p_clcb->auto_update = BTA_GATTC_NO_SCHEDULE;
|
|
|
|
if (p_clcb->p_srcb != NULL) {
|
|
/* set all srcb related clcb into discovery ST */
|
|
bta_gattc_set_discover_st(p_clcb->p_srcb);
|
|
|
|
/* clear the service change mask */
|
|
p_clcb->p_srcb->srvc_hdl_chg = false;
|
|
p_clcb->p_srcb->update_count = 0;
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC_ACT;
|
|
|
|
/* read db hash if db hash characteristic exists */
|
|
if (bta_gattc_is_robust_caching_enabled() &&
|
|
p_clcb->p_srcb->srvc_hdl_db_hash && bta_gattc_read_db_hash(p_clcb)) {
|
|
LOG(INFO) << __func__
|
|
<< ": pending service discovery, read db hash first";
|
|
p_clcb->p_srcb->srvc_hdl_db_hash = false;
|
|
return;
|
|
}
|
|
|
|
bta_gattc_start_discover_internal(p_clcb);
|
|
} else {
|
|
LOG(ERROR) << "unknown device, can not start discovery";
|
|
}
|
|
}
|
|
/* pending operation, wait until it finishes */
|
|
else {
|
|
p_clcb->auto_update = BTA_GATTC_DISC_WAITING;
|
|
|
|
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE)
|
|
p_clcb->state = BTA_GATTC_CONN_ST; /* set clcb state */
|
|
}
|
|
}
|
|
|
|
/** discovery on server is finished */
|
|
void bta_gattc_disc_cmpl(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd;
|
|
|
|
VLOG(1) << __func__ << ": conn_id=" << loghex(p_clcb->bta_conn_id);
|
|
|
|
if (p_clcb->transport == BT_TRANSPORT_LE)
|
|
L2CA_EnableUpdateBleConnParams(p_clcb->p_srcb->server_bda, true);
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
|
|
p_clcb->disc_active = false;
|
|
|
|
if (p_clcb->status != GATT_SUCCESS) {
|
|
/* clean up cache */
|
|
if (p_clcb->p_srcb) {
|
|
p_clcb->p_srcb->gatt_database.Clear();
|
|
}
|
|
|
|
/* used to reset cache in application */
|
|
bta_gattc_cache_reset(p_clcb->p_srcb->server_bda);
|
|
}
|
|
|
|
if (p_clcb->p_srcb) {
|
|
p_clcb->p_srcb->pending_discovery.Clear();
|
|
}
|
|
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
|
|
/* start discovery again */
|
|
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
/* get any queued command to proceed */
|
|
else if (p_q_cmd != NULL) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
/* execute pending operation of link block still present */
|
|
if (L2CA_IsLinkEstablished(p_clcb->p_srcb->server_bda, p_clcb->transport)) {
|
|
bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
|
|
}
|
|
/* if the command executed requeued the cmd, we don't
|
|
* want to free the underlying buffer that's being
|
|
* referenced by p_clcb->p_q_cmd
|
|
*/
|
|
if (p_q_cmd != p_clcb->p_q_cmd) osi_free_and_reset((void**)&p_q_cmd);
|
|
}
|
|
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
tBTA_GATTC bta_gattc;
|
|
bta_gattc.remote_bda = p_clcb->p_srcb->server_bda;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_SRVC_DISC_DONE_EVT, &bta_gattc);
|
|
}
|
|
}
|
|
|
|
/** Read an attribute */
|
|
void bta_gattc_read(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
if (!bta_gattc_enqueue(p_clcb, p_data)) return;
|
|
|
|
tGATT_STATUS status;
|
|
if (p_data->api_read.handle != 0) {
|
|
tGATT_READ_PARAM read_param;
|
|
memset(&read_param, 0, sizeof(tGATT_READ_PARAM));
|
|
read_param.by_handle.handle = p_data->api_read.handle;
|
|
read_param.by_handle.auth_req = p_data->api_read.auth_req;
|
|
status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_HANDLE, &read_param);
|
|
} else {
|
|
tGATT_READ_PARAM read_param;
|
|
memset(&read_param, 0, sizeof(tGATT_READ_BY_TYPE));
|
|
|
|
read_param.char_type.s_handle = p_data->api_read.s_handle;
|
|
read_param.char_type.e_handle = p_data->api_read.e_handle;
|
|
read_param.char_type.uuid = p_data->api_read.uuid;
|
|
read_param.char_type.auth_req = p_data->api_read.auth_req;
|
|
status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_TYPE, &read_param);
|
|
}
|
|
|
|
/* read fail */
|
|
if (status != GATT_SUCCESS) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) p_clcb->p_q_cmd = NULL;
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/** read multiple */
|
|
void bta_gattc_read_multi(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_DATA* p_data) {
|
|
if (!bta_gattc_enqueue(p_clcb, p_data)) return;
|
|
|
|
tGATT_READ_PARAM read_param;
|
|
memset(&read_param, 0, sizeof(tGATT_READ_PARAM));
|
|
|
|
read_param.read_multiple.num_handles = p_data->api_read_multi.num_attr;
|
|
read_param.read_multiple.auth_req = p_data->api_read_multi.auth_req;
|
|
memcpy(&read_param.read_multiple.handles, p_data->api_read_multi.handles,
|
|
sizeof(uint16_t) * p_data->api_read_multi.num_attr);
|
|
|
|
tGATT_STATUS status =
|
|
GATTC_Read(p_clcb->bta_conn_id, GATT_READ_MULTIPLE, &read_param);
|
|
/* read fail */
|
|
if (status != GATT_SUCCESS) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) p_clcb->p_q_cmd = NULL;
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/** Write an attribute */
|
|
void bta_gattc_write(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
if (!bta_gattc_enqueue(p_clcb, p_data)) return;
|
|
|
|
tGATT_STATUS status = GATT_SUCCESS;
|
|
tGATT_VALUE attr;
|
|
|
|
attr.conn_id = p_clcb->bta_conn_id;
|
|
attr.handle = p_data->api_write.handle;
|
|
attr.offset = p_data->api_write.offset;
|
|
attr.len = p_data->api_write.len;
|
|
attr.auth_req = p_data->api_write.auth_req;
|
|
|
|
if (p_data->api_write.p_value)
|
|
memcpy(attr.value, p_data->api_write.p_value, p_data->api_write.len);
|
|
|
|
status =
|
|
GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &attr);
|
|
|
|
/* write fail */
|
|
if (status != GATT_SUCCESS) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) p_clcb->p_q_cmd = NULL;
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_WRITE, status,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/** send execute write */
|
|
void bta_gattc_execute(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
if (!bta_gattc_enqueue(p_clcb, p_data)) return;
|
|
|
|
tGATT_STATUS status =
|
|
GATTC_ExecuteWrite(p_clcb->bta_conn_id, p_data->api_exec.is_execute);
|
|
if (status != GATT_SUCCESS) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) p_clcb->p_q_cmd = NULL;
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_EXE_WRITE, status,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/** send handle value confirmation */
|
|
void bta_gattc_confirm(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
uint16_t cid = p_data->api_confirm.cid;
|
|
|
|
if (GATTC_SendHandleValueConfirm(p_data->api_confirm.hdr.layer_specific,
|
|
cid) != GATT_SUCCESS) {
|
|
LOG(ERROR) << __func__ << ": to cid=" << loghex(cid) << " failed";
|
|
} else {
|
|
/* if over BR_EDR, inform PM for mode change */
|
|
if (p_clcb->transport == BT_TRANSPORT_BR_EDR) {
|
|
bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** read complete */
|
|
static void bta_gattc_read_cmpl(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_OP_CMPL* p_data) {
|
|
GATT_READ_OP_CB cb = p_clcb->p_q_cmd->api_read.read_cb;
|
|
void* my_cb_data = p_clcb->p_q_cmd->api_read.read_cb_data;
|
|
|
|
/* if it was read by handle, return the handle requested, if read by UUID, use
|
|
* handle returned from remote
|
|
*/
|
|
uint16_t handle = p_clcb->p_q_cmd->api_read.handle;
|
|
if (handle == 0) handle = p_data->p_cmpl->att_value.handle;
|
|
|
|
osi_free_and_reset((void**)&p_clcb->p_q_cmd);
|
|
|
|
if (cb) {
|
|
cb(p_clcb->bta_conn_id, p_data->status, handle,
|
|
p_data->p_cmpl->att_value.len, p_data->p_cmpl->att_value.value,
|
|
my_cb_data);
|
|
}
|
|
}
|
|
|
|
/** write complete */
|
|
static void bta_gattc_write_cmpl(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_OP_CMPL* p_data) {
|
|
GATT_WRITE_OP_CB cb = p_clcb->p_q_cmd->api_write.write_cb;
|
|
void* my_cb_data = p_clcb->p_q_cmd->api_write.write_cb_data;
|
|
|
|
osi_free_and_reset((void**)&p_clcb->p_q_cmd);
|
|
|
|
if (cb) {
|
|
cb(p_clcb->bta_conn_id, p_data->status, p_data->p_cmpl->att_value.handle,
|
|
my_cb_data);
|
|
}
|
|
}
|
|
|
|
/** execute write complete */
|
|
static void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_OP_CMPL* p_data) {
|
|
tBTA_GATTC cb_data;
|
|
|
|
osi_free_and_reset((void**)&p_clcb->p_q_cmd);
|
|
p_clcb->status = GATT_SUCCESS;
|
|
|
|
/* execute complete, callback */
|
|
cb_data.exec_cmpl.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.exec_cmpl.status = p_data->status;
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_EXEC_EVT, &cb_data);
|
|
}
|
|
|
|
/** configure MTU operation complete */
|
|
static void bta_gattc_cfg_mtu_cmpl(tBTA_GATTC_CLCB* p_clcb,
|
|
const tBTA_GATTC_OP_CMPL* p_data) {
|
|
GATT_CONFIGURE_MTU_OP_CB cb = p_clcb->p_q_cmd->api_mtu.mtu_cb;
|
|
void* my_cb_data = p_clcb->p_q_cmd->api_mtu.mtu_cb_data;
|
|
tBTA_GATTC cb_data;
|
|
|
|
osi_free_and_reset((void**)&p_clcb->p_q_cmd);
|
|
|
|
if (p_data->p_cmpl && p_data->status == GATT_SUCCESS)
|
|
p_clcb->p_srcb->mtu = p_data->p_cmpl->mtu;
|
|
|
|
/* configure MTU complete, callback */
|
|
p_clcb->status = p_data->status;
|
|
cb_data.cfg_mtu.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.cfg_mtu.status = p_data->status;
|
|
cb_data.cfg_mtu.mtu = p_clcb->p_srcb->mtu;
|
|
|
|
if (cb) {
|
|
cb(p_clcb->bta_conn_id, p_data->status, my_cb_data);
|
|
}
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CFG_MTU_EVT, &cb_data);
|
|
}
|
|
|
|
/** operation completed */
|
|
void bta_gattc_op_cmpl(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
if (p_clcb->p_q_cmd == NULL) {
|
|
LOG_ERROR("No pending command gatt client command");
|
|
return;
|
|
}
|
|
|
|
const tGATTC_OPTYPE op = p_data->op_cmpl.op_code;
|
|
switch (op) {
|
|
case GATTC_OPTYPE_READ:
|
|
case GATTC_OPTYPE_WRITE:
|
|
case GATTC_OPTYPE_EXE_WRITE:
|
|
case GATTC_OPTYPE_CONFIG:
|
|
break;
|
|
|
|
case GATTC_OPTYPE_NONE:
|
|
case GATTC_OPTYPE_DISCOVERY:
|
|
case GATTC_OPTYPE_NOTIFICATION:
|
|
case GATTC_OPTYPE_INDICATION:
|
|
default:
|
|
LOG(ERROR) << "unexpected operation, ignored";
|
|
return;
|
|
}
|
|
|
|
if (p_clcb->p_q_cmd->hdr.event !=
|
|
bta_gattc_opcode_to_int_evt[op - GATTC_OPTYPE_READ]) {
|
|
uint8_t mapped_op =
|
|
p_clcb->p_q_cmd->hdr.event - BTA_GATTC_API_READ_EVT + GATTC_OPTYPE_READ;
|
|
if (mapped_op > GATTC_OPTYPE_INDICATION) mapped_op = 0;
|
|
|
|
LOG(ERROR) << StringPrintf(
|
|
"expect op:(%s :0x%04x), receive unexpected operation (%s).",
|
|
bta_gattc_op_code_name[mapped_op], p_clcb->p_q_cmd->hdr.event,
|
|
bta_gattc_op_code_name[op]);
|
|
return;
|
|
}
|
|
|
|
/* Except for MTU configuration, discard responses if service change
|
|
* indication is received before operation completed
|
|
*/
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING &&
|
|
p_clcb->p_srcb->srvc_hdl_chg && op != GATTC_OPTYPE_CONFIG) {
|
|
VLOG(1) << "Discard all responses when service change indication is "
|
|
"received.";
|
|
// TODO Fix constness
|
|
const_cast<tBTA_GATTC_DATA*>(p_data)->op_cmpl.status = GATT_ERROR;
|
|
}
|
|
|
|
/* service handle change void the response, discard it */
|
|
if (op == GATTC_OPTYPE_READ)
|
|
bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl);
|
|
|
|
else if (op == GATTC_OPTYPE_WRITE)
|
|
bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);
|
|
|
|
else if (op == GATTC_OPTYPE_EXE_WRITE)
|
|
bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl);
|
|
|
|
else if (op == GATTC_OPTYPE_CONFIG)
|
|
bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);
|
|
|
|
// If receive DATABASE_OUT_OF_SYNC error code, bta_gattc should start service
|
|
// discovery immediately
|
|
if (bta_gattc_is_robust_caching_enabled() &&
|
|
p_data->op_cmpl.status == GATT_DATABASE_OUT_OF_SYNC) {
|
|
LOG(INFO) << __func__ << ": DATABASE_OUT_OF_SYNC, re-discover service";
|
|
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
|
|
/* request read db hash first */
|
|
p_clcb->p_srcb->srvc_hdl_db_hash = true;
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
return;
|
|
}
|
|
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
|
|
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
|
|
|
|
/* request read db hash first */
|
|
if (bta_gattc_is_robust_caching_enabled()) {
|
|
p_clcb->p_srcb->srvc_hdl_db_hash = true;
|
|
}
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
|
|
/** start a search in the local server cache */
|
|
void bta_gattc_search(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
tGATT_STATUS status = GATT_INTERNAL_ERROR;
|
|
tBTA_GATTC cb_data;
|
|
VLOG(1) << __func__ << ": conn_id=" << loghex(p_clcb->bta_conn_id);
|
|
if (p_clcb->p_srcb && !p_clcb->p_srcb->gatt_database.IsEmpty()) {
|
|
status = GATT_SUCCESS;
|
|
/* search the local cache of a server device */
|
|
bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);
|
|
}
|
|
cb_data.search_cmpl.status = status;
|
|
cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id;
|
|
|
|
/* end of search or no server cache available */
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT, &cb_data);
|
|
}
|
|
|
|
/** enqueue a command into control block, usually because discovery operation is
|
|
* busy */
|
|
void bta_gattc_q_cmd(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
|
|
bta_gattc_enqueue(p_clcb, p_data);
|
|
}
|
|
|
|
/** report API call failure back to apps */
|
|
void bta_gattc_fail(tBTA_GATTC_CLCB* p_clcb,
|
|
UNUSED_ATTR const tBTA_GATTC_DATA* p_data) {
|
|
if (p_clcb->status == GATT_SUCCESS) {
|
|
LOG(ERROR) << "operation not supported at current state " << +p_clcb->state;
|
|
}
|
|
}
|
|
|
|
/* De-Register a GATT client application with BTA completed */
|
|
static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB* p_clreg) {
|
|
tGATT_IF client_if = p_clreg->client_if;
|
|
tBTA_GATTC cb_data;
|
|
tBTA_GATTC_CBACK* p_cback = p_clreg->p_cback;
|
|
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
|
|
GATT_Deregister(p_clreg->client_if);
|
|
memset(p_clreg, 0, sizeof(tBTA_GATTC_RCB));
|
|
|
|
cb_data.reg_oper.client_if = client_if;
|
|
cb_data.reg_oper.status = GATT_SUCCESS;
|
|
|
|
if (p_cback) /* callback with de-register event */
|
|
(*p_cback)(BTA_GATTC_DEREG_EVT, &cb_data);
|
|
|
|
if (bta_gattc_num_reg_app() == 0 &&
|
|
bta_gattc_cb.state == BTA_GATTC_STATE_DISABLING) {
|
|
bta_gattc_cb.state = BTA_GATTC_STATE_DISABLED;
|
|
}
|
|
}
|
|
|
|
/** callback functions to GATT client stack */
|
|
static void bta_gattc_conn_cback(tGATT_IF gattc_if, const RawAddress& bdaddr,
|
|
uint16_t conn_id, bool connected,
|
|
tGATT_DISCONN_REASON reason,
|
|
tBT_TRANSPORT transport) {
|
|
if (connected) {
|
|
LOG_INFO("Connected att_id:%hhu transport:%s reason:%s", gattc_if,
|
|
bt_transport_text(transport).c_str(),
|
|
gatt_disconnection_reason_text(reason).c_str());
|
|
btif_debug_conn_state(bdaddr, BTIF_DEBUG_CONNECTED, GATT_CONN_UNKNOWN);
|
|
} else {
|
|
LOG_INFO("Disconnected att_id:%hhu transport:%s reason:%s", gattc_if,
|
|
bt_transport_text(transport).c_str(),
|
|
gatt_disconnection_reason_text(reason).c_str());
|
|
btif_debug_conn_state(bdaddr, BTIF_DEBUG_DISCONNECTED, GATT_CONN_UNKNOWN);
|
|
}
|
|
|
|
tBTA_GATTC_DATA* p_buf =
|
|
(tBTA_GATTC_DATA*)osi_calloc(sizeof(tBTA_GATTC_DATA));
|
|
p_buf->int_conn.hdr.event =
|
|
connected ? BTA_GATTC_INT_CONN_EVT : BTA_GATTC_INT_DISCONN_EVT;
|
|
p_buf->int_conn.hdr.layer_specific = conn_id;
|
|
p_buf->int_conn.client_if = gattc_if;
|
|
p_buf->int_conn.role = L2CA_GetBleConnRole(bdaddr);
|
|
p_buf->int_conn.reason = reason;
|
|
p_buf->int_conn.transport = transport;
|
|
p_buf->int_conn.remote_bda = bdaddr;
|
|
|
|
bta_sys_sendmsg(p_buf);
|
|
}
|
|
|
|
/** encryption complete callback function to GATT client stack */
|
|
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, const RawAddress& bda) {
|
|
tBTA_GATTC_CLCB* p_clcb =
|
|
bta_gattc_find_clcb_by_cif(gattc_if, bda, BT_TRANSPORT_LE);
|
|
|
|
if (p_clcb == NULL) return;
|
|
|
|
/* filter this event just for BTA HH LE GATT client,
|
|
* In the future, if we want to enable encryption complete event
|
|
* for all GATT clients, we can remove this code
|
|
*/
|
|
if (!bta_hh_le_is_hh_gatt_if(gattc_if)) {
|
|
return;
|
|
}
|
|
|
|
VLOG(1) << __func__ << ": cif:" << +gattc_if;
|
|
|
|
do_in_main_thread(FROM_HERE,
|
|
base::Bind(&bta_gattc_process_enc_cmpl, gattc_if, bda));
|
|
}
|
|
|
|
/** process refresh API to delete cache and start a new discovery if currently
|
|
* connected */
|
|
void bta_gattc_process_api_refresh(const RawAddress& remote_bda) {
|
|
tBTA_GATTC_SERV* p_srvc_cb = bta_gattc_find_srvr_cache(remote_bda);
|
|
if (p_srvc_cb) {
|
|
/* try to find a CLCB */
|
|
if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) {
|
|
bool found = false;
|
|
tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[0];
|
|
for (uint8_t i = 0; i < BTA_GATTC_CLCB_MAX; i++, p_clcb++) {
|
|
if (p_clcb->in_use && p_clcb->p_srcb == p_srvc_cb) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
return;
|
|
}
|
|
}
|
|
/* in all other cases, mark it and delete the cache */
|
|
|
|
p_srvc_cb->gatt_database.Clear();
|
|
}
|
|
|
|
/* used to reset cache in application */
|
|
bta_gattc_cache_reset(remote_bda);
|
|
}
|
|
|
|
/** process service change indication */
|
|
bool bta_gattc_process_srvc_chg_ind(uint16_t conn_id, tBTA_GATTC_RCB* p_clrcb,
|
|
tBTA_GATTC_SERV* p_srcb,
|
|
tBTA_GATTC_CLCB* p_clcb,
|
|
tBTA_GATTC_NOTIFY* p_notify,
|
|
tGATT_VALUE* att_value) {
|
|
|
|
Uuid gattp_uuid = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);
|
|
Uuid srvc_chg_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
|
|
|
|
if (p_srcb->gatt_database.IsEmpty() && p_srcb->state == BTA_GATTC_SERV_IDLE) {
|
|
bta_gattc_cache_load(p_srcb);
|
|
}
|
|
|
|
const gatt::Characteristic* p_char =
|
|
bta_gattc_get_characteristic_srcb(p_srcb, p_notify->handle);
|
|
if (!p_char) return false;
|
|
const gatt::Service* p_svc =
|
|
bta_gattc_get_service_for_handle_srcb(p_srcb, p_char->value_handle);
|
|
if (!p_svc || p_svc->uuid != gattp_uuid || p_char->uuid != srvc_chg_uuid) {
|
|
return false;
|
|
}
|
|
|
|
if (att_value->len != BTA_GATTC_SERVICE_CHANGED_LEN) {
|
|
LOG(ERROR) << __func__
|
|
<< ": received malformed service changed indication, skipping";
|
|
return false;
|
|
}
|
|
|
|
uint8_t* p = att_value->value;
|
|
uint16_t s_handle = ((uint16_t)(*(p)) + (((uint16_t)(*(p + 1))) << 8));
|
|
uint16_t e_handle = ((uint16_t)(*(p + 2)) + (((uint16_t)(*(p + 3))) << 8));
|
|
|
|
LOG(ERROR) << __func__ << ": service changed s_handle=" << loghex(s_handle)
|
|
<< ", e_handle=" << loghex(e_handle);
|
|
|
|
/* mark service handle change pending */
|
|
p_srcb->srvc_hdl_chg = true;
|
|
/* clear up all notification/indication registration */
|
|
bta_gattc_clear_notif_registration(p_srcb, conn_id, s_handle, e_handle);
|
|
/* service change indication all received, do discovery update */
|
|
if (++p_srcb->update_count == bta_gattc_num_reg_app()) {
|
|
/* not an opened connection; or connection busy */
|
|
/* search for first available clcb and start discovery */
|
|
if (p_clcb == NULL || (p_clcb && p_clcb->p_q_cmd != NULL)) {
|
|
for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) {
|
|
if (bta_gattc_cb.clcb[i].in_use &&
|
|
bta_gattc_cb.clcb[i].p_srcb == p_srcb &&
|
|
bta_gattc_cb.clcb[i].p_q_cmd == NULL) {
|
|
p_clcb = &bta_gattc_cb.clcb[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* send confirmation here if this is an indication, it should always be */
|
|
GATTC_SendHandleValueConfirm(conn_id, p_notify->cid);
|
|
|
|
/* if connection available, refresh cache by doing discovery now */
|
|
if (p_clcb) {
|
|
/* request read db hash first */
|
|
if (bta_gattc_is_robust_caching_enabled()) {
|
|
p_srcb->srvc_hdl_db_hash = true;
|
|
}
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
|
|
/* notify applicationf or service change */
|
|
if (p_clrcb->p_cback) {
|
|
tBTA_GATTC bta_gattc;
|
|
bta_gattc.service_changed.remote_bda = p_srcb->server_bda;
|
|
bta_gattc.service_changed.conn_id = conn_id;
|
|
(*p_clrcb->p_cback)(BTA_GATTC_SRVC_CHG_EVT, &bta_gattc);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** process all non-service change indication/notification */
|
|
void bta_gattc_proc_other_indication(tBTA_GATTC_CLCB* p_clcb, uint8_t op,
|
|
tGATT_CL_COMPLETE* p_data,
|
|
tBTA_GATTC_NOTIFY* p_notify) {
|
|
VLOG(1) << __func__
|
|
<< StringPrintf(
|
|
": check p_data->att_value.handle=%d p_data->handle=%d",
|
|
p_data->att_value.handle, p_data->handle);
|
|
VLOG(1) << "is_notify " << p_notify->is_notify;
|
|
|
|
p_notify->is_notify = (op == GATTC_OPTYPE_INDICATION) ? false : true;
|
|
p_notify->len = p_data->att_value.len;
|
|
p_notify->bda = p_clcb->bda;
|
|
memcpy(p_notify->value, p_data->att_value.value, p_data->att_value.len);
|
|
p_notify->conn_id = p_clcb->bta_conn_id;
|
|
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
tBTA_GATTC bta_gattc;
|
|
bta_gattc.notify = *p_notify;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_NOTIF_EVT, &bta_gattc);
|
|
}
|
|
}
|
|
|
|
/** process indication/notification */
|
|
void bta_gattc_process_indicate(uint16_t conn_id, tGATTC_OPTYPE op,
|
|
tGATT_CL_COMPLETE* p_data) {
|
|
uint16_t handle = p_data->att_value.handle;
|
|
tBTA_GATTC_NOTIFY notify;
|
|
RawAddress remote_bda;
|
|
tGATT_IF gatt_if;
|
|
tBT_TRANSPORT transport;
|
|
|
|
if (!GATT_GetConnectionInfor(conn_id, &gatt_if, remote_bda, &transport)) {
|
|
LOG(ERROR) << __func__ << ": indication/notif for unknown app";
|
|
if (op == GATTC_OPTYPE_INDICATION)
|
|
GATTC_SendHandleValueConfirm(conn_id, p_data->cid);
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_RCB* p_clrcb = bta_gattc_cl_get_regcb(gatt_if);
|
|
if (p_clrcb == NULL) {
|
|
LOG(ERROR) << __func__ << ": indication/notif for unregistered app";
|
|
if (op == GATTC_OPTYPE_INDICATION)
|
|
GATTC_SendHandleValueConfirm(conn_id, p_data->cid);
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_SERV* p_srcb = bta_gattc_find_srcb(remote_bda);
|
|
if (p_srcb == NULL) {
|
|
LOG(ERROR) << __func__ << ": indication/notif for unknown device, ignore";
|
|
if (op == GATTC_OPTYPE_INDICATION)
|
|
GATTC_SendHandleValueConfirm(conn_id, p_data->cid);
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
|
|
|
|
notify.handle = handle;
|
|
notify.cid = p_data->cid;
|
|
|
|
/* if service change indication/notification, don't forward to application */
|
|
if (bta_gattc_process_srvc_chg_ind(conn_id, p_clrcb, p_srcb, p_clcb, ¬ify,
|
|
&p_data->att_value))
|
|
return;
|
|
|
|
/* if app registered for the notification */
|
|
if (bta_gattc_check_notif_registry(p_clrcb, p_srcb, ¬ify)) {
|
|
/* connection not open yet */
|
|
if (p_clcb == NULL) {
|
|
p_clcb = bta_gattc_clcb_alloc(gatt_if, remote_bda, transport);
|
|
|
|
if (p_clcb == NULL) {
|
|
LOG(ERROR) << "No resources";
|
|
return;
|
|
}
|
|
|
|
p_clcb->bta_conn_id = conn_id;
|
|
p_clcb->transport = transport;
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, NULL);
|
|
}
|
|
|
|
if (p_clcb != NULL)
|
|
bta_gattc_proc_other_indication(p_clcb, op, p_data, ¬ify);
|
|
}
|
|
/* no one intersted and need ack? */
|
|
else if (op == GATTC_OPTYPE_INDICATION) {
|
|
VLOG(1) << __func__ << " no one interested, ack now";
|
|
GATTC_SendHandleValueConfirm(conn_id, p_data->cid);
|
|
}
|
|
}
|
|
|
|
/** client operation complete callback register with BTE GATT */
|
|
static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
|
|
tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE* p_data) {
|
|
VLOG(1) << __func__ << ": conn_id:" << +conn_id << " op:" << +op
|
|
<< " status:" << +status;
|
|
|
|
/* notification and indication processed right away */
|
|
if (op == GATTC_OPTYPE_NOTIFICATION || op == GATTC_OPTYPE_INDICATION) {
|
|
bta_gattc_process_indicate(conn_id, op, p_data);
|
|
return;
|
|
}
|
|
/* for all other operation, not expected if w/o connection */
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
|
|
if (!p_clcb) {
|
|
LOG(ERROR) << __func__ << ": unknown conn_id=" << loghex(conn_id)
|
|
<< " ignore data";
|
|
return;
|
|
}
|
|
|
|
/* if over BR_EDR, inform PM for mode change */
|
|
if (p_clcb->transport == BT_TRANSPORT_BR_EDR) {
|
|
bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(conn_id, op, status, p_data);
|
|
}
|
|
|
|
/** client operation complete send message */
|
|
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
|
|
tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE* p_data) {
|
|
const size_t len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE);
|
|
tBTA_GATTC_OP_CMPL* p_buf = (tBTA_GATTC_OP_CMPL*)osi_calloc(len);
|
|
|
|
p_buf->hdr.event = BTA_GATTC_OP_CMPL_EVT;
|
|
p_buf->hdr.layer_specific = conn_id;
|
|
p_buf->status = status;
|
|
p_buf->op_code = op;
|
|
|
|
if (p_data) {
|
|
p_buf->p_cmpl = (tGATT_CL_COMPLETE*)(p_buf + 1);
|
|
memcpy(p_buf->p_cmpl, p_data, sizeof(tGATT_CL_COMPLETE));
|
|
}
|
|
|
|
bta_sys_sendmsg(p_buf);
|
|
}
|
|
|
|
/** congestion callback for BTA GATT client */
|
|
static void bta_gattc_cong_cback(uint16_t conn_id, bool congested) {
|
|
tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
|
|
if (!p_clcb || !p_clcb->p_rcb->p_cback) return;
|
|
|
|
tBTA_GATTC cb_data;
|
|
cb_data.congest.conn_id = conn_id;
|
|
cb_data.congest.congested = congested;
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CONGEST_EVT, &cb_data);
|
|
}
|
|
|
|
static void bta_gattc_phy_update_cback(tGATT_IF gatt_if, uint16_t conn_id,
|
|
uint8_t tx_phy, uint8_t rx_phy,
|
|
tGATT_STATUS status) {
|
|
tBTA_GATTC_RCB* p_clreg = bta_gattc_cl_get_regcb(gatt_if);
|
|
|
|
if (!p_clreg || !p_clreg->p_cback) {
|
|
LOG(ERROR) << __func__ << ": client_if=" << +gatt_if << " not found";
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC cb_data;
|
|
cb_data.phy_update.conn_id = conn_id;
|
|
cb_data.phy_update.server_if = gatt_if;
|
|
cb_data.phy_update.tx_phy = tx_phy;
|
|
cb_data.phy_update.rx_phy = rx_phy;
|
|
cb_data.phy_update.status = status;
|
|
(*p_clreg->p_cback)(BTA_GATTC_PHY_UPDATE_EVT, &cb_data);
|
|
}
|
|
|
|
static void bta_gattc_conn_update_cback(tGATT_IF gatt_if, uint16_t conn_id,
|
|
uint16_t interval, uint16_t latency,
|
|
uint16_t timeout, tGATT_STATUS status) {
|
|
tBTA_GATTC_RCB* p_clreg = bta_gattc_cl_get_regcb(gatt_if);
|
|
|
|
if (!p_clreg || !p_clreg->p_cback) {
|
|
LOG(ERROR) << __func__ << ": client_if=" << gatt_if << " not found";
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC cb_data;
|
|
cb_data.conn_update.conn_id = conn_id;
|
|
cb_data.conn_update.interval = interval;
|
|
cb_data.conn_update.latency = latency;
|
|
cb_data.conn_update.timeout = timeout;
|
|
cb_data.conn_update.status = status;
|
|
(*p_clreg->p_cback)(BTA_GATTC_CONN_UPDATE_EVT, &cb_data);
|
|
}
|