You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/07/17 18:36:15 UTC

[7/9] incubator-mynewt-core git commit: BLE Host - GAP event when peer subscribes to CCCD.

BLE Host - GAP event when peer subscribes to CCCD.

API changes (minus comments):

+define BLE_GAP_EVENT_SUBSCRIBE             14

+define BLE_GAP_SUBSCRIBE_REASON_WRITE      1
+define BLE_GAP_SUBSCRIBE_REASON_TERM       2
+define BLE_GAP_SUBSCRIBE_REASON_RESTORE    3

struct {
    uint16_t conn_handle;
    uint16_t attr_handle;
    uint8_t reason;
    uint8_t prev_notify:1;
    uint8_t cur_notify:1;
    uint8_t prev_indicate:1;
    uint8_t cur_indicate:1;
} subscribe;


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/7c8a4d68
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/7c8a4d68
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/7c8a4d68

Branch: refs/heads/develop
Commit: 7c8a4d68a27c639dfcfdefd05bca7db533a54d3a
Parents: 6e78b30
Author: Christopher Collins <cc...@apache.org>
Authored: Sat Jul 16 10:21:14 2016 -0700
Committer: Christopher Collins <cc...@apache.org>
Committed: Sun Jul 17 11:27:37 2016 -0700

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gap.h          |  60 ++-
 net/nimble/host/src/ble_gap.c                   |  31 ++
 net/nimble/host/src/ble_gap_priv.h              |   4 +
 net/nimble/host/src/ble_gatt_priv.h             |   2 +-
 net/nimble/host/src/ble_gatts.c                 | 140 ++++--
 net/nimble/host/src/ble_hs_conn.c               |   2 -
 .../host/src/test/ble_gatts_notify_test.c       | 430 ++++++++++++++-----
 7 files changed, 538 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/include/host/ble_gap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_gap.h b/net/nimble/host/include/host/ble_gap.h
index fd645ea..8cd4402 100644
--- a/net/nimble/host/include/host/ble_gap.h
+++ b/net/nimble/host/include/host/ble_gap.h
@@ -106,6 +106,21 @@ struct hci_conn_update;
 #define BLE_GAP_EVENT_PASSKEY_ACTION        11
 #define BLE_GAP_EVENT_NOTIFY_RX             12
 #define BLE_GAP_EVENT_NOTIFY_TX             13
+#define BLE_GAP_EVENT_SUBSCRIBE             14
+
+/*** Reason codes for the subscribe GAP event. */
+
+/** Peer's CCCD subscription state changed due to a descriptor write. */
+#define BLE_GAP_SUBSCRIBE_REASON_WRITE      1
+
+/** Peer's CCCD subscription state cleared due to connection termination. */
+#define BLE_GAP_SUBSCRIBE_REASON_TERM       2
+
+/**
+ * Peer's CCCD subscription state changed due to restore from persistence
+ * (bonding restored).
+ */
+#define BLE_GAP_SUBSCRIBE_REASON_RESTORE    3
 
 struct ble_gap_sec_state {
     unsigned encrypted:1;
@@ -375,6 +390,7 @@ struct ble_gap_event {
 
         /**
          * Represents a received ATT notification or indication.
+         *
          * Valid for the following event types:
          *     o BLE_GAP_EVENT_NOTIFY_RX
          */
@@ -402,8 +418,9 @@ struct ble_gap_event {
 
         /**
          * Represents a transmitted ATT notification or indication, or a
-         * completed indication transaction.  Valid for the following event
-         * types:
+         * completed indication transaction.
+         *
+         * Valid for the following event types:
          *     o BLE_GAP_EVENT_NOTIFY_TX
          */
         struct {
@@ -431,10 +448,47 @@ struct ble_gap_event {
              */
             uint8_t indication:1;
         } notify_tx;
+
+        /**
+         * Represents a state change in a peer's subscription status.  In this
+         * comment, the term "update" is used to refer to either a notification
+         * or an indication.  This event is triggered by any of the following
+         * occurrences:
+         *     o Peer enables or disables updates via a CCCD write.
+         *     o Connection is about to be terminated and the peer is
+         *       subscribed to updates.
+         *     o Peer is now subscribed to updates after its state was restored
+         *       from persistence.  This happens when bonding is restored.
+         *
+         * Valid for the following event types:
+         *     o BLE_GAP_EVENT_SUBSCRIBE
+         */
+        struct {
+            /** The handle of the relevant connection. */
+            uint16_t conn_handle;
+
+            /** The value handle of the relevant characteristic. */
+            uint16_t attr_handle;
+
+            /** One of the BLE_GAP_SUBSCRIBE_REASON codes. */
+            uint8_t reason;
+
+            /** Whether the peer was previously subscribed to notifications. */
+            uint8_t prev_notify:1;
+
+            /** Whether the peer is currently subscribed to notifications. */
+            uint8_t cur_notify:1;
+
+            /** Whether the peer was previously subscribed to indications. */
+            uint8_t prev_indicate:1;
+
+            /** Whether the peer is currently subscribed to indications. */
+            uint8_t cur_indicate:1;
+        } subscribe;
     };
 };
 
-typedef int ble_gap_event_fn(struct ble_gap_event *ctxt, void *arg);
+typedef int ble_gap_event_fn(struct ble_gap_event *event, void *arg);
 
 #define BLE_GAP_CONN_MODE_NON               0
 #define BLE_GAP_CONN_MODE_DIR               1

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/ble_gap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index 204401d..8693d7c 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -726,6 +726,7 @@ ble_gap_conn_broken(uint16_t conn_handle, int reason)
 
     ble_sm_connection_broken(conn_handle);
     ble_gattc_connection_broken(conn_handle);
+    ble_gatts_connection_broken(conn_handle);
 
     ble_hs_atomic_conn_delete(conn_handle);
 
@@ -2843,6 +2844,36 @@ ble_gap_notify_tx_event(int status, uint16_t conn_handle, uint16_t attr_handle,
 }
 
 /*****************************************************************************
+ * $subscribe                                                                *
+ *****************************************************************************/
+
+void
+ble_gap_subscribe_event(uint16_t conn_handle, uint16_t attr_handle,
+                        uint8_t reason,
+                        uint8_t prev_notify, uint8_t cur_notify,
+                        uint8_t prev_indicate, uint8_t cur_indicate)
+{
+    struct ble_gap_event event;
+
+    BLE_HS_DBG_ASSERT(prev_notify != cur_notify ||
+                      prev_indicate != cur_indicate);
+    BLE_HS_DBG_ASSERT(reason == BLE_GAP_SUBSCRIBE_REASON_WRITE ||
+                      reason == BLE_GAP_SUBSCRIBE_REASON_TERM  ||
+                      reason == BLE_GAP_SUBSCRIBE_REASON_RESTORE);
+
+    memset(&event, 0, sizeof event);
+    event.type = BLE_GAP_EVENT_SUBSCRIBE;
+    event.subscribe.conn_handle = conn_handle;
+    event.subscribe.attr_handle = attr_handle;
+    event.subscribe.reason = reason;
+    event.subscribe.prev_notify = !!prev_notify;
+    event.subscribe.cur_notify = !!cur_notify;
+    event.subscribe.prev_indicate = !!prev_indicate;
+    event.subscribe.cur_indicate = !!cur_indicate;
+    ble_gap_call_conn_event_cb(&event, conn_handle);
+}
+
+/*****************************************************************************
  * $init                                                                     *
  *****************************************************************************/
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index b1e4d8b..69d9538 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -87,6 +87,10 @@ void ble_gap_notify_rx_event(uint16_t conn_handle, uint16_t attr_handle,
                              int is_indication);
 void ble_gap_notify_tx_event(int status, uint16_t conn_handle,
                              uint16_t attr_handle, int is_indication);
+void ble_gap_subscribe_event(uint16_t conn_handle, uint16_t attr_handle,
+                             uint8_t reason,
+                             uint8_t prev_notify, uint8_t cur_notify,
+                             uint8_t prev_indicate, uint8_t cur_indicate);
 int ble_gap_master_in_progress(void);
 
 int32_t ble_gap_heartbeat(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/ble_gatt_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt_priv.h b/net/nimble/host/src/ble_gatt_priv.h
index a80a94a..0b1e89f 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -144,9 +144,9 @@ int ble_gatts_rx_indicate_ack(uint16_t conn_handle, uint16_t chr_val_handle);
 int ble_gatts_send_next_indicate(uint16_t conn_handle);
 void ble_gatts_tx_notifications(void);
 void ble_gatts_bonding_restored(uint16_t conn_handle);
+void ble_gatts_connection_broken(uint16_t conn_handle);
 
 /*** @misc. */
-void ble_gatts_conn_deinit(struct ble_gatts_conn *gatts_conn);
 int ble_gatts_conn_can_alloc(void);
 int ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn);
 int ble_gatts_start(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/ble_gatts.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatts.c b/net/nimble/host/src/ble_gatts.c
index 9f96a40..e845f39 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -523,6 +523,22 @@ ble_gatts_clt_cfg_find(struct ble_gatts_clt_cfg *cfgs,
     }
 }
 
+static void
+ble_gatts_subscribe_event(uint16_t conn_handle, uint16_t attr_handle,
+                          uint8_t reason,
+                          uint8_t prev_flags, uint8_t cur_flags)
+{
+    if (prev_flags != cur_flags) {
+        ble_gap_subscribe_event(conn_handle,
+                                attr_handle,
+                                reason,
+                                prev_flags  & BLE_GATTS_CLT_CFG_F_NOTIFY,
+                                cur_flags   & BLE_GATTS_CLT_CFG_F_NOTIFY,
+                                prev_flags  & BLE_GATTS_CLT_CFG_F_INDICATE,
+                                cur_flags   & BLE_GATTS_CLT_CFG_F_INDICATE);
+    }
+}
+
 /**
  * Performs a read or write access on a client characteritic configuration
  * descriptor (CCCD).
@@ -547,7 +563,9 @@ static int
 ble_gatts_clt_cfg_access_locked(struct ble_hs_conn *conn, uint16_t attr_handle,
                                 uint8_t att_op,
                                 struct ble_att_svr_access_ctxt *ctxt,
-                                struct ble_store_value_cccd *out_cccd)
+                                struct ble_store_value_cccd *out_cccd,
+                                uint8_t *out_prev_clt_cfg_flags,
+                                uint8_t *out_cur_clt_cfg_flags)
 {
     struct ble_gatts_clt_cfg *clt_cfg;
     uint16_t chr_val_handle;
@@ -574,6 +592,10 @@ ble_gatts_clt_cfg_access_locked(struct ble_hs_conn *conn, uint16_t attr_handle,
         return BLE_ATT_ERR_UNLIKELY;
     }
 
+    /* Assume no change in flags. */
+    *out_prev_clt_cfg_flags = clt_cfg->flags;
+    *out_cur_clt_cfg_flags = clt_cfg->flags;
+
     gatt_op = ble_gatts_dsc_op(att_op);
     ble_gatts_dsc_inc_stat(gatt_op);
 
@@ -596,15 +618,18 @@ ble_gatts_clt_cfg_access_locked(struct ble_hs_conn *conn, uint16_t attr_handle,
             return BLE_ATT_ERR_WRITE_NOT_PERMITTED;
         }
 
-        clt_cfg->flags = flags;
-
-        /* Successful writes get persisted for bonded connections. */
-        if (conn->bhc_sec_state.bonded) {
-            out_cccd->peer_addr_type = conn->bhc_peer_addr_type;
-            memcpy(out_cccd->peer_addr, conn->bhc_peer_addr, 6);
-            out_cccd->chr_val_handle = chr_val_handle;
-            out_cccd->flags = clt_cfg->flags;
-            out_cccd->value_changed = 0;
+        if (clt_cfg->flags != flags) {
+            clt_cfg->flags = flags;
+            *out_cur_clt_cfg_flags = flags;
+
+            /* Successful writes get persisted for bonded connections. */
+            if (conn->bhc_sec_state.bonded) {
+                out_cccd->peer_addr_type = conn->bhc_peer_addr_type;
+                memcpy(out_cccd->peer_addr, conn->bhc_peer_addr, 6);
+                out_cccd->chr_val_handle = chr_val_handle;
+                out_cccd->flags = clt_cfg->flags;
+                out_cccd->value_changed = 0;
+            }
         }
         break;
 
@@ -625,6 +650,9 @@ ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
     struct ble_store_value_cccd cccd_value;
     struct ble_store_key_cccd cccd_key;
     struct ble_hs_conn *conn;
+    uint16_t chr_val_handle;
+    uint8_t prev_flags;
+    uint8_t cur_flags;
     int rc;
 
     ble_hs_lock();
@@ -634,13 +662,27 @@ ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
         rc = BLE_ATT_ERR_UNLIKELY;
     } else {
         rc = ble_gatts_clt_cfg_access_locked(conn, attr_handle, op, ctxt,
-                                             &cccd_value);
+                                             &cccd_value,
+                                             &prev_flags,
+                                             &cur_flags);
     }
 
     ble_hs_unlock();
 
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* The value attribute is always immediately after the declaration. */
+    chr_val_handle = attr_handle - 1;
+
+    /* Tell the application if the peer changed its subscription state. */
+    ble_gatts_subscribe_event(conn_handle, chr_val_handle,
+                              BLE_GAP_SUBSCRIBE_REASON_WRITE,
+                              prev_flags, cur_flags);
+
     /* Persist the CCCD if required. */
-    if (rc == 0 && cccd_value.chr_val_handle != 0) {
+    if (cccd_value.chr_val_handle != 0) {
         if (cccd_value.flags == 0) {
             ble_store_key_from_value_cccd(&cccd_key, &cccd_value);
             rc = ble_store_delete_cccd(&cccd_key);
@@ -952,23 +994,56 @@ ble_gatts_register_svcs(const struct ble_gatt_svc_def *svcs,
     return 0;
 }
 
+static int
+ble_gatts_clt_cfg_size(void)
+{
+    return ble_gatts_num_cfgable_chrs * sizeof (struct ble_gatts_clt_cfg);
+}
+
+/**
+ * Handles GATT server clean up for a terminated connection:
+ *     o Informs the application that the peer is no longer subscribed to any
+ *       characteristic updates.
+ *     o Frees GATT server resources consumed by the connection (CCCDs).
+ */
 void
-ble_gatts_conn_deinit(struct ble_gatts_conn *gatts_conn)
+ble_gatts_connection_broken(uint16_t conn_handle)
 {
+    struct ble_gatts_clt_cfg *clt_cfgs;
+    struct ble_hs_conn *conn;
+    int num_clt_cfgs;
     int rc;
+    int i;
 
-    if (gatts_conn->clt_cfgs != NULL) {
-        rc = os_memblock_put(&ble_gatts_clt_cfg_pool, gatts_conn->clt_cfgs);
-        BLE_HS_DBG_ASSERT_EVAL(rc == 0);
+    /* Find the specified connection and extract its CCCD entries. */
+    ble_hs_lock();
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn != NULL) {
+        clt_cfgs = conn->bhc_gatt_svr.clt_cfgs;
+        num_clt_cfgs = conn->bhc_gatt_svr.num_clt_cfgs;
 
-        gatts_conn->clt_cfgs = NULL;
+        conn->bhc_gatt_svr.clt_cfgs = NULL;
+        conn->bhc_gatt_svr.num_clt_cfgs = 0;
     }
-}
+    ble_hs_unlock();
 
-static int
-ble_gatts_clt_cfg_size(void)
-{
-    return ble_gatts_num_cfgable_chrs * sizeof (struct ble_gatts_clt_cfg);
+    if (conn == NULL) {
+        return;
+    }
+
+    /* Now that the mutex is unlocked, inform the application that the peer is
+     * no longer subscribed to any characteristic updates.
+     */
+    if (clt_cfgs != NULL) {
+        for (i = 0; i < num_clt_cfgs; i++) {
+            ble_gatts_subscribe_event(conn_handle, clt_cfgs[i].chr_val_handle,
+                                      BLE_GAP_SUBSCRIBE_REASON_TERM,
+                                      clt_cfgs[i].flags, 0);
+        }
+
+        rc = os_memblock_put(&ble_gatts_clt_cfg_pool, clt_cfgs);
+        BLE_HS_DBG_ASSERT_EVAL(rc == 0);
+    }
 }
 
 int
@@ -1035,16 +1110,19 @@ int
 ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn)
 {
     if (ble_gatts_num_cfgable_chrs > 0) {
-        ble_gatts_conn_deinit(gatts_conn);
         gatts_conn->clt_cfgs = os_memblock_get(&ble_gatts_clt_cfg_pool);
         if (gatts_conn->clt_cfgs == NULL) {
             return BLE_HS_ENOMEM;
         }
-    }
 
-    /* Initialize the client configuration with a copy of the cache. */
-    memcpy(gatts_conn->clt_cfgs, ble_gatts_clt_cfgs, ble_gatts_clt_cfg_size());
-    gatts_conn->num_clt_cfgs = ble_gatts_num_cfgable_chrs;
+        /* Initialize the client configuration with a copy of the cache. */
+        memcpy(gatts_conn->clt_cfgs, ble_gatts_clt_cfgs,
+               ble_gatts_clt_cfg_size());
+        gatts_conn->num_clt_cfgs = ble_gatts_num_cfgable_chrs;
+    } else {
+        gatts_conn->clt_cfgs = NULL;
+        gatts_conn->num_clt_cfgs = 0;
+    }
 
     return 0;
 }
@@ -1460,6 +1538,7 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
                                          cccd_value.chr_val_handle);
         if (clt_cfg != NULL) {
             clt_cfg->flags = cccd_value.flags;
+
             if (cccd_value.value_changed) {
                 /* The characteristic's value changed while the device was
                  * disconnected or unbonded.  Schedule the notification or
@@ -1472,6 +1551,13 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
 
         ble_hs_unlock();
 
+        /* Tell the application if the peer changed its subscription state
+         * when it was restored from persistence.
+         */
+        ble_gatts_subscribe_event(conn_handle, cccd_value.chr_val_handle,
+                                  BLE_GAP_SUBSCRIBE_REASON_RESTORE,
+                                  0, cccd_value.flags);
+
         switch (att_op) {
         case 0:
             break;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/ble_hs_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.c b/net/nimble/host/src/ble_hs_conn.c
index cd9f4b7..164cc6d 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -188,8 +188,6 @@ ble_hs_conn_free(struct ble_hs_conn *conn)
         return;
     }
 
-    ble_gatts_conn_deinit(&conn->bhc_gatt_svr);
-
     ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
 
     while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c8a4d68/net/nimble/host/src/test/ble_gatts_notify_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatts_notify_test.c b/net/nimble/host/src/test/ble_gatts_notify_test.c
index 6e5071b..c78eb55 100644
--- a/net/nimble/host/src/test/ble_gatts_notify_test.c
+++ b/net/nimble/host/src/test/ble_gatts_notify_test.c
@@ -29,6 +29,8 @@
 #define BLE_GATTS_NOTIFY_TEST_CHR_1_UUID    0x1111
 #define BLE_GATTS_NOTIFY_TEST_CHR_2_UUID    0x2222
 
+#define BLE_GATTS_NOTIFY_TEST_MAX_EVENTS    16
+
 static uint8_t ble_gatts_notify_test_peer_addr[6] = {2,3,4,5,6,7};
 
 static int
@@ -68,10 +70,34 @@ static uint16_t ble_gatts_notify_test_chr_2_def_handle;
 static uint8_t ble_gatts_notify_test_chr_2_val[1024];
 static int ble_gatts_notify_test_chr_2_len;
 
+static struct ble_gap_event
+ble_gatts_notify_test_events[BLE_GATTS_NOTIFY_TEST_MAX_EVENTS];
+
+static int ble_gatts_notify_test_num_events;
+
 typedef int ble_store_write_fn(int obj_type, union ble_store_value *val);
 
 typedef int ble_store_delete_fn(int obj_type, union ble_store_key *key);
 
+static int
+ble_gatts_notify_test_util_gap_event(struct ble_gap_event *event, void *arg)
+{
+    switch (event->type) {
+    case BLE_GAP_EVENT_NOTIFY_TX:
+    case BLE_GAP_EVENT_SUBSCRIBE:
+        TEST_ASSERT_FATAL(ble_gatts_notify_test_num_events <
+                          BLE_GATTS_NOTIFY_TEST_MAX_EVENTS);
+
+        ble_gatts_notify_test_events[ble_gatts_notify_test_num_events++] =
+            *event;
+
+    default:
+        break;
+    }
+
+    return 0;
+}
+
 static uint16_t
 ble_gatts_notify_test_misc_read_notify(uint16_t conn_handle,
                                        uint16_t chr_def_handle)
@@ -119,6 +145,73 @@ ble_gatts_notify_test_misc_enable_notify(uint16_t conn_handle,
 }
 
 static void
+ble_gatts_notify_test_util_next_event(struct ble_gap_event *event)
+{
+    TEST_ASSERT_FATAL(ble_gatts_notify_test_num_events > 0);
+
+    *event = *ble_gatts_notify_test_events;
+
+    ble_gatts_notify_test_num_events--;
+    if (ble_gatts_notify_test_num_events > 0) {
+        memmove(ble_gatts_notify_test_events + 0,
+                ble_gatts_notify_test_events + 1,
+                ble_gatts_notify_test_num_events * sizeof *event);
+    }
+}
+
+static void
+ble_gatts_notify_test_util_verify_sub_event(uint16_t conn_handle,
+                                            uint8_t attr_handle,
+                                            uint8_t reason,
+                                            uint8_t prevn, uint8_t curn,
+                                            uint8_t previ, uint8_t curi)
+{
+    struct ble_gap_event event;
+
+    ble_gatts_notify_test_util_next_event(&event);
+
+    TEST_ASSERT(event.type == BLE_GAP_EVENT_SUBSCRIBE);
+    TEST_ASSERT(event.subscribe.conn_handle == conn_handle);
+    TEST_ASSERT(event.subscribe.attr_handle == attr_handle);
+    TEST_ASSERT(event.subscribe.reason == reason);
+    TEST_ASSERT(event.subscribe.prev_notify == prevn);
+    TEST_ASSERT(event.subscribe.cur_notify == curn);
+    TEST_ASSERT(event.subscribe.prev_indicate == previ);
+    TEST_ASSERT(event.subscribe.cur_indicate == curi);
+}
+
+static void
+ble_gatts_notify_test_util_verify_tx_event(uint16_t conn_handle,
+                                           uint8_t attr_handle,
+                                           int indication)
+{
+    struct ble_gap_event event;
+
+    ble_gatts_notify_test_util_next_event(&event);
+
+    TEST_ASSERT(event.type == BLE_GAP_EVENT_NOTIFY_TX);
+    TEST_ASSERT(event.notify_tx.status == 0);
+    TEST_ASSERT(event.notify_tx.conn_handle == conn_handle);
+    TEST_ASSERT(event.notify_tx.attr_handle == attr_handle);
+    TEST_ASSERT(event.notify_tx.indication == indication);
+}
+
+static void
+ble_gatts_notify_test_util_verify_ack_event(uint16_t conn_handle,
+                                            uint8_t attr_handle)
+{
+    struct ble_gap_event event;
+
+    ble_gatts_notify_test_util_next_event(&event);
+
+    TEST_ASSERT(event.type == BLE_GAP_EVENT_NOTIFY_TX);
+    TEST_ASSERT(event.notify_tx.status == BLE_HS_EDONE);
+    TEST_ASSERT(event.notify_tx.conn_handle == conn_handle);
+    TEST_ASSERT(event.notify_tx.attr_handle == attr_handle);
+    TEST_ASSERT(event.notify_tx.indication == 1);
+}
+
+static void
 ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding,
                                 uint16_t chr1_flags, uint16_t chr2_flags)
 {
@@ -129,6 +222,8 @@ ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding,
 
     ble_hs_test_util_init();
 
+    ble_gatts_notify_test_num_events = 0;
+
     ble_hs_test_util_store_init(10, 10, 10);
     ble_hs_cfg.store_read_cb = ble_hs_test_util_store_read;
     ble_hs_cfg.store_write_cb = ble_hs_test_util_store_write;
@@ -142,7 +237,7 @@ ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding,
     ble_gatts_start();
 
     ble_hs_test_util_create_conn(2, ble_gatts_notify_test_peer_addr,
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
     *out_conn_handle = 2;
 
     if (bonding) {
@@ -163,16 +258,35 @@ ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding,
         2, ble_gatts_notify_test_chr_2_def_handle);
     TEST_ASSERT(flags == 0);
 
-    /* Set initial notification / indication state. */
+    /* Set initial notification / indication state and verify that subscription
+     * callback gets executed.
+     */
     if (chr1_flags != 0) {
         ble_gatts_notify_test_misc_enable_notify(
             2, ble_gatts_notify_test_chr_1_def_handle, chr1_flags);
+
+        ble_gatts_notify_test_util_verify_sub_event(
+            *out_conn_handle,
+            ble_gatts_notify_test_chr_1_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_WRITE,
+            0, chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY,
+            0, chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE);
     }
     if (chr2_flags != 0) {
         ble_gatts_notify_test_misc_enable_notify(
             2, ble_gatts_notify_test_chr_2_def_handle, chr2_flags);
+
+        ble_gatts_notify_test_util_verify_sub_event(
+            *out_conn_handle,
+            ble_gatts_notify_test_chr_2_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_WRITE,
+            0, chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY,
+            0, chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE);
     }
 
+    /* Ensure no extraneous subscription callbacks were executed. */
+    TEST_ASSERT(ble_gatts_notify_test_num_events == 0);
+
     /* Toss both write responses. */
     ble_hs_test_util_prev_tx_queue_clear();
 
@@ -185,24 +299,40 @@ ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding,
     TEST_ASSERT(flags == chr2_flags);
 
     /* Ensure both CCCDs still persisted. */
-    exp_num_cccds = (chr1_flags != 0) + (chr2_flags != 0);
+    if (bonding) {
+        exp_num_cccds = (chr1_flags != 0) + (chr2_flags != 0);
+    } else {
+        exp_num_cccds = 0;
+    }
     TEST_ASSERT(ble_hs_test_util_store_num_cccds == exp_num_cccds);
 }
 
 static void
-ble_gatts_notify_test_restore_bonding(uint16_t conn_handle)
+ble_gatts_notify_test_disconnect(uint16_t conn_handle,
+                                 uint8_t chr1_flags, uint8_t chr2_flags)
 {
-    struct ble_hs_conn *conn;
+    ble_hs_test_util_conn_disconnect(conn_handle);
 
-    ble_hs_lock();
-    conn = ble_hs_conn_find(conn_handle);
-    TEST_ASSERT_FATAL(conn != NULL);
-    conn->bhc_sec_state.encrypted = 1;
-    conn->bhc_sec_state.authenticated = 1;
-    conn->bhc_sec_state.bonded = 1;
-    ble_hs_unlock();
+    /* Verify subscription callback executed for each subscribed
+     * characteristic.
+     */
+    if (chr1_flags != 0) {
+        ble_gatts_notify_test_util_verify_sub_event(
+            conn_handle,
+            ble_gatts_notify_test_chr_1_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_TERM,
+            chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, 0,
+            chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE, 0);
+    }
 
-    ble_gatts_bonding_restored(conn_handle);
+    if (chr2_flags != 0) {
+        ble_gatts_notify_test_util_verify_sub_event(
+            conn_handle,
+            ble_gatts_notify_test_chr_2_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_TERM,
+            chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, 0,
+            chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE, 0);
+    }
 }
 
 static void
@@ -256,7 +386,8 @@ ble_gatts_notify_test_misc_access(uint16_t conn_handle,
 }
 
 static void
-ble_gatts_notify_test_misc_rx_indicate_rsp(uint16_t conn_handle)
+ble_gatts_notify_test_misc_rx_indicate_rsp(uint16_t conn_handle,
+                                           uint16_t attr_handle)
 {
     uint8_t buf[BLE_ATT_INDICATE_RSP_SZ];
     int rc;
@@ -266,11 +397,15 @@ ble_gatts_notify_test_misc_rx_indicate_rsp(uint16_t conn_handle)
     rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
                                                 buf, sizeof buf);
     TEST_ASSERT(rc == 0);
+
+    ble_gatts_notify_test_util_verify_ack_event(conn_handle, attr_handle);
 }
 
 
 static void
-ble_gatts_notify_test_misc_verify_tx_n(uint8_t *attr_data, int attr_len)
+ble_gatts_notify_test_misc_verify_tx_n(uint16_t conn_handle,
+                                       uint16_t attr_handle,
+                                       uint8_t *attr_data, int attr_len)
 {
     struct ble_att_notify_req req;
     struct os_mbuf *om;
@@ -282,15 +417,20 @@ ble_gatts_notify_test_misc_verify_tx_n(uint8_t *attr_data, int attr_len)
     TEST_ASSERT_FATAL(om != NULL);
 
     ble_att_notify_req_parse(om->om_data, om->om_len, &req);
+    TEST_ASSERT(req.banq_handle == attr_handle);
 
     for (i = 0; i < attr_len; i++) {
         TEST_ASSERT(om->om_data[BLE_ATT_NOTIFY_REQ_BASE_SZ + i] ==
                     attr_data[i]);
     }
+
+    ble_gatts_notify_test_util_verify_tx_event(conn_handle, attr_handle, 0);
 }
 
 static void
-ble_gatts_notify_test_misc_verify_tx_i(uint8_t *attr_data, int attr_len)
+ble_gatts_notify_test_misc_verify_tx_i(uint16_t conn_handle,
+                                       uint16_t attr_handle,
+                                       uint8_t *attr_data, int attr_len)
 {
     struct ble_att_indicate_req req;
     struct os_mbuf *om;
@@ -302,33 +442,117 @@ ble_gatts_notify_test_misc_verify_tx_i(uint8_t *attr_data, int attr_len)
     TEST_ASSERT_FATAL(om != NULL);
 
     ble_att_indicate_req_parse(om->om_data, om->om_len, &req);
+    TEST_ASSERT(req.baiq_handle == attr_handle);
 
     for (i = 0; i < attr_len; i++) {
         TEST_ASSERT(om->om_data[BLE_ATT_INDICATE_REQ_BASE_SZ + i] ==
                     attr_data[i]);
     }
+
+    ble_gatts_notify_test_util_verify_tx_event(conn_handle, attr_handle, 1);
 }
 
-TEST_CASE(ble_gatts_notify_test_n)
+static void
+ble_gatts_notify_test_misc_verify_tx_gen(uint16_t conn_handle, int attr_idx,
+                                         uint8_t chr_flags)
 {
-    uint16_t conn_handle;
-    uint16_t flags;
+    uint16_t attr_handle;
+    uint16_t attr_len;
+    void *attr_val;
+
+    switch (attr_idx) {
+    case 1:
+        attr_handle = ble_gatts_notify_test_chr_1_def_handle + 1;
+        attr_len = ble_gatts_notify_test_chr_1_len;
+        attr_val = ble_gatts_notify_test_chr_1_val;
+        break;
+
+    case 2:
+        attr_handle = ble_gatts_notify_test_chr_2_def_handle + 1;
+        attr_len = ble_gatts_notify_test_chr_2_len;
+        attr_val = ble_gatts_notify_test_chr_2_val;
+        break;
+
+    default:
+        TEST_ASSERT_FATAL(0);
+        break;
+    }
 
-    ble_gatts_notify_test_misc_init(&conn_handle, 0, 0, 0);
+    switch (chr_flags) {
+    case 0:
+        break;
 
-    /* Enable notifications on both characteristics. */
-    ble_gatts_notify_test_misc_enable_notify(
-        conn_handle, ble_gatts_notify_test_chr_1_def_handle,
-        BLE_GATTS_CLT_CFG_F_NOTIFY);
-    ble_gatts_notify_test_misc_enable_notify(
-        conn_handle, ble_gatts_notify_test_chr_2_def_handle,
-        BLE_GATTS_CLT_CFG_F_NOTIFY);
+    case BLE_GATTS_CLT_CFG_F_NOTIFY:
+        ble_gatts_notify_test_misc_verify_tx_n(conn_handle, attr_handle,
+                                               attr_val, attr_len);
+        break;
 
-    /* Toss both write responses. */
-    ble_hs_test_util_prev_tx_queue_clear();
+    case BLE_GATTS_CLT_CFG_F_INDICATE:
+        ble_gatts_notify_test_misc_verify_tx_i(conn_handle, attr_handle,
+                                               attr_val, attr_len);
+        break;
+
+    default:
+        TEST_ASSERT_FATAL(0);
+        break;
+    }
+}
+
+static void
+ble_gatts_notify_test_restore_bonding(uint16_t conn_handle,
+                                      uint8_t chr1_flags, uint8_t chr1_tx,
+                                      uint8_t chr2_flags, uint8_t chr2_tx)
+{
+    struct ble_hs_conn *conn;
+
+    ble_hs_lock();
+    conn = ble_hs_conn_find(conn_handle);
+    TEST_ASSERT_FATAL(conn != NULL);
+    conn->bhc_sec_state.encrypted = 1;
+    conn->bhc_sec_state.authenticated = 1;
+    conn->bhc_sec_state.bonded = 1;
+    ble_hs_unlock();
+
+    ble_gatts_bonding_restored(conn_handle);
+
+    /* Verify subscription callback executed for each subscribed
+     * characteristic.
+     */
+    if (chr1_flags != 0) {
+        ble_gatts_notify_test_util_verify_sub_event(
+            conn_handle,
+            ble_gatts_notify_test_chr_1_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_RESTORE,
+            0, chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY,
+            0, chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE);
 
-    /* Ensure nothing got persisted since peer is not bonded. */
-    TEST_ASSERT(ble_hs_test_util_store_num_cccds == 0);
+    }
+    if (chr1_tx) {
+        ble_gatts_notify_test_misc_verify_tx_gen(conn_handle, 1, chr1_flags);
+    }
+
+
+    if (chr2_flags != 0) {
+        ble_gatts_notify_test_util_verify_sub_event(
+            conn_handle,
+            ble_gatts_notify_test_chr_2_def_handle + 1,
+            BLE_GAP_SUBSCRIBE_REASON_RESTORE,
+            0, chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY,
+            0, chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE);
+    }
+    if (chr2_tx) {
+        ble_gatts_notify_test_misc_verify_tx_gen(conn_handle, 2, chr2_flags);
+    }
+}
+
+TEST_CASE(ble_gatts_notify_test_n)
+{
+    uint16_t conn_handle;
+    uint16_t flags;
+
+    ble_gatts_notify_test_misc_init(&conn_handle, 0,
+                                    BLE_GATTS_CLT_CFG_F_NOTIFY,
+                                    BLE_GATTS_CLT_CFG_F_NOTIFY);
 
     /* Ensure notifications read back as enabled. */
     flags = ble_gatts_notify_test_misc_read_notify(
@@ -344,8 +568,11 @@ TEST_CASE(ble_gatts_notify_test_n)
     ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1);
 
     /* Verify notification sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_n(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
+    ble_gatts_notify_test_misc_verify_tx_n(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1,
+        ble_gatts_notify_test_chr_1_val,
+        ble_gatts_notify_test_chr_1_len);
 
     /* Update characteristic 2's value. */
     ble_gatts_notify_test_chr_2_len = 16;
@@ -354,15 +581,20 @@ TEST_CASE(ble_gatts_notify_test_n)
     ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1);
 
     /* Verify notification sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_n(ble_gatts_notify_test_chr_2_val,
-                                           ble_gatts_notify_test_chr_2_len);
+    ble_gatts_notify_test_misc_verify_tx_n(
+        conn_handle,
+        ble_gatts_notify_test_chr_2_def_handle + 1,
+        ble_gatts_notify_test_chr_2_val,
+        ble_gatts_notify_test_chr_2_len);
 
     /***
      * Disconnect, modify characteristic values, and reconnect.  Ensure
      * notifications are not sent and are no longer enabled.
      */
 
-    ble_hs_test_util_conn_disconnect(conn_handle);
+    ble_gatts_notify_test_disconnect(conn_handle,
+                                     BLE_GATTS_CLT_CFG_F_NOTIFY,
+                                     BLE_GATTS_CLT_CFG_F_NOTIFY);
 
     /* Update characteristic 1's value. */
     ble_gatts_notify_test_chr_1_len = 1;
@@ -376,7 +608,7 @@ TEST_CASE(ble_gatts_notify_test_n)
     ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1);
 
     ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
 
     /* Ensure no notifications sent. */
     TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL);
