mirror of
https://github.com/google/pebble.git
synced 2025-09-05 18:25:57 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
539
tests/fw/services/bluetooth/test_ble_hrm.c
Normal file
539
tests/fw/services/bluetooth/test_ble_hrm.c
Normal 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));
|
||||
}
|
143
tests/fw/services/bluetooth/test_ble_root_keys.c
Normal file
143
tests/fw/services/bluetooth/test_ble_root_keys.c
Normal 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));
|
||||
}
|
1399
tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c
Normal file
1399
tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
207
tests/fw/services/bluetooth/test_local_addr.c
Normal file
207
tests/fw/services/bluetooth/test_local_addr.c
Normal 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();
|
||||
}
|
66
tests/fw/services/bluetooth/wscript
Normal file
66
tests/fw/services/bluetooth/wscript
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue