LE: Register for service change indication
Some bonded remote devices require the client to register for service
change indication and actually set the client configuration descriptor
before service change indications can be removed.
This change add an additional step after device bonding has occured to
register for service change indication if possible.
Bug: 18173911
Change-Id: I25386faec0d58834ee2b0a9d1db2d2e052311264
diff --git a/bta/dm/bta_dm_act.c b/bta/dm/bta_dm_act.c
index 2ef728b..ed38b7e 100644
--- a/bta/dm/bta_dm_act.c
+++ b/bta/dm/bta_dm_act.c
@@ -4937,8 +4937,8 @@
else
{
sec_event.auth_cmpl.success = TRUE;
+ GATT_ConfigServiceChangeCCC(bda, TRUE, BT_TRANSPORT_LE);
}
- sec_event.auth_cmpl.privacy_enabled = p_data->complt.privacy_supported;
if (bta_dm_cb.p_sec_cback)
{
//bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index b9403e9..22eab79 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -761,9 +761,6 @@
LINK_KEY key; /* Link key associated with peer device. */
UINT8 key_type; /* The type of Link Key */
BOOLEAN success; /* TRUE of authentication succeeded, FALSE if failed. */
-#if BLE_INCLUDED == TRUE
- BOOLEAN privacy_enabled; /* used for BLE device only */
-#endif
UINT8 fail_reason; /* The HCI reason/error code for when success=FALSE */
} tBTA_DM_AUTH_CMPL;
diff --git a/stack/gatt/gatt_attr.c b/stack/gatt/gatt_attr.c
index df852e3..8773036 100644
--- a/stack/gatt/gatt_attr.c
+++ b/stack/gatt/gatt_attr.c
@@ -40,17 +40,24 @@
#define GATTP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, GATTP_MAX_CHAR_VALUE_SIZE)
#endif
-static void gatt_profile_request_cback (UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data);
-static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
- BOOLEAN connected, tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport);
+static void gatt_request_cback(UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data);
+static void gatt_connect_cback(tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected,
+ tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport);
+static void gatt_disc_res_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data);
+static void gatt_disc_cmpl_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status);
+static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
+ tGATT_CL_COMPLETE *p_data);
+
+static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb);
+
static tGATT_CBACK gatt_profile_cback =
{
- gatt_profile_connect_cback,
- NULL,
- NULL,
- NULL,
- gatt_profile_request_cback,
+ gatt_connect_cback,
+ gatt_cl_op_cmpl_cback,
+ gatt_disc_res_cback,
+ gatt_disc_cmpl_cback,
+ gatt_request_cback,
NULL,
NULL
} ;
@@ -59,25 +66,39 @@
**
** Function gatt_profile_find_conn_id_by_bd_addr
**
-** Description The function searches all LCB with macthing bd address
+** Description Find the connection ID by remote address
**
-** Returns total number of clcb found.
+** Returns Connection ID
**
*******************************************************************************/
-UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda)
+UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR remote_bda)
+{
+ UINT16 conn_id = GATT_INVALID_CONN_ID;
+ GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &conn_id, BT_TRANSPORT_LE);
+ return conn_id;
+}
+
+/*******************************************************************************
+**
+** Function gatt_profile_find_clcb_by_conn_id
+**
+** Description find clcb by Connection ID
+**
+** Returns Pointer to the found link conenction control block.
+**
+*******************************************************************************/
+static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_conn_id(UINT16 conn_id)
{
UINT8 i_clcb;
tGATT_PROFILE_CLCB *p_clcb = NULL;
for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++)
{
- if (p_clcb->in_use && p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN))
- {
- return p_clcb->conn_id;
- }
+ if (p_clcb->in_use && p_clcb->conn_id == conn_id)
+ return p_clcb;
}
- return GATT_INVALID_CONN_ID;
+ return p_clcb;
}
/*******************************************************************************
@@ -98,9 +119,7 @@
{
if (p_clcb->in_use && p_clcb->transport == transport &&
p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN))
- {
return p_clcb;
- }
}
return p_clcb;
@@ -134,42 +153,31 @@
}
return p_clcb;
}
+
/*******************************************************************************
**
** Function gatt_profile_clcb_dealloc
**
** Description The function deallocates a GATT profile connection link control block
**
-** Returns NTrue the deallocation is successful
+** Returns void
**
*******************************************************************************/
-BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id)
+void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb)
{
- UINT8 i_clcb = 0;
- tGATT_PROFILE_CLCB *p_clcb = NULL;
-
- for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++)
- {
- if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id))
- {
- memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
- return TRUE;
- }
- }
- return FALSE;
+ memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
}
-
/*******************************************************************************
**
-** Function gatt_profile_request_cback
+** Function gatt_request_cback
**
** Description GATT profile attribute access request callback.
**
** Returns void.
**
*******************************************************************************/
-static void gatt_profile_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type,
+static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type,
tGATTS_DATA *p_data)
{
UINT8 status = GATT_INVALID_PDU;
@@ -211,36 +219,40 @@
/*******************************************************************************
**
-** Function gatt_profile_connect_cback
+** Function gatt_connect_cback
**
** Description Gatt profile connection callback.
**
** Returns void
**
*******************************************************************************/
-static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
+static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
BOOLEAN connected, tGATT_DISCONN_REASON reason,
tBT_TRANSPORT transport)
{
UNUSED(gatt_if);
- GATT_TRACE_EVENT ("gatt_profile_connect_cback: from %08x%04x connected:%d conn_id=%d reason = 0x%04x",
+ GATT_TRACE_EVENT ("%s: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", __FUNCTION__,
(bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3],
(bda[4]<<8)+bda[5], connected, conn_id, reason);
+ tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr(bda, transport);
+ if (p_clcb == NULL)
+ return;
+
if (connected)
{
- if (gatt_profile_clcb_alloc(conn_id, bda, transport) == NULL)
- {
- GATT_TRACE_ERROR ("gatt_profile_connect_cback: no_resource");
- return;
- }
- }
- else
- {
- gatt_profile_clcb_dealloc(conn_id);
- }
+ p_clcb->conn_id = conn_id;
+ p_clcb->connected = TRUE;
+ if (p_clcb->ccc_stage == GATT_SVC_CHANGED_CONNECTING)
+ {
+ p_clcb->ccc_stage ++;
+ gatt_cl_start_config_ccc(p_clcb);
+ }
+ } else {
+ gatt_profile_clcb_dealloc(p_clcb);
+ }
}
/*******************************************************************************
@@ -284,4 +296,215 @@
gatt_cb.gatt_if, status);
}
+/*******************************************************************************
+**
+** Function gatt_config_ccc_complete
+**
+** Description The function finish the service change ccc configuration
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_config_ccc_complete(tGATT_PROFILE_CLCB *p_clcb)
+{
+ GATT_Disconnect(p_clcb->conn_id);
+ gatt_profile_clcb_dealloc(p_clcb);
+}
+
+/*******************************************************************************
+**
+** Function gatt_disc_res_cback
+**
+** Description Gatt profile discovery result callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data)
+{
+ tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
+
+ if (p_clcb == NULL)
+ return;
+
+ switch (disc_type)
+ {
+ case GATT_DISC_SRVC_BY_UUID:/* stage 1 */
+ p_clcb->e_handle = p_data->value.group_value.e_handle;
+ p_clcb->ccc_result ++;
+ break;
+
+ case GATT_DISC_CHAR:/* stage 2 */
+ p_clcb->s_handle = p_data->value.dclr_value.val_handle;
+ p_clcb->ccc_result ++;
+ break;
+
+ case GATT_DISC_CHAR_DSCPT: /* stage 3 */
+ if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG)
+ {
+ p_clcb->s_handle = p_data->handle;
+ p_clcb->ccc_result ++;
+ }
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_disc_cmpl_cback
+**
+** Description Gatt profile discovery complete callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status)
+{
+ tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
+
+ if (p_clcb == NULL)
+ return;
+
+ if (status == GATT_SUCCESS && p_clcb->ccc_result > 0)
+ {
+ p_clcb->ccc_result = 0;
+ p_clcb->ccc_stage ++;
+ gatt_cl_start_config_ccc(p_clcb);
+ } else {
+ GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_cl_op_cmpl_cback
+**
+** Description Gatt profile client operation complete callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op,
+ tGATT_STATUS status, tGATT_CL_COMPLETE *p_data)
+{
+ tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
+
+ if (p_clcb == NULL)
+ return;
+
+ if (op == GATTC_OPTYPE_WRITE)
+ {
+ GATT_TRACE_DEBUG("%s() - ccc write status : %d", __FUNCTION__, status);
+ }
+
+ /* free the connection */
+ gatt_config_ccc_complete (p_clcb);
+}
+
+/*******************************************************************************
+**
+** Function gatt_cl_start_config_ccc
+**
+** Description Gatt profile start configure service change CCC
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb)
+{
+ tGATT_DISC_PARAM srvc_disc_param;
+ tGATT_VALUE ccc_value;
+
+ GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage);
+
+ memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM));
+ memset (&ccc_value, 0 , sizeof(tGATT_VALUE));
+
+ switch(p_clcb->ccc_stage)
+ {
+ case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */
+ srvc_disc_param.s_handle = 1;
+ srvc_disc_param.e_handle = 0xffff;
+ srvc_disc_param.service.len = 2;
+ srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER;
+ if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS)
+ {
+ GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__);
+ gatt_config_ccc_complete(p_clcb);
+ }
+ break;
+
+ case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */
+ srvc_disc_param.s_handle = 1;
+ srvc_disc_param.e_handle = p_clcb->e_handle;
+ srvc_disc_param.service.len = 2;
+ srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD;
+ if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS)
+ {
+ GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__);
+ gatt_config_ccc_complete(p_clcb);
+ }
+ break;
+
+ case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */
+ srvc_disc_param.s_handle = p_clcb->s_handle;
+ srvc_disc_param.e_handle = p_clcb->e_handle;
+ if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS)
+ {
+ GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__);
+ gatt_config_ccc_complete(p_clcb);
+ }
+ break;
+
+ case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */
+ ccc_value.handle = p_clcb->s_handle;
+ ccc_value.len = 2;
+ ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
+ if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS)
+ {
+ GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__);
+ gatt_config_ccc_complete(p_clcb);
+ }
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function GATT_ConfigServiceChangeCCC
+**
+** Description Configure service change indication on remote device
+**
+** Returns none
+**
+*******************************************************************************/
+void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport)
+{
+ UINT16 conn_id = GATT_INVALID_CONN_ID;
+ tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport);
+
+ if (p_clcb == NULL)
+ p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport);
+
+ if (p_clcb == NULL)
+ return;
+
+ if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport))
+ {
+ p_clcb->connected = TRUE;
+ }
+ /* hold the link here */
+ GATT_Connect(gatt_cb.gatt_if, remote_bda, TRUE, transport);
+ p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING;
+
+ if (!p_clcb->connected)
+ {
+ /* wait for connection */
+ return;
+ }
+
+ p_clcb->ccc_stage ++;
+ gatt_cl_start_config_ccc(p_clcb);
+}
+
#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h
index 663aa7d..d49c6e1 100644
--- a/stack/gatt/gatt_int.h
+++ b/stack/gatt/gatt_int.h
@@ -462,6 +462,11 @@
BOOLEAN in_use;
}tGATT_BG_CONN_DEV;
+#define GATT_SVC_CHANGED_CONNECTING 1 /* wait for connection */
+#define GATT_SVC_CHANGED_SERVICE 2 /* GATT service discovery */
+#define GATT_SVC_CHANGED_CHARACTERISTIC 3 /* service change char discovery */
+#define GATT_SVC_CHANGED_DESCRIPTOR 4 /* service change CCC discoery */
+#define GATT_SVC_CHANGED_CONFIGURE_CCCD 5 /* config CCC */
typedef struct
{
@@ -470,6 +475,12 @@
BOOLEAN connected;
BD_ADDR bda;
tBT_TRANSPORT transport;
+
+ /* GATT service change CCC related variables */
+ UINT8 ccc_stage;
+ UINT8 ccc_result;
+ UINT16 s_handle;
+ UINT16 e_handle;
}tGATT_PROFILE_CLCB;
typedef struct
@@ -557,8 +568,6 @@
/* from gatt_attr.c */
extern UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda);
-extern BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id);
-extern tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda, tBT_TRANSPORT transport);
/* Functions provided by att_protocol.c */
diff --git a/stack/include/gatt_api.h b/stack/include/gatt_api.h
index 392b4b2..2c99c02 100644
--- a/stack/include/gatt_api.h
+++ b/stack/include/gatt_api.h
@@ -1169,7 +1169,18 @@
*******************************************************************************/
GATT_API extern BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr);
-
+/*******************************************************************************
+**
+** Function GATT_ConfigServiceChangeCCC
+**
+** Description Configure service change indication on remote device
+**
+** Returns None.
+**
+*******************************************************************************/
+ GATT_API extern void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable,
+ tBT_TRANSPORT transport);
+
#ifdef __cplusplus
}