Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/fw/services/hrm/hrm_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ T_STATIC uint32_t prv_num_system_task_events_queued(void) {
&s_manager_state.system_task_event_buffer);
return avail_bytes / sizeof(PebbleHRMEvent);
}

// Used by unit tests
T_STATIC uint32_t prv_get_dropped_events_count(void) {
return s_manager_state.dropped_events;
}
#endif

static void prv_handle_accel_data(void * data) {
Expand Down Expand Up @@ -506,7 +511,10 @@ void hrm_manager_new_data_cb(const HRMData *data) {
kernel_bg_features_sent |= feature;
}
prv_populate_hrm_event(&hrm_event, feature, data);
PBL_ASSERTN(prv_event_put(state, &hrm_event));
if (!prv_event_put(state, &hrm_event)) {
// Consumer queue full (e.g. app not draining events); drop instead of panicking.
++s_manager_state.dropped_events;
}
}

// If this is an app subscription, see if we need to send an "expiring" event. We check
Expand All @@ -516,8 +524,12 @@ void hrm_manager_new_data_cb(const HRMData *data) {
.event_type = HRMEvent_SubscriptionExpiring,
.expiring.session_ref = state->session_ref,
};
PBL_ASSERTN(prv_event_put(state, &hrm_event));
state->sent_expiration_event = true;
if (prv_event_put(state, &hrm_event)) {
state->sent_expiration_event = true;
} else {
// Retry on the next sample rather than panicking.
++s_manager_state.dropped_events;
}
}

if (state->expire_utc && (utc_now >= state->expire_utc)) {
Expand Down
34 changes: 34 additions & 0 deletions tests/fw/services/test_hrm_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern uint32_t prv_num_system_task_events_queued(void);
extern TimerID prv_get_timer_id(void);
extern bool prv_can_turn_sensor_on(void);
extern void prv_charger_event_cb(PebbleEvent *e);
extern uint32_t prv_get_dropped_events_count(void);


// -----------------------------------------------------------------------------
Expand All @@ -63,10 +64,14 @@ bool hrm_is_enabled(HRMDevice *dev) { return s_hrm_state.enabled; }

static const QueueHandle_t FAKE_APP_QUEUE = (QueueHandle_t) 1337;
static uint32_t s_event_count;
static bool s_queue_full;
static PebbleEvent s_events_received[16];
signed portBASE_TYPE xQueueGenericSend(QueueHandle_t xQueue, const void * const pvItemToQueue,
TickType_t xTicksToWait, portBASE_TYPE xCopyPosition) {
cl_assert_equal_i((intptr_t) xQueue, (intptr_t) FAKE_APP_QUEUE);
if (s_queue_full) {
return pdFALSE;
}
if (s_event_count < ARRAY_LENGTH(s_events_received)) {
s_events_received[s_event_count] = *((PebbleEvent *)pvItemToQueue);
}
Expand Down Expand Up @@ -153,6 +158,7 @@ void test_hrm_manager__initialize(void) {

s_activity_prefs_heart_rate_is_enabled = true;
s_event_count = 0;
s_queue_full = false;
s_num_cb_events_1 = 0;
s_num_cb_events_2 = 0;
memset(&s_hrm_state, 0, sizeof(s_hrm_state));
Expand Down Expand Up @@ -746,6 +752,34 @@ void test_hrm_manager__can_turn_sensor_on(void) {
cl_assert(hrm_is_enabled(HRM));
}

// A full app event queue (app not draining events fast enough) must not panic the firmware.
// The HRM sample is dropped and counted, and delivery resumes once the queue drains.
void test_hrm_manager__app_queue_full_drops_without_panic(void) {
stub_pebble_tasks_set_current(PebbleTask_App);

AppInstallId app_id = 1;
const uint16_t expire_s = SECONDS_PER_MINUTE;
HRMSessionRef session_ref = sys_hrm_manager_app_subscribe(app_id, 1, expire_s, HRMFeature_BPM);

// App is not draining its event queue.
s_queue_full = true;

// Must not panic; the event is dropped (not delivered) and counted.
prv_fake_send_new_data();
cl_assert_equal_i(s_event_count, 0);
cl_assert_equal_i(prv_get_dropped_events_count(), 1);

// Subscription survives and delivery resumes once the queue drains.
s_queue_full = false;
prv_fake_send_new_data();
cl_assert_equal_i(s_event_count, 1);
cl_assert_equal_i(s_events_received[0].type, PEBBLE_HRM_EVENT);
cl_assert_equal_i(s_events_received[0].hrm.event_type, HRMEvent_BPM);
cl_assert_equal_i(prv_get_dropped_events_count(), 1);

sys_hrm_manager_unsubscribe(session_ref);
}

// Test that OffWrist quality is delivered immediately without delay
void test_hrm_manager__immediate_off_wrist(void) {
stub_pebble_tasks_set_current(PebbleTask_KernelBackground);
Expand Down
1 change: 0 additions & 1 deletion tests/wscript
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,6 @@ def build(bld):
'test_graphics_fill_circle_8bit.c',
'test_graphics_gtransform_1bit.c',
'test_graphics_gtransform_8bit.c',
'test_hrm_manager.c',
'test_kickstart.c',
'test_launcher_menu_layer.c',
'test_pfs.c',
Expand Down
Loading