blob: 7fff0f72c154c4ad7aa646a4dd6e66c434ed13d6 [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "utils.h"
#include "main.h"
#include "gatt_utils.h"
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/settings/settings.h>
#include <zephyr/toolchain.h>
#include <stdint.h>
#include <string.h>
void set_public_addr(void)
{
bt_addr_le_t addr = {BT_ADDR_LE_RANDOM,
{{0x0A, 0x89, 0x67, 0x45, 0x23, 0xC1}}};
bt_id_create(&addr, NULL);
}
void server_round_0(void)
{
struct bt_conn *conn;
conn = connect_as_central();
wait_for_client_read();
printk("bonding\n");
bond(conn);
}
void server_round_1(void)
{
struct bt_conn *conn;
/* Wait for GATT DB hash to complete. */
k_sleep(K_SECONDS(2));
conn = connect_as_central();
printk("encrypting\n");
set_security(conn, BT_SECURITY_L2);
wait_for_client_read();
wait_disconnected();
printk("register second service, peer will be change-unaware\n");
gatt_register_service_2();
/* on-disk hash will be different when round 2 (and round 4)
* start, the peer will be marked as change-unaware
*/
k_sleep(K_MSEC(100));
}
void server_round_2(void)
{
struct bt_conn *conn;
conn = connect_as_central();
printk("encrypting\n");
set_security(conn, BT_SECURITY_L2);
wait_for_client_read();
/* Kill the power before the graceful disconnect, to make sure
* that the change-aware status has been written correctly to
* NVS. We still have to wait for the delayed work to be run.
*/
k_sleep(K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
}
void server_round_3(void)
{
struct bt_conn *conn;
conn = connect_as_central();
printk("encrypting\n");
set_security(conn, BT_SECURITY_L2);
wait_for_client_read();
wait_disconnected();
printk("register second service, peer will be change-unaware\n");
gatt_register_service_2();
/* on-disk hash will be different when round 2 (and round 4)
* start, the peer will be marked as change-unaware
*/
k_sleep(K_MSEC(100));
}
void server_round_4(void)
{
struct bt_conn *conn;
conn = connect_as_central();
printk("encrypting\n");
set_security(conn, BT_SECURITY_L2);
wait_for_client_read();
wait_disconnected();
}
void server_round_5(void)
{
gatt_register_service_2();
/* sleep long enough to ensure the DB hash is stored to disk, but short
* enough to make sure the delayed storage work item is not executed.
*/
k_sleep(K_MSEC(100));
}
void server_round_6(void)
{
struct bt_conn *conn;
gatt_register_service_2();
conn = connect_as_central();
printk("encrypting\n");
set_security(conn, BT_SECURITY_L2);
wait_for_client_read();
wait_disconnected();
}
/* What is being tested: since this deals with settings it's not the rounds
* themselves, but rather the transitions that test expected behavior.
*
* Round 0 -> 1: test CCC / CF values written before bonding are stored to NVS
* if the server reboots before disconnecting.
*
* Round 1 -> 2: test change-awareness is updated if GATT DB changes _after_ the
* peer has disconnected. In round 2 we also make sure we receive the Service
* Changed indication.
*
* Round 2 -> 3: tests `CONFIG_BT_SETTINGS_CF_STORE_ON_WRITE` does its job, and
* writes the change-awareness before we get disconnected. Basically, this
* transition simulates a user yanking the power of the device before it has the
* chance to disconnect.
*
* Round 3 -> 4: same as (1->2), except this time we won't get the SC indication
* (as we have unsubscribed from it). We should instead get the
* `BT_ATT_ERR_DB_OUT_OF_SYNC` error on the first attribute read. This also
* tests that robust GATT caching is enforced.
*
* Round 4 -> 5: tests change-awareness status is still written on disconnect.
* This is a non-regression test to make sure we didn't break the previous
* behavior.
*
* Round 5 -> 6: tests DFU corner case: in this case, we are on the first boot
* of an updated firmware, that will register new services. But for some unknown
* reason, we decide to reboot before the delayed store work item has had the
* time to execute and store that the peers are now change-unaware. Round 6 then
* makes sure that we are indeed change-unaware.
*/
void server_procedure(void)
{
uint8_t round = get_test_round();
wait_for_round_start();
printk("Start test round: %d\n", get_test_round());
/* Use the same public address for all instances of the central. If we
* don't do that, encryption (using the bond stored in NVS) will
* fail.
*/
set_public_addr();
gatt_register_service_1();
bt_enable(NULL);
settings_load();
switch (round) {
case 0:
server_round_0();
break;
case 1:
server_round_1();
break;
case 2:
server_round_2();
break;
case 3:
server_round_3();
break;
case 4:
server_round_4();
break;
case 5:
server_round_5();
break;
case 6:
server_round_6();
break;
default:
FAIL("Round %d doesn't exist\n", round);
break;
}
signal_next_test_round();
}