1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "utils.h"
8 #include "gatt_utils.h"
9 
10 #include <zephyr/bluetooth/addr.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/conn.h>
13 #include <zephyr/settings/settings.h>
14 #include <zephyr/toolchain.h>
15 
16 #include <stdint.h>
17 #include <string.h>
18 
client_round_0(void)19 void client_round_0(void)
20 {
21 	struct bt_conn *conn;
22 
23 	printk("start round 0...........\n");
24 
25 	conn = connect_as_peripheral();
26 	printk("connected: conn %p\n", conn);
27 
28 	gatt_discover();
29 	activate_robust_caching();
30 	/* subscribe to the SC indication, so we don't have to ATT read to
31 	 * become change-aware.
32 	 */
33 	gatt_subscribe_to_service_changed(true);
34 	read_test_char(true);
35 
36 	/* We should normally wait until we are bonded to write the CCC / CF
37 	 * characteristics, but here we bond after the fact on purpose, to
38 	 * simulate a client that has this exact behavior.
39 	 * The CCC and CF should still persist on reboot.
40 	 */
41 	wait_bonded();
42 
43 	disconnect(conn);
44 }
45 
client_round_1(void)46 void client_round_1(void)
47 {
48 	struct bt_conn *conn;
49 
50 	printk("start round 1...........\n");
51 
52 	conn = connect_as_peripheral();
53 	printk("connected: conn %p\n", conn);
54 	wait_secured();
55 
56 	/* server should remember we are change-aware */
57 	read_test_char(true);
58 
59 	disconnect(conn);
60 }
61 
client_round_2(void)62 void client_round_2(void)
63 {
64 	struct bt_conn *conn;
65 
66 	printk("start round 2...........\n");
67 
68 	conn = connect_as_peripheral();
69 	printk("connected: conn %p\n", conn);
70 	wait_secured();
71 
72 	/* We are change-unaware. wait until the Service Changed indication is
73 	 * received, that should then make us change-aware.
74 	 */
75 	wait_for_sc_indication();
76 	read_test_char(true);
77 
78 	/* We sleep just enough so that the server's `delayed store` work item
79 	 * is executed. We still trigger a disconnect, even though the server
80 	 * device will be unresponsive for this round.
81 	 */
82 	k_sleep(K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
83 
84 	disconnect(conn);
85 }
86 
client_round_3(void)87 void client_round_3(void)
88 {
89 	struct bt_conn *conn;
90 
91 	printk("start round 3...........\n");
92 
93 	conn = connect_as_peripheral();
94 	printk("connected: conn %p\n", conn);
95 	wait_secured();
96 
97 	/* server should remember we are change-aware */
98 	read_test_char(true);
99 
100 	/* Unsubscribe from the SC indication.
101 	 *
102 	 * In the next round, we will be change-unaware, so the first ATT read
103 	 * will fail, but the second one will succeed and we will be marked as
104 	 * change-aware again.
105 	 */
106 	gatt_subscribe_to_service_changed(false);
107 
108 	disconnect(conn);
109 }
110 
client_round_4(void)111 void client_round_4(void)
112 {
113 	struct bt_conn *conn;
114 
115 	printk("start round 4...........\n");
116 
117 	conn = connect_as_peripheral();
118 	printk("connected: conn %p\n", conn);
119 	wait_secured();
120 
121 	/* GATT DB has changed again.
122 	 * Before disc: svc1
123 	 * After disc: svc1 + svc2
124 	 * At boot: svc1
125 	 * Expect a failure on the first read of the same GATT handle.
126 	 */
127 	read_test_char(false);
128 	read_test_char(true);
129 
130 	disconnect(conn);
131 }
132 
client_round_5(void)133 void client_round_5(void)
134 {
135 	printk("start round 5...........\n");
136 	printk("don't need to do anything, central will "
137 	       "not connect to us\n");
138 }
139 
client_round_6(void)140 void client_round_6(void)
141 {
142 	struct bt_conn *conn;
143 
144 	printk("start round 6...........\n");
145 	conn = connect_as_peripheral();
146 	printk("connected: conn %p\n", conn);
147 	wait_secured();
148 
149 	/* GATT DB has changed again.
150 	 * Expect a failure on the first read of the same GATT handle.
151 	 */
152 	read_test_char(false);
153 	read_test_char(true);
154 
155 	disconnect(conn);
156 }
157 
client_procedure(void)158 void client_procedure(void)
159 {
160 	bt_enable(NULL);
161 	settings_load();
162 
163 	client_round_0();
164 	client_round_1();
165 	client_round_2();
166 	client_round_3();
167 	client_round_4();
168 	client_round_5();
169 	client_round_6();
170 
171 	PASS("PASS\n");
172 }
173