Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,539 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "services/normal/bluetooth/ble_hrm.h"
#include "comm/ble/gap_le_connection.h"
#include "services/common/hrm/hrm_manager_private.h"
#include <bluetooth/hrm_service.h>
#include <btutil/bt_device.h>
#include <util/size.h>
#include <clar.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
// Stubs & Fakes
#include "fake_event_service.h"
#include "fake_pebble_tasks.h"
#include "fake_pbl_malloc.h"
#include "fake_regular_timer.h"
#include "stubs_analytics.h"
#include "stubs_bt_lock.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
void gap_le_slave_reconnect_hrm_restart(void) {
}
void gap_le_slave_reconnect_hrm_stop(void) {
}
static bool s_activity_prefs_heart_rate_is_enabled;
bool activity_prefs_heart_rate_is_enabled(void) {
return s_activity_prefs_heart_rate_is_enabled;
}
static bool s_bt_driver_hrm_service_is_enabled;
static int s_bt_driver_hrm_service_enable_call_count;
void bt_driver_hrm_service_enable(bool enable) {
s_bt_driver_hrm_service_enable_call_count++;
s_bt_driver_hrm_service_is_enabled = enable;
}
static BleHrmServiceMeasurement s_last_ble_hrm_measurement;
static int s_bt_driver_hrm_service_handle_measurement_call_count;
static BTDeviceInternal s_last_permitted_devices[10];
static size_t s_last_num_permitted_devices;
void bt_driver_hrm_service_handle_measurement(const BleHrmServiceMeasurement *measurement,
const BTDeviceInternal *permitted_devices,
size_t num_permitted_devices) {
++s_bt_driver_hrm_service_handle_measurement_call_count;
s_last_ble_hrm_measurement = *measurement;
s_last_num_permitted_devices = num_permitted_devices;
memcpy(s_last_permitted_devices, permitted_devices,
sizeof(*permitted_devices) * num_permitted_devices);
}
static BLEHRMSharingRequest *s_last_sharing_request;
static int s_ble_hrm_push_sharing_request_window_call_count;
void ble_hrm_push_sharing_request_window(BLEHRMSharingRequest *sharing_request) {
++s_ble_hrm_push_sharing_request_window_call_count;
cl_assert_equal_p(s_last_sharing_request, NULL);
s_last_sharing_request = sharing_request;
}
bool bt_driver_is_hrm_service_supported(void) {
return true;
}
static BTDeviceInternal s_last_disconnected;
int bt_driver_gap_le_disconnect(const BTDeviceInternal *peer_address) {
s_last_disconnected = *peer_address;
return 0;
}
static void prv_assert_last_disconnected(const BTDeviceInternal *peer_address) {
cl_assert_equal_b(bt_device_internal_equal(peer_address, &s_last_disconnected), true);
}
static int s_ble_hrm_push_reminder_popup_call_count;
void ble_hrm_push_reminder_popup(void) {
s_ble_hrm_push_reminder_popup_call_count++;
}
static int s_hrm_manager_subscribe_with_callback_call_count;
static HRMSessionRef s_last_session_ref;
static HRMSessionRef s_next_session_ref;
HRMSessionRef hrm_manager_subscribe_with_callback(AppInstallId app_id, uint32_t update_interval_s,
uint16_t expire_s, HRMFeature features,
HRMSubscriberCallback callback, void *context) {
cl_assert_equal_p(NULL, callback); // we're using the event service
cl_assert_equal_i(features, HRMFeature_BPM);
++s_hrm_manager_subscribe_with_callback_call_count;
s_last_session_ref = ++s_next_session_ref;
return s_last_session_ref;
}
static GAPLEConnection *s_connections[2];
GAPLEConnection *gap_le_connection_by_device(const BTDeviceInternal *device) {
for (int i = 0; i < ARRAY_LENGTH(s_connections); ++i) {
if (bt_device_internal_equal(device, &s_connections[i]->device)) {
return s_connections[i];
}
}
return NULL;
}
BTDeviceInternal *device_from_le_connection(GAPLEConnection *conn) {
return &conn->device;
}
bool gap_le_connection_is_valid(const GAPLEConnection *conn) {
for (int i = 0; i < ARRAY_LENGTH(s_connections); ++i) {
if (s_connections[i] == conn) {
return true;
}
}
return false;
}
void gap_le_connection_for_each(GAPLEConnectionForEachCallback cb, void *data) {
for (int i = 0; i < ARRAY_LENGTH(s_connections); ++i) {
cb(s_connections[i], data);
}
}
void launcher_task_add_callback(CallbackEventCallback callback, void *data) {
callback(data);
}
bool sys_hrm_manager_is_hrm_present(void) {
return true;
}
static int s_sys_hrm_manager_unsubscribe_call_count;
bool sys_hrm_manager_unsubscribe(HRMSessionRef session) {
++s_sys_hrm_manager_unsubscribe_call_count;
cl_assert_equal_i(session, s_last_session_ref);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tests
static void prv_assert_event_service_subscribed(bool is_subscribed) {
const EventServiceInfo *const info = fake_event_service_get_info(PEBBLE_HRM_EVENT);
if (is_subscribed) {
cl_assert(info->handler);
} else {
cl_assert_equal_p(NULL, info->handler);
}
}
void test_ble_hrm__cleanup(void) {
ble_hrm_deinit();
prv_assert_event_service_subscribed(false);
// hrm manager sub vs unsub calls should be the same, there should be no subscription any more
// after de-initing:
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count,
s_hrm_manager_subscribe_with_callback_call_count);
fake_pbl_malloc_check_net_allocs();
// Assert all regular timers are deregistered:
cl_assert_equal_p(s_seconds_callbacks.next, NULL);
cl_assert_equal_p(s_minutes_callbacks.next, NULL);
}
#define TEST_DEVICE_NAME "iPhone Martijn"
static GAPLEConnection s_conn_a;
static GAPLEConnection s_conn_b;
static const BTDeviceInternal *s_device_a;
static const BTDeviceInternal *s_device_b;
void test_ble_hrm__initialize(void) {
fake_pbl_malloc_clear_tracking();
for (int i = 0; i < ARRAY_LENGTH(s_connections); ++i) {
s_connections[i] = NULL;
}
s_activity_prefs_heart_rate_is_enabled = true;
s_bt_driver_hrm_service_is_enabled = true;
s_last_num_permitted_devices = 0;
memset(s_last_permitted_devices, 0, sizeof(s_last_permitted_devices));
s_bt_driver_hrm_service_enable_call_count = 0;
s_hrm_manager_subscribe_with_callback_call_count = 0;
s_sys_hrm_manager_unsubscribe_call_count = 0;
s_bt_driver_hrm_service_handle_measurement_call_count = 0;
s_ble_hrm_push_sharing_request_window_call_count = 0;
s_ble_hrm_push_reminder_popup_call_count = 0;
s_last_session_ref = ~0;
s_next_session_ref = 1234;
s_last_disconnected = (BTDeviceInternal) {};
s_last_sharing_request = NULL;
s_last_ble_hrm_measurement = (BleHrmServiceMeasurement) {};
fake_event_service_init();
// Set up fake devices/connections:
s_conn_a = (GAPLEConnection) {
.device_name = TEST_DEVICE_NAME,
.device = {
.address = {
.octets = {1, 2, 3, 4, 5, 6},
},
},
};
s_conn_b = (GAPLEConnection) {
.device_name = TEST_DEVICE_NAME,
.device = {
.address = {
.octets = {6, 5, 4, 3, 2, 1},
},
},
};
s_connections[0] = &s_conn_a;
s_connections[1] = &s_conn_b;
s_device_a = device_from_le_connection(&s_conn_a);
s_device_b = device_from_le_connection(&s_conn_b);
ble_hrm_init();
}
void test_ble_hrm__init_deinit_no_subscriptions(void) {
// let cleanup & initialize do the work :)
}
static void prv_assert_permissions_ui_and_respond(bool is_granted) {
cl_assert(s_last_sharing_request);
ble_hrm_handle_sharing_request_response(is_granted, s_last_sharing_request);
s_last_sharing_request = NULL;
}
void test_ble_hrm__sub_unsub(void) {
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 0);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 0);
prv_assert_event_service_subscribed(false);
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Expect HRM manager NOT to be subscribed to yet, need to grant permission first:
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 0);
prv_assert_event_service_subscribed(false);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
// Expect permissions UI to be presented:
prv_assert_permissions_ui_and_respond(true /* is_granted */);
// Expect HRM manager to be subscribed to:
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 1);
prv_assert_event_service_subscribed(true);
cl_assert_equal_b(true, ble_hrm_is_sharing_to_connection(&s_conn_a));
// Device A subscribes again, should be a no-op, no new permissions prompt:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 1);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 0);
// Device B subscribes, shouldn't resubscribe to HRM manager, but should present a new
// permission prompt, because it's a different device:
bt_driver_cb_hrm_service_update_subscription(s_device_b, true);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_b));
prv_assert_permissions_ui_and_respond(true /* is_granted */);
cl_assert_equal_b(true, ble_hrm_is_sharing_to_connection(&s_conn_b));
prv_assert_event_service_subscribed(true);
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 1);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 0);
// Device A disconnects, shouldn't unsubscribe from HRM manager because A is still subscribed:
ble_hrm_handle_disconnection(&s_conn_a);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
prv_assert_event_service_subscribed(true);
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 1);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 0);
// Device B unsubscribes, expect to be unsubscribed from HRM manager, because there are no more
// devices subscribed to the BLE HRM service:
bt_driver_cb_hrm_service_update_subscription(s_device_b, false);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
prv_assert_event_service_subscribed(false);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 1);
// Device B unsubscribes again, should be no-op
bt_driver_cb_hrm_service_update_subscription(s_device_b, false);
prv_assert_event_service_subscribed(false);
cl_assert_equal_i(s_sys_hrm_manager_unsubscribe_call_count, 1);
}
void test_ble_hrm__sub_unsub_resub(void) {
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Expect permissions UI to be presented:
prv_assert_permissions_ui_and_respond(true /* is_granted */);
// Device A unsubscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, false);
prv_assert_event_service_subscribed(false);
// Device A re-subscribes, permission should still be valid:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
prv_assert_event_service_subscribed(true);
}
void test_ble_hrm__revoke(void) {
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Expect permissions UI to be presented:
prv_assert_permissions_ui_and_respond(true /* is_granted */);
cl_assert_equal_b(true, ble_hrm_is_sharing_to_connection(&s_conn_a));
cl_assert_equal_b(true, ble_hrm_is_sharing());
prv_assert_event_service_subscribed(true);
// Revoke:
ble_hrm_revoke_sharing_permission_for_connection(&s_conn_a);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
cl_assert_equal_b(false, ble_hrm_is_sharing());
prv_assert_event_service_subscribed(false);
prv_assert_last_disconnected(s_device_a);
}
void test_ble_hrm__revoke_all(void) {
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Expect permissions UI to be presented:
prv_assert_permissions_ui_and_respond(true /* is_granted */);
// Device B subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_b, true);
// Expect permissions UI to be presented:
prv_assert_permissions_ui_and_respond(true /* is_granted */);
ble_hrm_revoke_all();
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_b));
cl_assert_equal_b(false, ble_hrm_is_sharing());
prv_assert_event_service_subscribed(false);
}
void test_ble_hrm__revoke_after_disconnection(void) {
ble_hrm_revoke_sharing_permission_for_connection(NULL);
s_connections[0] = NULL;
ble_hrm_revoke_sharing_permission_for_connection(&s_conn_a);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(NULL));
// Shouldn't crash or anything
}
void test_ble_hrm__grant_after_disconnection(void) {
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Fake disconnection:
s_connections[0] = NULL;
// Grabt permission after disconnection.
// Request object should be freed and thing shouldn't crash.
prv_assert_permissions_ui_and_respond(true /* is_granted */);
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
}
void test_ble_hrm__decline_permission_dont_ask_again_even_after_reconnecting(void) {
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// Decline:
prv_assert_permissions_ui_and_respond(false /* is_granted */);
// Unsub, resub:
bt_driver_cb_hrm_service_update_subscription(s_device_a, false);
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// No sharing request UI:
cl_assert_equal_p(NULL, s_last_sharing_request);
// Fake disconnection:
ble_hrm_handle_disconnection(&s_conn_a);
// Fake reconn & subscribe:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
// No sharing request UI:
cl_assert_equal_p(NULL, s_last_sharing_request);
// Still declined:
cl_assert_equal_b(false, ble_hrm_is_sharing_to_connection(&s_conn_a));
}
void test_ble_hrm__unsub_upon_deinit(void) {
// Device A subscribes:
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
prv_assert_permissions_ui_and_respond(true /* is_granted */);
// __cleanup() will do the deinit and also assert that there's no subscription to the HRM mgr.
}
// Test that we handle a races where a subscription/disconnection callback happens in after
// deiniting the stack:
void test_ble_hrm__sub_after_deinit(void) {
ble_hrm_deinit();
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
prv_assert_event_service_subscribed(false);
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 0);
ble_hrm_handle_disconnection(&s_conn_a);
prv_assert_event_service_subscribed(false);
cl_assert_equal_i(s_hrm_manager_subscribe_with_callback_call_count, 0);
ble_hrm_init(); // reinit, __cleanup() will deinit again
}
static void prv_put_and_assert_hrm_event(HRMEventType subtype, uint8_t bpm, HRMQuality quality,
bool expect_bt_driver_cb, bool expected_is_on_wrist) {
int call_count_before = s_bt_driver_hrm_service_handle_measurement_call_count;
PebbleEvent hrm_event = {
.type = PEBBLE_HRM_EVENT,
.hrm = {
.event_type = subtype,
.bpm = {
.bpm = bpm,
.quality = quality,
},
},
};
event_put(&hrm_event);
fake_event_service_handle_last();
if (expect_bt_driver_cb) {
cl_assert_equal_i(call_count_before + 1, s_bt_driver_hrm_service_handle_measurement_call_count);
cl_assert_equal_i(bpm, s_last_ble_hrm_measurement.bpm);
cl_assert_equal_b(expected_is_on_wrist, s_last_ble_hrm_measurement.is_on_wrist);
} else {
cl_assert_equal_i(call_count_before, s_bt_driver_hrm_service_handle_measurement_call_count);
}
}
void test_ble_hrm__handle_hrm_event(void) {
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
cl_assert_equal_i(0, s_bt_driver_hrm_service_handle_measurement_call_count);
prv_assert_permissions_ui_and_respond(true /* is_granted */);
// Don't grant permission to device B:
bt_driver_cb_hrm_service_update_subscription(s_device_b, true);
prv_assert_permissions_ui_and_respond(false /* is_granted */);
prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_Excellent,
true /* expect bt driver cb */, true /* expected_is_on_wrist */);
// Assert only device A is listed as "permitted device" and B is not:
cl_assert_equal_i(1, s_last_num_permitted_devices);
cl_assert_equal_m(&s_last_permitted_devices[0], s_device_a, sizeof(*s_device_a));
prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_NoSignal,
true /* expect bt driver cb */, false /* expected_is_on_wrist */);
prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_NoAccel,
true /* expect bt driver cb */, false /* expected_is_on_wrist */);
prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_OffWrist,
true /* expect bt driver cb */, false /* expected_is_on_wrist */);
// Ignore non-BPM event:
prv_put_and_assert_hrm_event(HRMEvent_HRV, 80, HRMQuality_OffWrist,
false /* expect bt driver cb */, false /* expected_is_on_wrist */);
}
void test_ble_hrm__handle_activity_pref_hrm_changes(void) {
cl_assert_equal_b(true, s_bt_driver_hrm_service_is_enabled);
cl_assert_equal_i(0, s_bt_driver_hrm_service_enable_call_count);
ble_hrm_handle_activity_prefs_heart_rate_is_enabled(false);
cl_assert_equal_i(1, s_bt_driver_hrm_service_enable_call_count);
cl_assert_equal_b(false, s_bt_driver_hrm_service_is_enabled);
// Disabled, again -- would lead to another call to bt_driver_hrm_service_enable(),
// the BT driver lib keeps track of whether it's enabled and is expected to ignore the call.
ble_hrm_handle_activity_prefs_heart_rate_is_enabled(false);
cl_assert_equal_i(2, s_bt_driver_hrm_service_enable_call_count);
cl_assert_equal_b(false, s_bt_driver_hrm_service_is_enabled);
// Enable
ble_hrm_handle_activity_prefs_heart_rate_is_enabled(true);
cl_assert_equal_i(3, s_bt_driver_hrm_service_enable_call_count);
cl_assert_equal_b(true, s_bt_driver_hrm_service_is_enabled);
}
void test_ble_hrm__popup_after_long_continuous_use(void) {
extern RegularTimerInfo *ble_hrm_timer(void);
RegularTimerInfo *timer = ble_hrm_timer();
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
prv_assert_permissions_ui_and_respond(true /* is_granted */);
cl_assert_equal_b(true, regular_timer_is_scheduled(timer));
bt_driver_cb_hrm_service_update_subscription(s_device_a, false);
cl_assert_equal_b(false, regular_timer_is_scheduled(timer));
bt_driver_cb_hrm_service_update_subscription(s_device_a, true);
cl_assert_equal_b(true, regular_timer_is_scheduled(timer));
cl_assert_equal_i(0, s_ble_hrm_push_reminder_popup_call_count);
fake_regular_timer_trigger(timer);
cl_assert_equal_i(1, s_ble_hrm_push_reminder_popup_call_count);
// Except timer to be rescheduled again:
cl_assert_equal_b(true, regular_timer_is_scheduled(timer));
bt_driver_cb_hrm_service_update_subscription(s_device_a, false);
cl_assert_equal_b(false, regular_timer_is_scheduled(timer));
}