@@ -395,29 +627,9 @@ TEST_CASE(ble_gatts_notify_test_i)
     uint16_t conn_handle;
     uint16_t flags;
 
-    ble_gatts_notify_test_misc_init(&conn_handle, 0, 0, 0);
-
-    /* Enable indications on both characteristics. */
-    ble_gatts_notify_test_misc_enable_notify(
-        conn_handle, ble_gatts_notify_test_chr_1_def_handle,
-        BLE_GATTS_CLT_CFG_F_INDICATE);
-    ble_gatts_notify_test_misc_enable_notify(
-        conn_handle, ble_gatts_notify_test_chr_2_def_handle,
-        BLE_GATTS_CLT_CFG_F_INDICATE);
-
-    /* Toss both write responses. */
-    ble_hs_test_util_prev_tx_queue_clear();
-
-    /* Ensure nothing got persisted since peer is not bonded. */
-    TEST_ASSERT(ble_hs_test_util_store_num_cccds == 0);
-
-    /* Ensure indications read back as enabled. */
-    flags = ble_gatts_notify_test_misc_read_notify(
-        conn_handle, ble_gatts_notify_test_chr_1_def_handle);
-    TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_INDICATE);
-    flags = ble_gatts_notify_test_misc_read_notify(
-        conn_handle, ble_gatts_notify_test_chr_2_def_handle);
-    TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_INDICATE);
+    ble_gatts_notify_test_misc_init(&conn_handle, 0,
+                                    BLE_GATTS_CLT_CFG_F_INDICATE,
+                                    BLE_GATTS_CLT_CFG_F_INDICATE);
 
     /* Update characteristic 1's value. */
     ble_gatts_notify_test_chr_1_len = 1;
@@ -425,8 +637,11 @@ TEST_CASE(ble_gatts_notify_test_i)
     ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1);
 
     /* Verify indication sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
+    ble_gatts_notify_test_misc_verify_tx_i(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1,
+        ble_gatts_notify_test_chr_1_val,
+        ble_gatts_notify_test_chr_1_len);
 
     /* Update characteristic 2's value. */
     ble_gatts_notify_test_chr_2_len = 16;
@@ -441,15 +656,22 @@ TEST_CASE(ble_gatts_notify_test_i)
     TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0);
 
     /* Receive the confirmation for the first indication. */
-    ble_gatts_notify_test_misc_rx_indicate_rsp(conn_handle);
+    ble_gatts_notify_test_misc_rx_indicate_rsp(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1);
 
     /* Verify indication sent properly. */
     ble_hs_test_util_tx_all();
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_2_val,
-                                           ble_gatts_notify_test_chr_2_len);
+    ble_gatts_notify_test_misc_verify_tx_i(
+        conn_handle,
+        ble_gatts_notify_test_chr_2_def_handle + 1,
+        ble_gatts_notify_test_chr_2_val,
+        ble_gatts_notify_test_chr_2_len);
 
     /* Receive the confirmation for the second indication. */
-    ble_gatts_notify_test_misc_rx_indicate_rsp(conn_handle);
+    ble_gatts_notify_test_misc_rx_indicate_rsp(
+        conn_handle,
+        ble_gatts_notify_test_chr_2_def_handle + 1);
 
     /* Verify no pending GATT jobs. */
     TEST_ASSERT(!ble_gattc_any_jobs());
