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