View file

@ -0,0 +1,143 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "services/common/bluetooth/ble_root_keys.h"
#include "clar.h"
#include <string.h>
/// Stubs
#include "stubs_hexdump.h"
#include "stubs_logging.h"
static const SM128BitKey s_retrieved_keys[SMRootKeyTypeNum] = {
[SMRootKeyTypeEncryption] = {
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
},
[SMRootKeyTypeIdentity] = {
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
},
};
bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) {
cl_assert(key_type < SMRootKeyTypeNum);
bool rv = cl_mock_type(bool);
if (rv) {
memcpy(key_out, &s_retrieved_keys[key_type], sizeof(*key_out));
}
return rv;
}
static SM128BitKey s_stored_keys[SMRootKeyTypeNum];
void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) {
cl_mock_type(void);
memcpy(s_stored_keys, keys_in, sizeof(SM128BitKey) * SMRootKeyTypeNum);
}
static uint32_t s_rng_output;
#define RNG_ROUNDS (sizeof(SM128BitKey) / sizeof(uint32_t))
#define RNG_MAX_RETRIES (20)
bool rng_rand(uint32_t *rand_out) {
bool rv = cl_mock_type(bool);
if (rv) {
*rand_out = s_rng_output;
++s_rng_output;
}
return rv;
}
int rand(void) {
return cl_mock_type(int);
}
/// Tests
static void prv_assert_key_is_equal_to_retrieved_key(const SM128BitKey *key, SMRootKeyType type) {
cl_assert_equal_m(key, &s_retrieved_keys[type], sizeof(SM128BitKey));
}
static void prv_assert_key_is_equal_to_rng_key(const SM128BitKey *key, SMRootKeyType type) {
int rng_start_output = type * RNG_ROUNDS;
uint8_t rng_buffer[sizeof(SM128BitKey)];
uint32_t *rngs = (uint32_t *)rng_buffer;
for (int i = 0; i < RNG_ROUNDS; ++i) {
rngs[i] = rng_start_output + i;
}
SM128BitKey *rng_key = (SM128BitKey *)rng_buffer;
cl_assert_equal_m(key, rng_key, sizeof(SM128BitKey));
}
void test_ble_root_keys__initialize(void) {
s_rng_output = 0;
memset(s_stored_keys, 0, sizeof(SM128BitKey) * SMRootKeyTypeNum);
}
void test_ble_root_keys__cleanup(void) {
}
void test_ble_root_keys__has_existing_root_keys(void) {
cl_will_return_count(bt_persistent_storage_get_root_key, true, 2);
SM128BitKey keys[SMRootKeyTypeNum];
ble_root_keys_get_and_generate_if_needed(keys);
prv_assert_key_is_equal_to_retrieved_key(&keys[SMRootKeyTypeEncryption], SMRootKeyTypeEncryption);
prv_assert_key_is_equal_to_retrieved_key(&keys[SMRootKeyTypeIdentity], SMRootKeyTypeIdentity);
}
void test_ble_root_keys__regenerate_if_key_not_present(void) {
// Pretend one of the root keys isn't there:
cl_will_return_count(bt_persistent_storage_get_root_key, true, 1);
cl_will_return_count(bt_persistent_storage_get_root_key, false, 1);
cl_will_return_count(rng_rand, true, RNG_ROUNDS * SMRootKeyTypeNum);
cl_will_return_count(bt_persistent_storage_set_root_keys, true, 1);
SM128BitKey keys[SMRootKeyTypeNum];
ble_root_keys_get_and_generate_if_needed(keys);
prv_assert_key_is_equal_to_rng_key(&keys[SMRootKeyTypeEncryption], SMRootKeyTypeEncryption);
prv_assert_key_is_equal_to_rng_key(&keys[SMRootKeyTypeIdentity], SMRootKeyTypeIdentity);
prv_assert_key_is_equal_to_rng_key(&s_stored_keys[SMRootKeyTypeEncryption], SMRootKeyTypeEncryption);
prv_assert_key_is_equal_to_rng_key(&s_stored_keys[SMRootKeyTypeIdentity], SMRootKeyTypeIdentity);
}
void test_ble_root_keys__fall_back_to_rand(void) {
// Pretend one of the root keys isn't there:
cl_will_return_count(bt_persistent_storage_get_root_key, true, 1);
cl_will_return_count(bt_persistent_storage_get_root_key, false, 1);
cl_will_return_count(rng_rand, false, RNG_MAX_RETRIES);
cl_will_return_count(rand, 0x55, sizeof(SM128BitKey));
cl_will_return_count(rand, 0xaa, sizeof(SM128BitKey));
cl_will_return_count(bt_persistent_storage_set_root_keys, true, 1);
SM128BitKey keys[SMRootKeyTypeNum];
ble_root_keys_get_and_generate_if_needed(keys);
SM128BitKey rand_enc;
SM128BitKey rand_id;
memset(&rand_enc, 0x55, sizeof(SM128BitKey));
memset(&rand_id, 0xaa, sizeof(SM128BitKey));
cl_assert_equal_m(&keys[SMRootKeyTypeEncryption], &rand_enc, sizeof(SM128BitKey));
cl_assert_equal_m(&keys[SMRootKeyTypeIdentity], &rand_id, sizeof(SM128BitKey));
cl_assert_equal_m(&s_stored_keys[SMRootKeyTypeEncryption], &rand_enc, sizeof(SM128BitKey));
cl_assert_equal_m(&s_stored_keys[SMRootKeyTypeIdentity], &rand_id, sizeof(SM128BitKey));
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,705 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clar.h"
#include <bluetooth/bonding_sync.h>
#include <bluetooth/gap_le_connect.h>
#include "services/common/analytics/analytics.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/shared_prf_storage/shared_prf_storage.h"
#include "services/common/system_task.h"
#include "flash_region/flash_region_s29vs.h"
#include "util/size.h"
typedef struct GAPLEConnection GAPLEConnection;
#include "fake_bonding_sync.h"
#include "fake_new_timer.h"
#include "fake_pbl_malloc.h"
#include "fake_spi_flash.h"
#include "fake_regular_timer.h"
#include "stubs_bluetopia_interface.h"
#include "stubs_bt_lock.h"
#include "stubs_gap_le_advert.h"
#include "stubs_bluetooth_analytics.h"
#include "stubs_gatt_client_discovery.h"
#include "stubs_gatt_client_subscriptions.h"
#include "stubs_hexdump.h"
#include "stubs_hexdump.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pebble_pairing_service.h"
bool bt_driver_supports_bt_classic(void) {
return true;
}
void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) {
}
void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) {
}
typedef bool (*BondingSyncFilterCb)(const BleBonding *bonding, void *ctx);
const BleBonding *bonding_sync_find(BondingSyncFilterCb cb, void *ctx) {
return NULL;
}
void bt_driver_pebble_pairing_service_handle_status_change(const GAPLEConnection *connection) {
}
bool bt_ctl_is_bluetooth_running(void) {
return true;
}
void bt_driver_handle_le_conn_params_update_event(
const BleConnectionUpdateCompleteEvent *event) {
}
typedef struct PairingUserConfirmationCtx PairingUserConfirmationCtx;
void bt_driver_cb_pairing_confirm_handle_request(const PairingUserConfirmationCtx *ctx,
const char *device_name,
const char *confirmation_token) {
}
void bt_driver_cb_pairing_confirm_handle_completed(const PairingUserConfirmationCtx *ctx,
bool success) {
}
void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) {
}
extern RegularTimerInfo *shared_prf_storage_get_writeback_timer(void);
static void prv_fire_writeback_timer(void) {
fake_regular_timer_trigger(shared_prf_storage_get_writeback_timer());
}
static int s_bonding_change_count;
static BtPersistBondingOp s_bonding_change_ops[2];
void kernel_le_client_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) {
if (s_bonding_change_count <= ARRAY_LENGTH(s_bonding_change_ops)) {
s_bonding_change_ops[s_bonding_change_count] = op;
}
++s_bonding_change_count;
}
static void prv_reset_change_op_tracking(void) {
s_bonding_change_count = 0;
for (int i = 0; i < ARRAY_LENGTH(s_bonding_change_ops); ++i) {
s_bonding_change_ops[i] = BtPersistBondingOpInvalid;
}
}
void cc2564A_bad_le_connection_complete_handle(unsigned int stack_id,
const GAP_LE_Current_Connection_Parameters_t *params) {
}
void gap_le_connect_handle_bonding_change(BTBondingID bonding_id, BtPersistBondingOp op) {
}
void gap_le_connection_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) {
}
void gap_le_device_name_request(uintptr_t stack_id, GAPLEConnection *connection) {
}
uint16_t gaps_get_starting_att_handle(void) {
return 4;
}
void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connection) {
}
void bt_pairability_update_due_to_bonding_change(void) {
}
void launcher_task_add_callback(void (*callback)(void *data), void *data) {
callback(data);
}
bool system_task_add_callback(SystemTaskEventCallback cb, void *data) {
cb(data);
return true;
}
// Tests
///////////////////////////////////////////////////////////
void test_bluetooth_persistent_storage_prf__initialize(void) {
bonding_sync_init();
prv_reset_change_op_tracking();
fake_spi_flash_init(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN,
FLASH_REGION_SHARED_PRF_STORAGE_END - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN);
shared_prf_storage_init();
bt_persistent_storage_init();
}
void test_bluetooth_persistent_storage_prf__cleanup(void) {
fake_spi_flash_cleanup();
bonding_sync_deinit();
}
void test_bluetooth_persistent_storage_prf__ble_store_and_get(void) {
bool ret;
// Output variables
SMIdentityResolvingKey irk_out;
BTDeviceInternal device_out;
// Store a new pairing
SMPairingInfo pairing_1 = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {
.octets = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16
},
},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
BTBondingID id_1 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */,
NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id_1 != BT_BONDING_ID_INVALID);
cl_assert_equal_i(s_bonding_change_count, 1);
cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpDidAdd);
// Read it back
ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, &irk_out, &device_out, NULL /* name */);
cl_assert(ret);
cl_assert_equal_m(&irk_out, &pairing_1.irk, sizeof(irk_out));
cl_assert_equal_m(&device_out, &pairing_1.identity, sizeof(device_out));
// Re-pair device 1 again:
// In case the device is the same as the existing pairing, make sure the operation is "change"
// and not "delete" to avoid disconnecting just because the existing pairing is deleted.
// For bug details see https://pebbletechnology.atlassian.net/browse/PBL-24690
prv_reset_change_op_tracking();
id_1 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id_1 != BT_BONDING_ID_INVALID);
cl_assert_equal_i(s_bonding_change_count, 1);
cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpDidChange);
// Store another pairing (different device):
SMPairingInfo pairing_2 = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data = {
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x08,
0x09, 0x02, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x20
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {
.octets = {
0x21, 0x22, 0x13, 0x14, 0x15, 0x26
},
},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
prv_reset_change_op_tracking();
BTBondingID id_2 = bt_persistent_storage_store_ble_pairing(&pairing_2, true /* is_gateway */,
NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id_2 != BT_BONDING_ID_INVALID);
cl_assert_equal_i(s_bonding_change_count, 2);
cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpWillDelete);
cl_assert_equal_i(s_bonding_change_ops[1], BtPersistBondingOpDidAdd);
// Read it back
ret = bt_persistent_storage_get_ble_pairing_by_id(id_2, &irk_out, &device_out, NULL /* name */);
cl_assert(ret);
cl_assert_equal_m(&irk_out, &pairing_2.irk, sizeof(irk_out));
cl_assert_equal_m(&device_out, &pairing_2.identity, sizeof(device_out));
// Store another pairing, this time it isn't a gateway
SMPairingInfo pairing_3 = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data = {
0x33, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x08,
0x39, 0x02, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x20
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {
.octets = {
0x33, 0x22, 0x13, 0x14, 0x15, 0x26
},
},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
prv_reset_change_op_tracking();
BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_2, false /* is_gateway */,
NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id_3 == BT_BONDING_ID_INVALID);
cl_assert_equal_i(s_bonding_change_count, 0);
// Read out the stored pairing (id_2 should still be stored)
ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, &irk_out, &device_out, NULL /* name */);
cl_assert(ret);
cl_assert_equal_m(&irk_out, &pairing_2.irk, sizeof(irk_out));
cl_assert_equal_m(&device_out, &pairing_2.identity, sizeof(device_out));
bt_persistent_storage_register_existing_ble_bondings();
cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_1, true), false);
cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_2, true), true);
cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_3, false), false);
}
void test_bluetooth_persistent_storage_prf__get_ble_by_address(void) {
bool ret;
// Output variables
SMIdentityResolvingKey irk_out;
// Store a pairing
SMPairingInfo pairing = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {
.octets = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
},
},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
BTBondingID id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// Read it back
ret = bt_persistent_storage_get_ble_pairing_by_addr(&pairing.identity, &irk_out, NULL);
cl_assert(ret);
cl_assert_equal_m(&irk_out, &pairing.irk, sizeof(irk_out));
}
void test_bluetooth_persistent_storage_prf__delete_ble_pairing_by_id(void) {
bool ret;
// Output variables
SMIdentityResolvingKey irk_out;
BTDeviceInternal device_out;
// Store a pairing
SMPairingInfo pairing = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {
.octets = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16
},
},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
BleBonding ble_bonding = (BleBonding) {
.is_gateway = true,
.pairing_info = pairing,
};
bonding_sync_add_bonding(&ble_bonding);
BTBondingID id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// Delete the Pairing
bt_persistent_storage_delete_ble_pairing_by_id(id);
// Try to read it back
ret = bt_persistent_storage_get_ble_pairing_by_id(id, &irk_out, &device_out, NULL);
cl_assert(!ret);
// Add the pairing again
id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
}
// ///////////////////////////////////////////////////////////////////////////////////////////////////
// //! BT Classic Pairing Info
void test_bluetooth_persistent_storage_prf__bt_classic_store_and_get(void) {
bool ret;
// Output variables
BTDeviceAddress addr_out;
SM128BitKey link_key_out;
char name_out[BT_DEVICE_NAME_BUFFER_SIZE];
uint8_t platform_bits_out;
// Store a new pairing
BTDeviceAddress addr_1 = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_1 = {
.data = {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}
};
char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1";
uint8_t platform_bits_1 = 0x11;
BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1,
name_1, &platform_bits_1);
prv_fire_writeback_timer();
cl_assert(id_1 != BT_BONDING_ID_INVALID);
// Read it back
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(ret);
cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out));
cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out));
cl_assert_equal_s(name_1, name_out);
cl_assert_equal_i(platform_bits_1, platform_bits_out);
// Store another pairing
BTDeviceAddress addr_2 = {.octets = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26}};
SM128BitKey link_key_2 = {
.data = {
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
},
};
char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2";
uint8_t platform_bits_2 = 0x22;
BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2,
name_2, &platform_bits_2);
prv_fire_writeback_timer();
cl_assert(id_2 != BT_BONDING_ID_INVALID);
// Read it pairings back (purposefully using the wrong bonding ID cause it doesn't matter)
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(ret);
cl_assert_equal_m(&addr_2, &addr_out, sizeof(addr_out));
cl_assert_equal_m(&link_key_2, &link_key_out, sizeof(link_key_out));
cl_assert_equal_s(name_2, name_out);
cl_assert_equal_i(platform_bits_2, platform_bits_out);
// Add a thrid pairing
BTDeviceAddress addr_3 = {.octets = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36}};
SM128BitKey link_key_3 = {
.data = {
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
},
};
char name_3[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 3";
uint8_t platform_bits_3 = 0x33;
BTBondingID id_3 = bt_persistent_storage_store_bt_classic_pairing(&addr_3, &link_key_3,
name_3, &platform_bits_3);
prv_fire_writeback_timer();
cl_assert(id_3 != BT_BONDING_ID_INVALID);
// Read all three pairings back (purposefully using the wrong bonding ID cause it doesn't matter)
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(ret);
cl_assert_equal_m(&addr_3, &addr_out, sizeof(addr_out));
cl_assert_equal_m(&link_key_3, &link_key_out, sizeof(link_key_out));
cl_assert_equal_s(name_3, name_out);
cl_assert_equal_i(platform_bits_3, platform_bits_out);
}
void test_bluetooth_persistent_storage_prf__get_bt_classic_pairing_by_addr(void) {
// Output variables
SM128BitKey link_key_out;
char name_out[BT_DEVICE_NAME_BUFFER_SIZE];
uint8_t platform_bits_out;
// Store a new pairing
BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_in = {
.data = {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
},
};
char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1";
uint8_t platform_bits_in = 0x11;
BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in,
name_in, &platform_bits_in);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// Read it back
BTBondingID id_out = bt_persistent_storage_get_bt_classic_pairing_by_addr(&addr_in, &link_key_out,
name_out, &platform_bits_out);
cl_assert_equal_i(id, id_out);
cl_assert_equal_m(&link_key_in, &link_key_out, sizeof(link_key_out));
cl_assert_equal_s(name_in, name_out);
cl_assert_equal_i(platform_bits_in, platform_bits_out);
}
void test_bluetooth_persistent_storage_prf__delete_bt_classic_pairing_by_id(void) {
bool ret;
// Output variables
BTDeviceAddress addr_out;
SM128BitKey link_key_out;
char name_out[BT_DEVICE_NAME_BUFFER_SIZE];
uint8_t platform_bits_out;
// Store a new pairing
BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_in = {
.data = {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
},
};
char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1";
uint8_t platform_bits_in = 0x11;
BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in,
name_in, &platform_bits_in);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// Delete the Pairing
bt_persistent_storage_delete_bt_classic_pairing_by_id(id);
// Try to read it back
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(!ret);
// Add the pairing again
id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in,
name_in, &platform_bits_in);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// And delete is again
bt_persistent_storage_delete_bt_classic_pairing_by_id(id);
// Try to read it back
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(!ret);
}
void test_bluetooth_persistent_storage_prf__delete_bt_classic_pairing_by_addr(void) {
bool ret;
// Output variables
BTDeviceAddress addr_out;
SM128BitKey link_key_out;
char name_out[BT_DEVICE_NAME_BUFFER_SIZE];
uint8_t platform_bits_out;
// Store a new pairing
BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_in = {
.data = {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
},
};
char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1";
uint8_t platform_bits_in = 0x11;
BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in,
name_in, &platform_bits_in);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// Delete the Pairing
bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in);
// Try to read it back
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(!ret);
// Add the pairing again
id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in,
name_in, &platform_bits_in);
prv_fire_writeback_timer();
cl_assert(id != BT_BONDING_ID_INVALID);
// And delete is again
bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in);
// Try to read it back
ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out,
name_out, &platform_bits_out);
cl_assert(!ret);
}
// ///////////////////////////////////////////////////////////////////////////////////////////////////
// //! Local Device Info
void test_bluetooth_persistent_storage_prf__test_active_gateway(void) {
bool ret;
BtPersistBondingType type_out;
BTBondingID id_out;
// Nothing is stored, so no active gateways yet
ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out);
cl_assert(!ret);
ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding();
cl_assert(!ret);
ret = bt_persistent_storage_has_active_ble_gateway_bonding();
cl_assert(!ret);
// Store a new BT Classic pairing
BTDeviceAddress addr_1 = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_1 = {
.data = {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
},
};
char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1";
uint8_t platform_bits_1 = 0x11;
BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1,
name_1, &platform_bits_1);
prv_fire_writeback_timer();
cl_assert(id_1 != BT_BONDING_ID_INVALID);
// It should be the active gateway
ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out);
cl_assert(ret);
cl_assert_equal_i(id_out, id_1);
cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic);
ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding();
cl_assert(ret);
ret = bt_persistent_storage_has_active_ble_gateway_bonding();
cl_assert(!ret);
// Store another BT Classic pairing
BTDeviceAddress addr_2 = {.octets = {0x22, 0x12, 0x13, 0x14, 0x15, 0x16}};
SM128BitKey link_key_2 = {
.data = {
0x22, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
},
};
char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2";
uint8_t platform_bits_2 = 0x22;
BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2,
name_2, &platform_bits_2);
prv_fire_writeback_timer();
cl_assert(id_2 != BT_BONDING_ID_INVALID);
ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out);
cl_assert(ret);
cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic);
ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding();
cl_assert(ret);
ret = bt_persistent_storage_has_active_ble_gateway_bonding();
cl_assert(!ret);
// Delete the pairing.
bt_persistent_storage_delete_bt_classic_pairing_by_id(id_2);
// There should be no active gateway now
ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out);
cl_assert(!ret);
ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding();
cl_assert(!ret);
ret = bt_persistent_storage_has_active_ble_gateway_bonding();
cl_assert(!ret);
// Store a new BLE pairing
SMPairingInfo pairing_1 = (SMPairingInfo) {
.irk = (SMIdentityResolvingKey) {
.data= {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00
},
},
.identity = (BTDeviceInternal) {
.address = (BTDeviceAddress) {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
.is_classic = false,
.is_random_address = false,
},
.is_remote_identity_info_valid = true,
};
BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */,
NULL,
false /* requires_address_pinning */,
false /* auto_accept_re_pairing */);
prv_fire_writeback_timer();
cl_assert(id_3 != BT_BONDING_ID_INVALID);
// There should now be an active BLE gateway
ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out);
cl_assert(!ret);
ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding();
cl_assert(!ret);
ret = bt_persistent_storage_has_active_ble_gateway_bonding();
cl_assert(ret);
}