@@ -459,7 +681,9 @@ TEST_CASE(ble_gatts_notify_test_i)
      * indications are not sent and are no longer enabled.
      */
 
-    ble_hs_test_util_conn_disconnect(conn_handle);
+    ble_gatts_notify_test_disconnect(conn_handle,
+                                     BLE_GATTS_CLT_CFG_F_INDICATE,
+                                     BLE_GATTS_CLT_CFG_F_INDICATE);
 
     /* Update characteristic 1's value. */
     ble_gatts_notify_test_chr_1_len = 1;
@@ -473,7 +697,7 @@ TEST_CASE(ble_gatts_notify_test_i)
     ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1);
 
     ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
 
     /* Ensure no indications sent. */
     TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL);
@@ -497,7 +721,9 @@ TEST_CASE(ble_gatts_notify_test_bonded_n)
                                     BLE_GATTS_CLT_CFG_F_NOTIFY);
 
     /* Disconnect. */
-    ble_hs_test_util_conn_disconnect(conn_handle);
+    ble_gatts_notify_test_disconnect(conn_handle,
+                                     BLE_GATTS_CLT_CFG_F_NOTIFY,
+                                     BLE_GATTS_CLT_CFG_F_NOTIFY);
 
     /* Ensure both CCCDs still persisted. */
     TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2);
