1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "utils.h"
8 #include "main.h"
9 #include "gatt_utils.h"
10 
11 #include <zephyr/bluetooth/addr.h>
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/bluetooth/conn.h>
14 #include <zephyr/settings/settings.h>
15 #include <zephyr/toolchain.h>
16 
17 #include <stdint.h>
18 #include <string.h>
19 
set_public_addr(void)20 void set_public_addr(void)
21 {
22 	bt_addr_le_t addr = {BT_ADDR_LE_RANDOM,
23 			     {{0x0A, 0x89, 0x67, 0x45, 0x23, 0xC1}}};
24 	bt_id_create(&addr, NULL);
25 }
26 
server_round_0(void)27 void server_round_0(void)
28 {
29 	struct bt_conn *conn;
30 
31 	conn = connect_as_central();
32 	wait_for_client_read();
33 
34 	printk("bonding\n");
35 	bond(conn);
36 }
37 
server_round_1(void)38 void server_round_1(void)
39 {
40 	struct bt_conn *conn;
41 
42 	/* Wait for GATT DB hash to complete. */
43 	k_sleep(K_SECONDS(2));
44 	conn = connect_as_central();
45 	printk("encrypting\n");
46 	set_security(conn, BT_SECURITY_L2);
47 
48 	wait_for_client_read();
49 	wait_disconnected();
50 
51 	printk("register second service, peer will be change-unaware\n");
52 	gatt_register_service_2();
53 	/* on-disk hash will be different when round 2 (and round 4)
54 	 * start, the peer will be marked as change-unaware
55 	 */
56 	k_sleep(K_MSEC(100));
57 }
58 
server_round_2(void)59 void server_round_2(void)
60 {
61 	struct bt_conn *conn;
62 
63 	conn = connect_as_central();
64 	printk("encrypting\n");
65 	set_security(conn, BT_SECURITY_L2);
66 
67 	wait_for_client_read();
68 
69 	/* Kill the power before the graceful disconnect, to make sure
70 	 * that the change-aware status has been written correctly to
71 	 * NVS. We still have to wait for the delayed work to be run.
72 	 */
73 	k_sleep(K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
74 }
75 
server_round_3(void)76 void server_round_3(void)
77 {
78 	struct bt_conn *conn;
79 
80 	conn = connect_as_central();
81 	printk("encrypting\n");
82 	set_security(conn, BT_SECURITY_L2);
83 
84 	wait_for_client_read();
85 	wait_disconnected();
86 
87 	printk("register second service, peer will be change-unaware\n");
88 	gatt_register_service_2();
89 	/* on-disk hash will be different when round 2 (and round 4)
90 	 * start, the peer will be marked as change-unaware
91 	 */
92 	k_sleep(K_MSEC(100));
93 }
94 
server_round_4(void)95 void server_round_4(void)
96 {
97 	struct bt_conn *conn;
98 
99 	conn = connect_as_central();
100 	printk("encrypting\n");
101 	set_security(conn, BT_SECURITY_L2);
102 
103 	wait_for_client_read();
104 	wait_disconnected();
105 }
106 
server_round_5(void)107 void server_round_5(void)
108 {
109 	gatt_register_service_2();
110 
111 	/* sleep long enough to ensure the DB hash is stored to disk, but short
112 	 * enough to make sure the delayed storage work item is not executed.
113 	 */
114 	k_sleep(K_MSEC(100));
115 }
116 
server_round_6(void)117 void server_round_6(void)
118 {
119 	struct bt_conn *conn;
120 
121 	gatt_register_service_2();
122 
123 	conn = connect_as_central();
124 	printk("encrypting\n");
125 	set_security(conn, BT_SECURITY_L2);
126 
127 	wait_for_client_read();
128 	wait_disconnected();
129 }
130 
131 /* What is being tested: since this deals with settings it's not the rounds
132  * themselves, but rather the transitions that test expected behavior.
133  *
134  * Round 0 -> 1: test CCC / CF values written before bonding are stored to NVS
135  * if the server reboots before disconnecting.
136  *
137  * Round 1 -> 2: test change-awareness is updated if GATT DB changes _after_ the
138  * peer has disconnected. In round 2 we also make sure we receive the Service
139  * Changed indication.
140  *
141  * Round 2 -> 3: tests `CONFIG_BT_SETTINGS_CF_STORE_ON_WRITE` does its job, and
142  * writes the change-awareness before we get disconnected. Basically, this
143  * transition simulates a user yanking the power of the device before it has the
144  * chance to disconnect.
145  *
146  * Round 3 -> 4: same as (1->2), except this time we won't get the SC indication
147  * (as we have unsubscribed from it). We should instead get the
148  * `BT_ATT_ERR_DB_OUT_OF_SYNC` error on the first attribute read. This also
149  * tests that robust GATT caching is enforced.
150  *
151  * Round 4 -> 5: tests change-awareness status is still written on disconnect.
152  * This is a non-regression test to make sure we didn't break the previous
153  * behavior.
154  *
155  * Round 5 -> 6: tests DFU corner case: in this case, we are on the first boot
156  * of an updated firmware, that will register new services. But for some unknown
157  * reason, we decide to reboot before the delayed store work item has had the
158  * time to execute and store that the peers are now change-unaware. Round 6 then
159  * makes sure that we are indeed change-unaware.
160  */
server_procedure(void)161 void server_procedure(void)
162 {
163 	uint8_t round = get_test_round();
164 
165 	wait_for_round_start();
166 
167 	printk("Start test round: %d\n", get_test_round());
168 
169 	/* Use the same public address for all instances of the central. If we
170 	 * don't do that, encryption (using the bond stored in NVS) will
171 	 * fail.
172 	 */
173 	set_public_addr();
174 
175 	gatt_register_service_1();
176 
177 	bt_enable(NULL);
178 	settings_load();
179 
180 	switch (round) {
181 	case 0:
182 		server_round_0();
183 		break;
184 	case 1:
185 		server_round_1();
186 		break;
187 	case 2:
188 		server_round_2();
189 		break;
190 	case 3:
191 		server_round_3();
192 		break;
193 	case 4:
194 		server_round_4();
195 		break;
196 	case 5:
197 		server_round_5();
198 		break;
199 	case 6:
200 		server_round_6();
201 		break;
202 	default:
203 		FAIL("Round %d doesn't exist\n", round);
204 		break;
205 	}
206 
207 	signal_next_test_round();
208 }
209