View file

@ -0,0 +1,207 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/bluetooth/local_addr.h"
#include <bluetooth/bluetooth_types.h>
#include <stdbool.h>
#include <clar.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
// Fakes / Stubs
#include "stubs_bt_lock.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
static bool s_last_driver_allow_cycling;
BTDeviceAddress s_last_driver_addr;
bool s_last_driver_addr_is_null;
void bt_driver_set_local_address(bool allow_cycling,
const BTDeviceAddress *pinned_address) {
s_last_driver_allow_cycling = allow_cycling;
if (pinned_address) {
s_last_driver_addr_is_null = false;
s_last_driver_addr = *pinned_address;
} else {
s_last_driver_addr_is_null = true;
memset(&s_last_driver_addr, 0x00, sizeof(s_last_driver_addr));
}
return cl_mock_type(void);
}
BTDeviceAddress s_last_bt_persist_pinned_addr;
bool s_last_bt_persist_pinned_addr_is_null;
bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out) {
if (s_last_bt_persist_pinned_addr_is_null) {
return false;
}
if (address_out) {
*address_out = s_last_bt_persist_pinned_addr;
}
return true;
}
bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *addr) {
if (addr) {
s_last_bt_persist_pinned_addr_is_null = false;
s_last_bt_persist_pinned_addr = *addr;
} else {
s_last_bt_persist_pinned_addr_is_null = true;
memset(&s_last_bt_persist_pinned_addr, 0x00, sizeof(s_last_bt_persist_pinned_addr));
}
return cl_mock_type(bool);
}
bool bt_persistent_storage_has_pinned_ble_pairings(void) {
return cl_mock_type(bool);
}
#define TEST_PINNED_ADDR_1 ((BTDeviceAddress){ .octets = { 0x11, 0x22, 0x33, 0x33, 0x44, 0x55 } })
#define TEST_PINNED_ADDR_2 ((BTDeviceAddress){ .octets = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } })
bool bt_driver_id_generate_private_resolvable_address(BTDeviceAddress *root_pinned_address_out) {
*root_pinned_address_out = TEST_PINNED_ADDR_1;
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tests
#define TEST_BONDING_ID ((BTBondingID) 1)
static void prv_init_no_pinnings_no_pinned_address(void) {
cl_will_return(bt_persistent_storage_has_pinned_ble_pairings, false);
cl_will_return(bt_persistent_storage_set_ble_pinned_address, true);
cl_will_return(bt_driver_set_local_address, 0);
s_last_bt_persist_pinned_addr_is_null = true;
bt_local_addr_init();
}
static void prv_assert_driver_addr_is_null(void) {
cl_assert_equal_b(s_last_driver_addr_is_null, true);
cl_assert_equal_m(&s_last_driver_addr, &(BTDeviceAddress){}, sizeof(s_last_driver_addr));
}
static void prv_assert_driver_addr_is_addr1(void) {
cl_assert_equal_b(s_last_driver_addr_is_null, false);
cl_assert_equal_m(&s_last_driver_addr, &TEST_PINNED_ADDR_1, sizeof(s_last_driver_addr));
}
static void prv_assert_driver_addr_is_addr2(void) {
cl_assert_equal_b(s_last_driver_addr_is_null, false);
cl_assert_equal_m(&s_last_driver_addr, &TEST_PINNED_ADDR_2, sizeof(s_last_driver_addr));
}
void test_local_addr__initialize(void) {
memset(&s_last_bt_persist_pinned_addr, 0xff, sizeof(s_last_bt_persist_pinned_addr));
memset(&s_last_driver_addr, 0xff, sizeof(s_last_driver_addr));
s_last_bt_persist_pinned_addr_is_null = false;
s_last_driver_allow_cycling = false;
s_last_driver_addr_is_null = false;
}
void test_local_addr__cleanup(void) {
}
void test_local_addr__init_generates_pinned_address_if_needed(void) {
prv_init_no_pinnings_no_pinned_address();
cl_assert_equal_b(false, s_last_bt_persist_pinned_addr_is_null);
cl_assert_equal_m(&s_last_bt_persist_pinned_addr,
&TEST_PINNED_ADDR_1, sizeof(s_last_bt_persist_pinned_addr));
cl_assert_equal_b(true, s_last_driver_allow_cycling);
}
void test_local_addr__init_loads_stored_pinned_address(void) {
cl_will_return(bt_persistent_storage_has_pinned_ble_pairings, true);
cl_will_return(bt_persistent_storage_set_ble_pinned_address, true);
cl_will_return(bt_driver_set_local_address, 0);
s_last_bt_persist_pinned_addr_is_null = false;
s_last_bt_persist_pinned_addr = TEST_PINNED_ADDR_2;
bt_local_addr_init();
cl_assert_equal_b(false, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_addr2();
}
void test_local_addr__pause_resume(void) {
prv_init_no_pinnings_no_pinned_address();
// flip to make sure that bt_local_addr_pause_cycling() will set these again:
s_last_driver_allow_cycling = true;
s_last_driver_addr_is_null = false;
cl_will_return(bt_driver_set_local_address, 0);
bt_local_addr_pause_cycling();
cl_assert_equal_b(false, s_last_driver_allow_cycling);
// Check that it's using the pinned address that was generated upon initialization:
prv_assert_driver_addr_is_addr1();
bt_local_addr_pause_cycling();
// Already paused, shouldn't result in a bt_driver_set_local_address() call.
bt_local_addr_resume_cycling();
// Still paused, shouldn't result in a bt_driver_set_local_address() call.
// flip to make sure that bt_local_addr_pause_cycling() will set these again:
s_last_driver_allow_cycling = true;
s_last_driver_addr_is_null = false;
cl_will_return(bt_driver_set_local_address, 0);
bt_local_addr_resume_cycling();
cl_assert_equal_b(true, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_null();
}
void test_local_addr__pin_unpin(void) {
prv_init_no_pinnings_no_pinned_address();
// Pin:
cl_will_return(bt_driver_set_local_address, 0);
cl_will_return(bt_persistent_storage_has_pinned_ble_pairings, true);
bt_local_addr_pin(&TEST_PINNED_ADDR_1);
bt_local_addr_handle_bonding_change(TEST_BONDING_ID, BtPersistBondingOpDidAdd);
cl_assert_equal_b(false, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_addr1();
// Unpin (done implicitly when bonding is removed):
cl_will_return(bt_persistent_storage_has_pinned_ble_pairings, false);
cl_will_return(bt_driver_set_local_address, 0);
bt_local_addr_handle_bonding_change(TEST_BONDING_ID, BtPersistBondingOpWillDelete);
cl_assert_equal_b(true, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_null();
}
void test_local_addr__pause_then_pin(void) {
prv_init_no_pinnings_no_pinned_address();
// Pause:
cl_will_return(bt_driver_set_local_address, 0);
bt_local_addr_pause_cycling();
cl_assert_equal_b(false, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_addr1();
// Pin, expect pinned address to be sent to BT driver:
cl_will_return(bt_driver_set_local_address, 0);
cl_will_return(bt_persistent_storage_has_pinned_ble_pairings, true);
bt_local_addr_pin(&TEST_PINNED_ADDR_1);
bt_local_addr_handle_bonding_change(TEST_BONDING_ID, BtPersistBondingOpDidAdd);
cl_assert_equal_b(false, s_last_driver_allow_cycling);
prv_assert_driver_addr_is_addr1();
}

View file

@ -0,0 +1,66 @@
from waftools.pebble_test import clar
def _test_bluetooth_persistent_storage(bld, version=1):
test_name = "test_bluetooth_persistent_storage_v%u" % version
version_override_include = 'bluetooth_persistent_storage_v%u' % version
clar(bld,
sources_ant_glob=(
"src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c "
"src/fw/services/normal/settings/settings_file.c "
"src/fw/services/normal/settings/settings_raw_iter.c "
"src/fw/services/normal/filesystem/pfs.c "
"src/fw/services/normal/filesystem/flash_translation.c "
"src/fw/flash_region/flash_region.c "
"src/fw/flash_region/filesystem_regions.c "
"src/fw/util/crc8.c "
"src/fw/util/legacy_checksum.c "
"src/fw/system/hexdump.c "
"tests/fakes/fake_shared_prf_storage.c "
"tests/fakes/fake_events.c "
"tests/fakes/fake_spi_flash.c "
"tests/fakes/fake_rtc.c "
),
test_sources_ant_glob="test_bluetooth_persistent_storage.c",
test_name=test_name,
override_includes=['dummy_board', version_override_include])
def build(bld):
clar(bld,
sources_ant_glob=(
"src/fw/services/normal/bluetooth/ble_hrm.c "
"tests/fakes/fake_events.c "
),
test_sources_ant_glob="test_ble_hrm.c",
defines=['CAPABILITY_HAS_BUILTIN_HRM=1'])
clar(bld,
sources_ant_glob=(
"src/fw/services/common/bluetooth/ble_root_keys.c"
),
test_sources_ant_glob="test_ble_root_keys.c")
clar(bld,
sources_ant_glob=(
"src/fw/services/common/bluetooth/local_addr.c"
),
test_sources_ant_glob="test_local_addr.c")
clar(bld,
sources_ant_glob=(
"src/fw/services/prf/bluetooth/bluetooth_persistent_storage.c "
"src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c "
"tests/fakes/fake_spi_flash.c "
"tests/fakes/fake_events.c "
),
test_sources_ant_glob="test_bluetooth_persistent_storage_prf.c",
override_includes=['snowy_mfg_board'])
# Run the bluetooth_persistent_storage.c unit tests
# for the v1 and v2 serialization formats:
_test_bluetooth_persistent_storage(bld, version=1)
_test_bluetooth_persistent_storage(bld, version=2)
# vim:filetype=python