@@ -518,8 +744,9 @@ TEST_CASE(ble_gatts_notify_test_bonded_n)
      */
 
     ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
 
+    ble_gatts_notify_test_num_events = 0;
     /* Ensure no notifications sent. */
     TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL);
 
@@ -532,13 +759,9 @@ TEST_CASE(ble_gatts_notify_test_bonded_n)
     TEST_ASSERT(flags == 0);
 
     /* Simulate a successful encryption procedure (bonding restoration). */
-    ble_gatts_notify_test_restore_bonding(conn_handle);
-
-    /* Verify notifications sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_n(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
-    ble_gatts_notify_test_misc_verify_tx_n(ble_gatts_notify_test_chr_2_val,
-                                           ble_gatts_notify_test_chr_2_len);
+    ble_gatts_notify_test_restore_bonding(conn_handle,
+                                          BLE_GATTS_CLT_CFG_F_NOTIFY, 1,
+                                          BLE_GATTS_CLT_CFG_F_NOTIFY, 1);
 
     /* Ensure notifications enabled. */
     flags = ble_gatts_notify_test_misc_read_notify(
@@ -562,7 +785,9 @@ TEST_CASE(ble_gatts_notify_test_bonded_i)
                                     BLE_GATTS_CLT_CFG_F_INDICATE);
 
     /* Disconnect. */
