diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d15acf..e82a3286 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## 3.31.0 (in progress) - Add encrypted dataformats 0x08 (recommended) and 0xFA (legacy). + - Add contactless bootloader entry through endpoint 0x2A - Fix resource leak in flash storage ## 3.30.4 diff --git a/src/app_comms.c b/src/app_comms.c index b1f8397e..fabcb514 100644 --- a/src/app_comms.c +++ b/src/app_comms.c @@ -3,6 +3,7 @@ #include "app_heartbeat.h" #include "app_led.h" #include "app_sensor.h" +#include "app_testing.h" #include "ruuvi_boards.h" #include "ruuvi_endpoints.h" #include "ruuvi_interface_communication.h" @@ -41,14 +42,10 @@ #define CONN_PARAM_UPDATE_DELAY_MS (30U * 1000U) //!< Delay before switching to faster conn params in long ops. #if APP_COMMS_BIDIR_ENABLED -#ifndef CEEDLING -static -#endif -bool m_config_enabled_on_curr_conn; //!< This connection has config enabled. -#ifndef CEEDLING -static -#endif -bool m_config_enabled_on_next_conn; //!< Next connection has config enabled. +TESTABLE_STATIC bool +m_config_enabled_on_curr_conn; //!< This connection has config enabled. +TESTABLE_STATIC bool +m_config_enabled_on_next_conn; //!< Next connection has config enabled. #endif #ifndef CEEDLING @@ -59,17 +56,10 @@ typedef struct } mode_changes_t; #endif +static volatile bool m_tx_done; //!< Flag for data transfer done static uint8_t m_bleadv_repeat_count; //!< Number of times to repeat advertisement. - -#ifndef CEEDLING -static -#endif -ri_timer_id_t m_comm_timer; //!< Timer for communication mode changes. - -#ifndef CEEDLING -static -#endif -mode_changes_t m_mode_ops; //!< Pending mode changes. +TESTABLE_STATIC ri_timer_id_t m_comm_timer; //!< Timer for communication mode changes. +TESTABLE_STATIC mode_changes_t m_mode_ops; //!< Pending mode changes. uint8_t app_comms_bleadv_send_count_get (void) { @@ -133,10 +123,148 @@ static uint8_t initial_adv_send_count (void) #if APP_COMMS_BIDIR_ENABLED -static void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, - size_t data_len) +// Returns true if command is allowed. +// If Configuration is not allowed, command type must be read. +static bool command_is_authorized (const uint8_t op) +{ + return (RE_STANDARD_OP_READ_BIT & op) || m_config_enabled_on_curr_conn; +} + +static rd_status_t reply_unauthorized (const ri_comm_xfer_fp_t reply_fp, + const uint8_t * const raw_message) +{ + ri_comm_message_t msg = {0}; + msg.data_length = RE_STANDARD_MESSAGE_LENGTH; + msg.repeat_count = 1; + msg.data[RE_STANDARD_DESTINATION_INDEX] = raw_message[RE_STANDARD_SOURCE_INDEX]; + msg.data[RE_STANDARD_SOURCE_INDEX] = raw_message[RE_STANDARD_DESTINATION_INDEX]; + msg.data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_OP_UNAUTHORIZED; + + for (uint8_t ii = RE_STANDARD_PAYLOAD_START_INDEX; + ii < RE_STANDARD_MESSAGE_LENGTH; ii++) + { + msg.data[ii] = 0xFFU; + } + + return reply_fp (&msg); +} + +static rd_status_t reply_authorized (const ri_comm_xfer_fp_t reply_fp, + const uint8_t * const raw_message) +{ + ri_comm_message_t msg = {0}; + msg.data_length = RE_STANDARD_MESSAGE_LENGTH; + msg.repeat_count = 1; + msg.data[RE_STANDARD_DESTINATION_INDEX] = raw_message[RE_STANDARD_SOURCE_INDEX]; + msg.data[RE_STANDARD_SOURCE_INDEX] = raw_message[RE_STANDARD_DESTINATION_INDEX]; + msg.data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_VALUE_WRITE; + + for (uint8_t ii = RE_STANDARD_PAYLOAD_START_INDEX; + ii < RE_STANDARD_MESSAGE_LENGTH; ii++) + { + msg.data[ii] = raw_message[ii]; + } + + return reply_fp (&msg); +} + +/** + * @brief Allow configuration commands on next connection. + * + * Has side effect of disconnecting current GATT connecction due to need to re-init + * GATT services. + * + * @param[in] enable True to enable configuration on connection, false to disable. + */ +static rd_status_t enable_config_on_next_conn (const bool enable) { rd_status_t err_code = RD_SUCCESS; + // Kicks out current connection. + err_code |= app_comms_ble_uninit(); + err_code |= app_comms_ble_init (!enable); + m_config_enabled_on_next_conn = enable; + app_led_configuration_signal (enable); + return err_code; +} + +static rd_status_t wait_for_tx_done (const uint32_t timeout_ms) +{ + rd_status_t err_code = RD_SUCCESS; + const uint64_t start = ri_rtc_millis(); + const uint64_t timeout = start + timeout_ms; + + while ( (!m_tx_done) && (timeout > ri_rtc_millis())) + { + ri_yield(); + } + + if (!m_tx_done) + { + err_code |= RD_ERROR_TIMEOUT; + } + + m_tx_done = false; + return err_code; +} + +static rd_status_t password_check (const ri_comm_xfer_fp_t reply_fp, + const uint8_t * const raw_message) +{ + rd_status_t err_code = RD_SUCCESS; + uint64_t entered_password = 0U; + bool auth_ok = false; + // Use non-zero initial value so passwords won't match on error + uint64_t current_password = 0xDEADBEEF12345678U; + re_op_t op = (re_op_t) raw_message[RE_STANDARD_OPERATION_INDEX]; + + if (RE_STANDARD_VALUE_READ == op) + { + err_code |= ri_comm_id_get (¤t_password); + + // Convert data to U64 + for (uint8_t ii = RE_STANDARD_PAYLOAD_START_INDEX; + ii < RE_STANDARD_MESSAGE_LENGTH; ii++) + { + uint8_t byte_offset = RE_STANDARD_MESSAGE_LENGTH - ii - 1U; + uint8_t bit_offset = byte_offset * 8U; + uint64_t password_part = raw_message[ii]; + password_part = password_part << bit_offset; + entered_password += password_part; + } + } + + m_tx_done = false; + + if (entered_password == current_password) + { + err_code |= reply_authorized (reply_fp, raw_message); + auth_ok = true; + } + else + { + err_code |= reply_unauthorized (reply_fp, raw_message); + auth_ok = false; + } + + err_code |= wait_for_tx_done (BLOCKING_COMM_TIMEOUT_MS); + + if (auth_ok) + { + app_comms_configure_next_enable (); + } + else + { + app_comms_configure_next_disable (); + } + + return err_code; +} + +TESTABLE_STATIC void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, + size_t data_len) +{ + rd_status_t err_code = RD_SUCCESS; + const uint8_t * const raw_message = (uint8_t *) p_data; if (NULL == p_data) { @@ -146,6 +274,10 @@ static void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, { err_code |= RD_ERROR_INVALID_PARAM; } + else if (!command_is_authorized (raw_message[RE_STANDARD_OPERATION_INDEX])) + { + err_code |= reply_unauthorized (reply_fp, raw_message); + } else { // Stop heartbeat processing. @@ -153,7 +285,6 @@ static void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, // Switch GATT to faster params. err_code |= ri_gatt_params_request (RI_GATT_TURBO, CONN_PARAM_UPDATE_DELAY_MS); // Parse message type. - const uint8_t * const raw_message = (uint8_t *) p_data; re_type_t type = raw_message[RE_STANDARD_DESTINATION_INDEX]; // Route message to proper handler. @@ -174,6 +305,10 @@ static void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, err_code |= app_sensor_handle (reply_fp, raw_message, data_len); break; + case RE_SEC_PASS: + err_code |= password_check (reply_fp, raw_message); + break; + default: break; } @@ -187,34 +322,6 @@ static void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, RD_ERROR_CHECK (err_code, ~RD_ERROR_FATAL); } -/** - * @brief Allow configuration commands on next connection. - * - * Has side effect of disconnecting current GATT connecction due to need to re-init - * GATT services. - * - * @param[in] enable True to enable configuration on connection, false to disable. - */ -static rd_status_t enable_config_on_next_conn (const bool enable) -{ - rd_status_t err_code = RD_SUCCESS; - // Kicks out current connection. - err_code |= app_comms_ble_uninit(); - err_code |= app_comms_ble_init (!enable); - m_config_enabled_on_next_conn = enable; - - if (enable) - { - app_led_configuration_signal (true); - } - else - { - app_led_configuration_signal (false); - } - - return err_code; -} - /** * @brief Configures this connection, call this in on_connected handler. */ @@ -241,10 +348,7 @@ static void config_cleanup_on_disconnect (void) #if APP_GATT_ENABLED -#ifndef CEEDLING -static -#endif -void handle_gatt_data (void * p_data, uint16_t data_len) +TESTABLE_STATIC void handle_gatt_data (void * p_data, uint16_t data_len) { handle_comms (&rt_gatt_send_asynchronous, p_data, data_len); } @@ -252,10 +356,7 @@ void handle_gatt_data (void * p_data, uint16_t data_len) /** * @brief Disable advertising for GATT connection and setup current connection. */ -#ifndef CEEDLING -static -#endif -void handle_gatt_connected (void * p_data, uint16_t data_len) +TESTABLE_STATIC void handle_gatt_connected (void * p_data, uint16_t data_len) { rd_status_t err_code = RD_SUCCESS; // Disables advertising for GATT, does not kick current connetion out. @@ -269,10 +370,7 @@ void handle_gatt_connected (void * p_data, uint16_t data_len) /** * @brief Callback when GATT is connected */ -#ifndef CEEDLING -static -#endif -void on_gatt_connected_isr (void * p_data, size_t data_len) +TESTABLE_STATIC void on_gatt_connected_isr (void * p_data, size_t data_len) { rd_status_t err_code = RD_SUCCESS; err_code |= ri_scheduler_event_put (p_data, (uint16_t) data_len, @@ -282,10 +380,7 @@ void on_gatt_connected_isr (void * p_data, size_t data_len) RD_ERROR_CHECK (err_code, RD_SUCCESS); } -#ifndef CEEDLING -static -#endif -void handle_gatt_disconnected (void * p_data, uint16_t data_len) +TESTABLE_STATIC void handle_gatt_disconnected (void * p_data, uint16_t data_len) { rd_status_t err_code = RD_SUCCESS; config_cleanup_on_disconnect(); @@ -293,10 +388,7 @@ void handle_gatt_disconnected (void * p_data, uint16_t data_len) } /** @brief Callback when GATT is disconnected" */ -#ifndef CEEDLING -static -#endif -void on_gatt_disconnected_isr (void * p_data, size_t data_len) +TESTABLE_STATIC void on_gatt_disconnected_isr (void * p_data, size_t data_len) { rd_status_t err_code = RD_SUCCESS; err_code |= ri_scheduler_event_put (p_data, (uint16_t) data_len, @@ -309,16 +401,24 @@ void on_gatt_disconnected_isr (void * p_data, size_t data_len) * * Schedule handling incoming message and replying back via given function pointer. */ -#ifndef CEEDLING -static -#endif -void on_gatt_data_isr (void * p_data, size_t data_len) +TESTABLE_STATIC void on_gatt_data_isr (void * p_data, size_t data_len) { rd_status_t err_code = RD_SUCCESS; err_code |= ri_scheduler_event_put (p_data, (uint16_t) data_len, &handle_gatt_data); RD_ERROR_CHECK (err_code, RD_SUCCESS); } +/** + * @brief Callback when data is sent via GATT + * + * Schedule handling incoming message and replying back via given function pointer. + */ +TESTABLE_STATIC void on_gatt_tx_done_isr (void * p_data, size_t data_len) +{ + m_tx_done = true; +} + + static rd_status_t ble_name_string_create (char * const name_str, const size_t name_len) { uint64_t radio_addr = 0; @@ -332,10 +432,7 @@ static rd_status_t ble_name_string_create (char * const name_str, const size_t n #if APP_NFC_ENABLED -#ifndef CEEDLING -static -#endif -void handle_nfc_connected (void * p_data, uint16_t data_len) +TESTABLE_STATIC void handle_nfc_connected (void * p_data, uint16_t data_len) { rd_status_t err_code = RD_SUCCESS; // Kick BLE connection to not allow it to configure tag @@ -345,20 +442,14 @@ void handle_nfc_connected (void * p_data, uint16_t data_len) RD_ERROR_CHECK (err_code, RD_SUCCESS); } -#ifndef CEEDLING -static -#endif -void on_nfc_connected_isr (void * p_data, size_t data_len) +TESTABLE_STATIC void on_nfc_connected_isr (void * p_data, size_t data_len) { rd_status_t err_code = RD_SUCCESS; err_code |= ri_scheduler_event_put (p_data, (uint16_t) data_len, &handle_nfc_connected); RD_ERROR_CHECK (err_code, RD_SUCCESS); } -#ifndef CEEDLING -static -#endif -void handle_nfc_disconnected (void * p_data, uint16_t data_len) +TESTABLE_STATIC void handle_nfc_disconnected (void * p_data, uint16_t data_len) { rd_status_t err_code = RD_SUCCESS; config_cleanup_on_disconnect(); @@ -367,16 +458,19 @@ void handle_nfc_disconnected (void * p_data, uint16_t data_len) } /** @brief Callback when NFC is disconnected" */ -#ifndef CEEDLING -static -#endif -void on_nfc_disconnected_isr (void * p_data, size_t data_len) +TESTABLE_STATIC void on_nfc_disconnected_isr (void * p_data, size_t data_len) { rd_status_t err_code = RD_SUCCESS; err_code |= ri_scheduler_event_put (p_data, (uint16_t) data_len, &handle_nfc_disconnected); RD_ERROR_CHECK (err_code, RD_SUCCESS); } + +/** @brief Callback when NFC has sent data" */ +TESTABLE_STATIC void on_nfc_tx_done_isr (void * p_data, size_t data_len) +{ + m_tx_done = true; +} #endif rd_status_t app_comms_configure_next_enable (void) @@ -389,10 +483,14 @@ rd_status_t app_comms_configure_next_enable (void) return err_code; } -#ifndef CEEDLING -static -#endif -void handle_config_disable (void * p_data, uint16_t data_len) +rd_status_t app_comms_configure_next_disable (void) +{ + rd_status_t err_code = RD_SUCCESS; + err_code |= enable_config_on_next_conn (false); + return err_code; +} + +TESTABLE_STATIC void handle_config_disable (void * p_data, uint16_t data_len) { rd_status_t err_code = RD_SUCCESS; @@ -454,14 +552,12 @@ static rd_status_t nfc_init (ri_comm_dis_init_t * const p_dis) err_code |= rt_nfc_init (p_dis); rt_nfc_set_on_connected_isr (&on_nfc_connected_isr); rt_nfc_set_on_disconn_isr (&on_nfc_disconnected_isr); + rt_nfc_set_on_sent_isr (&on_nfc_tx_done_isr); #endif return err_code; } -#ifndef CEEDLING -static -#endif -void comm_mode_change_isr (void * const p_context) +TESTABLE_STATIC void comm_mode_change_isr (void * const p_context) { mode_changes_t * const p_change = (mode_changes_t *) p_context; @@ -526,6 +622,7 @@ static rd_status_t gatt_init (const ri_comm_dis_init_t * const p_dis, const bool rt_gatt_set_on_connected_isr (&on_gatt_connected_isr); rt_gatt_set_on_disconn_isr (&on_gatt_disconnected_isr); rt_gatt_set_on_received_isr (&on_gatt_data_isr); + rt_gatt_set_on_sent_isr (&on_gatt_tx_done_isr); err_code |= rt_gatt_adv_enable(); #endif return err_code; diff --git a/src/app_comms.h b/src/app_comms.h index a0cb51c7..bde5e291 100644 --- a/src/app_comms.h +++ b/src/app_comms.h @@ -83,7 +83,8 @@ void app_comms_bleadv_send_count_set (const uint8_t count); * - Configuration commands are allowed. * * The configuration is enabled for the next communication connection and is disabled - * after disconnection or once timeout is triggered. + * after disconnection or once timeout is triggered. This kicks current Bluetooth + * connection (if any). * * This also calls app_led to indicate configuration is enabled. * @@ -92,6 +93,21 @@ void app_comms_bleadv_send_count_set (const uint8_t count); */ rd_status_t app_comms_configure_next_enable (void); +/** + * @brief Kick current BLE connection (if any) and disable security-sensitive configuration options. + * + * After calling this function: + * - DFU service is disabled + * - Device ID is not readable through DIS/Serial Number characteristic. + * - Configuration commands are not allowed. + * + * This also calls app_led to indicate configuration is disabled. + * + * @retval RD_SUCCESS if configuration mode was entered. + * @retval RD_ERROR_INVALID_STATE if communications are not initialized. + */ +rd_status_t app_comms_configure_next_disable (void); + /** * @brief Enable Bluetooth on device * @@ -159,6 +175,7 @@ typedef struct void on_gatt_connected_isr (void * p_data, size_t data_len); void on_gatt_disconnected_isr (void * p_data, size_t data_len); void on_gatt_data_isr (void * p_data, size_t data_len); +void on_gatt_tx_done_isr (void * p_data, size_t data_len); void handle_config_disable (void * p_data, uint16_t data_len); void handle_gatt_data (void * p_data, uint16_t data_len); void handle_gatt_connected (void * p_data, uint16_t data_len); @@ -167,7 +184,9 @@ void handle_nfc_connected (void * p_data, uint16_t data_len); void handle_nfc_disconnected (void * p_data, uint16_t data_len); void on_nfc_connected_isr (void * p_data, size_t data_len); void on_nfc_disconnected_isr (void * p_data, size_t data_len); +void on_nfc_tx_done_isr (void * p_data, size_t data_len); void comm_mode_change_isr (void * const p_context); +void handle_comms (const ri_comm_xfer_fp_t reply_fp, void * p_data, size_t data_len); #endif diff --git a/testing.h b/src/app_testing.h similarity index 98% rename from testing.h rename to src/app_testing.h index 824dac3f..5fe9440a 100644 --- a/testing.h +++ b/src/app_testing.h @@ -1,5 +1,5 @@ /** - * @file testing.h + * @file app_testing.h * @copyright Ruuvi Innovations Ltd, license BSD-3-Clause. * @author DG12 * @date 2021-10-22 diff --git a/src/main.h b/src/main.h index 4a19ba73..490a64c4 100644 --- a/src/main.h +++ b/src/main.h @@ -24,7 +24,7 @@ // Submodule requirements #define RUUVI_BOARDS_REQ "3.7.1" #define RUUVI_DRIVERS_REQ "3.9.0" -#define RUUVI_ENDPOINTS_REQ "3.1.0" +#define RUUVI_ENDPOINTS_REQ "3.2.0" #define RUUVI_LIBRARIES_REQ "3.0.0" #define APP_SELFTEST_OK_DELAY_MS (1000U) //!< time to show "ok" led. diff --git a/src/ruuvi.endpoints.c b/src/ruuvi.endpoints.c index b4362cc9..fb05ea0b 160000 --- a/src/ruuvi.endpoints.c +++ b/src/ruuvi.endpoints.c @@ -1 +1 @@ -Subproject commit b4362cc962dba863605d054a13b8bc2f9c0cd190 +Subproject commit fb05ea0baeccc749c605a1484e7a175e10a5e68d diff --git a/test/test_app_comms.c b/test/test_app_comms.c index d8ebddaa..214ebd17 100644 --- a/test/test_app_comms.c +++ b/test/test_app_comms.c @@ -11,6 +11,7 @@ #include "mock_ruuvi_interface_communication_ble_advertising.h" #include "mock_ruuvi_interface_communication_ble_gatt.h" #include "mock_ruuvi_interface_communication_radio.h" +#include "mock_ruuvi_interface_communication.h" #include "mock_ruuvi_interface_rtc.h" #include "mock_ruuvi_interface_scheduler.h" #include "mock_ruuvi_interface_timer.h" @@ -54,6 +55,11 @@ static rd_status_t dummy_comm (ri_comm_message_t * const msg) err_code = RD_ERROR_NO_MEM; } + if (RD_SUCCESS == err_code) + { + on_gatt_tx_done_isr (NULL, 0); + } + m_expect_sends++; return err_code; } @@ -138,6 +144,7 @@ static void gatt_init_Expect (ri_comm_dis_init_t * p_dis, const bool secure) rt_gatt_set_on_connected_isr_Expect (&on_gatt_connected_isr); rt_gatt_set_on_disconn_isr_Expect (&on_gatt_disconnected_isr); rt_gatt_set_on_received_isr_Expect (&on_gatt_data_isr); + rt_gatt_set_on_sent_isr_Expect (&on_gatt_tx_done_isr); rt_gatt_adv_enable_ExpectAndReturn (RD_SUCCESS); #endif } @@ -148,6 +155,7 @@ static void nfc_init_Expect (ri_comm_dis_init_t * p_dis) rt_nfc_init_ExpectWithArrayAndReturn (p_dis, 1, RD_SUCCESS); rt_nfc_set_on_connected_isr_Expect (&on_nfc_connected_isr); rt_nfc_set_on_disconn_isr_Expect (&on_nfc_disconnected_isr); + rt_nfc_set_on_sent_isr_Expect (&on_nfc_tx_done_isr); #endif } @@ -202,6 +210,15 @@ static void app_comms_configure_next_enable_Expect (void) RD_SUCCESS); } +static void app_comms_configure_next_disable_Expect (void) +{ + static ri_comm_dis_init_t ble_dis; + memset (&ble_dis, 0, sizeof (ble_dis)); + app_comms_ble_uninit_Expect(); + app_comms_ble_init_Expect (true, &ble_dis); + app_led_configuration_signal_Expect (false); +} + static void connection_cleanup_Expect (void) { static ri_comm_dis_init_t ble_dis; @@ -282,6 +299,7 @@ void test_handle_gatt_sensor_op_acc (void) { uint8_t mock_data[RE_STANDARD_MESSAGE_LENGTH] = {0}; mock_data[RE_STANDARD_DESTINATION_INDEX] = RE_STANDARD_DESTINATION_ACCELERATION; + mock_data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_VALUE_READ; app_heartbeat_stop_ExpectAndReturn (RD_SUCCESS); ri_gatt_params_request_ExpectAndReturn (RI_GATT_TURBO, (30 * 1000), RD_SUCCESS); app_sensor_handle_ExpectAndReturn (&rt_gatt_send_asynchronous, mock_data, @@ -292,6 +310,66 @@ void test_handle_gatt_sensor_op_acc (void) handle_gatt_data (mock_data, sizeof (mock_data)); } +void test_handle_gatt_password_ok (void) +{ + uint64_t password = 0x1122334455667788; + uint8_t mock_data[RE_STANDARD_MESSAGE_LENGTH] = {0}; + mock_data[RE_STANDARD_DESTINATION_INDEX] = RE_STANDARD_DESTINATION_PASSWORD; + mock_data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_VALUE_READ; + mock_data[3] = 0x11; + mock_data[4] = 0x22; + mock_data[5] = 0x33; + mock_data[6] = 0x44; + mock_data[7] = 0x55; + mock_data[8] = 0x66; + mock_data[9] = 0x77; + mock_data[10] = 0x88; + app_heartbeat_stop_ExpectAndReturn (RD_SUCCESS); + ri_gatt_params_request_ExpectAndReturn (RI_GATT_TURBO, (30 * 1000), RD_SUCCESS); + ri_comm_id_get_ExpectAnyArgsAndReturn (RD_SUCCESS); + ri_comm_id_get_ReturnThruPtr_id (&password); + ri_rtc_millis_ExpectAndReturn (0); + app_comms_configure_next_enable_Expect (); + ri_gatt_params_request_ExpectAndReturn (RI_GATT_LOW_POWER, 0, RD_ERROR_INVALID_STATE); + app_heartbeat_start_ExpectAndReturn (RD_SUCCESS); + handle_comms (&dummy_comm, mock_data, sizeof (mock_data)); + TEST_ASSERT (m_config_enabled_on_next_conn); +} + +void test_handle_gatt_password_denied (void) +{ + uint64_t password = 0x1122334455667788; + uint8_t mock_data[RE_STANDARD_MESSAGE_LENGTH] = {0}; + mock_data[RE_STANDARD_DESTINATION_INDEX] = RE_STANDARD_DESTINATION_PASSWORD; + mock_data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_VALUE_READ; + app_heartbeat_stop_ExpectAndReturn (RD_SUCCESS); + ri_gatt_params_request_ExpectAndReturn (RI_GATT_TURBO, (30 * 1000), RD_SUCCESS); + ri_comm_id_get_ExpectAnyArgsAndReturn (RD_SUCCESS); + ri_comm_id_get_ReturnThruPtr_id (&password); + ri_rtc_millis_ExpectAndReturn (0); + app_comms_configure_next_disable_Expect (); + ri_gatt_params_request_ExpectAndReturn (RI_GATT_LOW_POWER, 0, RD_ERROR_INVALID_STATE); + app_heartbeat_start_ExpectAndReturn (RD_SUCCESS); + handle_comms (&dummy_comm, mock_data, sizeof (mock_data)); + TEST_ASSERT (!m_config_enabled_on_next_conn); +} + +#if 0 +void test_handle_gatt_unauthorized (void) +{ + uint8_t mock_data[RE_STANDARD_MESSAGE_LENGTH] = {0}; + mock_data[RE_STANDARD_DESTINATION_INDEX] = RE_STANDARD_DESTINATION_PASSWORD; + mock_data[RE_STANDARD_OPERATION_INDEX] = RE_STANDARD_VALUE_READ; + app_heartbeat_stop_ExpectAndReturn (RD_SUCCESS); + ri_gatt_params_request_ExpectAndReturn (RI_GATT_TURBO, (30 * 1000), RD_SUCCESS); + ri_comm_id_get_ExpectAnyArgsAndReturn() + ri_gatt_params_request_ExpectAndReturn (RI_GATT_LOW_POWER, 0, RD_SUCCESS); + app_heartbeat_start_ExpectAndReturn (RD_SUCCESS); + RD_ERROR_CHECK_EXPECT (RD_SUCCESS, ~RD_ERROR_FATAL); + handle_gatt_data (mock_data, sizeof (mock_data)); +} +#endif + void test_handle_gatt_null_data (void) { RD_ERROR_CHECK_EXPECT (RD_ERROR_NULL, ~RD_ERROR_FATAL);