-    ble_hs_test_util_conn_disconnect(conn_handle);
+    ble_gatts_notify_test_disconnect(conn_handle,
+                                     BLE_GATTS_CLT_CFG_F_INDICATE,
+                                     BLE_GATTS_CLT_CFG_F_INDICATE);
 
     /* Ensure both CCCDs still persisted. */
     TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2);
@@ -583,7 +808,7 @@ TEST_CASE(ble_gatts_notify_test_bonded_i)
      */
 
     ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
 
     /* Ensure no indications sent. */
     TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL);
@@ -597,11 +822,9 @@ TEST_CASE(ble_gatts_notify_test_bonded_i)
     TEST_ASSERT(flags == 0);
 
     /* Simulate a successful encryption procedure (bonding restoration). */
-    ble_gatts_notify_test_restore_bonding(conn_handle);
-
-    /* Verify first indication sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
+    ble_gatts_notify_test_restore_bonding(conn_handle,
+                                          BLE_GATTS_CLT_CFG_F_INDICATE, 1,
+                                          BLE_GATTS_CLT_CFG_F_INDICATE, 0);
 
     /* Verify the second indication doesn't get sent until the first is
      * confirmed.
@@ -610,15 +833,22 @@ TEST_CASE(ble_gatts_notify_test_bonded_i)
     TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0);
 
     /* Receive the confirmation for the first indication. */
-    ble_gatts_notify_test_misc_rx_indicate_rsp(conn_handle);
+    ble_gatts_notify_test_misc_rx_indicate_rsp(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1);
 
     /* Verify indication sent properly. */
     ble_hs_test_util_tx_all();
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_2_val,
-                                           ble_gatts_notify_test_chr_2_len);
+    ble_gatts_notify_test_misc_verify_tx_i(
+        conn_handle,
+        ble_gatts_notify_test_chr_2_def_handle + 1,
+        ble_gatts_notify_test_chr_2_val,
+        ble_gatts_notify_test_chr_2_len);
 
     /* Receive the confirmation for the second indication. */
-    ble_gatts_notify_test_misc_rx_indicate_rsp(conn_handle);
+    ble_gatts_notify_test_misc_rx_indicate_rsp(
+        conn_handle,
+        ble_gatts_notify_test_chr_2_def_handle + 1);
 
     /* Verify no pending GATT jobs. */
     TEST_ASSERT(!ble_gattc_any_jobs());
@@ -653,8 +883,11 @@ TEST_CASE(ble_gatts_notify_test_bonded_i_no_ack)
 
     /* Verify indication sent properly. */
     ble_hs_test_util_tx_all();
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
+    ble_gatts_notify_test_misc_verify_tx_i(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1,
+        ble_gatts_notify_test_chr_1_val,
+        ble_gatts_notify_test_chr_1_len);
 
     /* Verify 'updated' state is still persisted. */
     key_cccd.peer_addr_type = BLE_STORE_ADDR_TYPE_NONE;
@@ -666,24 +899,25 @@ TEST_CASE(ble_gatts_notify_test_bonded_i_no_ack)
     TEST_ASSERT(value_cccd.value_changed);
 
     /* Disconnect. */
-    ble_hs_test_util_conn_disconnect(conn_handle);
+    ble_gatts_notify_test_disconnect(conn_handle,
+                                     BLE_GATTS_CLT_CFG_F_INDICATE, 0);
 
     /* Ensure CCCD still persisted. */
     TEST_ASSERT(ble_hs_test_util_store_num_cccds == 1);
 
     /* Reconnect. */
     ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                 NULL, NULL);
+                                 ble_gatts_notify_test_util_gap_event, NULL);
 
     /* Simulate a successful encryption procedure (bonding restoration). */
-    ble_gatts_notify_test_restore_bonding(conn_handle);
-
-    /* Verify indication sent properly. */
-    ble_gatts_notify_test_misc_verify_tx_i(ble_gatts_notify_test_chr_1_val,
-                                           ble_gatts_notify_test_chr_1_len);
+    ble_gatts_notify_test_restore_bonding(conn_handle,
+                                          BLE_GATTS_CLT_CFG_F_INDICATE, 1,
+                                          0, 0);
 
     /* Receive the confirmation for the indication. */
-    ble_gatts_notify_test_misc_rx_indicate_rsp(conn_handle);
+    ble_gatts_notify_test_misc_rx_indicate_rsp(
+        conn_handle,
+        ble_gatts_notify_test_chr_1_def_handle + 1);
 
     /* Verify no pending GATT jobs. */
     TEST_ASSERT(!ble_gattc_any_jobs());