@@ -273,9 +273,18 @@ static uint8_t s_alarms_app_opened = 0;
273273
274274#define PREF_KEY_ACTIVITY_HRM_PREFERENCES "hrmPreferences"
275275static ActivityHRMSettings s_activity_hrm_preferences = ACTIVITY_HRM_DEFAULT_PREFERENCES ;
276+ #if !UNITTEST
277+ _Static_assert (sizeof (ActivityHRMSettings ) == 3 ,
278+ "ActivityHRMSettings changed size; prv_migrate_activity_hrm_prefs() only widens "
279+ "records whose fields were appended!" );
280+ #endif
276281
277282#define PREF_KEY_ACTIVITY_SPO2_PREFERENCES "spo2Preferences"
278283static ActivitySpO2Settings s_activity_spo2_preferences = ACTIVITY_SPO2_DEFAULT_PREFERENCES ;
284+ #if !UNITTEST
285+ _Static_assert (sizeof (ActivitySpO2Settings ) == 1 ,
286+ "sizeof(ActivitySpO2Settings) grew, stored records need migrating!" );
287+ #endif
279288
280289// Blood oxygen on/off. Synced from the phone (and the on-watch toggle) as a
281290// single bool byte under its own key, mirroring the mobile app's
@@ -1009,6 +1018,28 @@ static void prv_convert_deprecated_dynamic_intensity_key(SettingsFile *file) {
10091018}
10101019#endif
10111020
1021+ // hrmPreferences gained fields twice without a version tag, and the loader below only
1022+ // accepts an exact size match. A short record would otherwise be skipped on every boot,
1023+ // silently replacing the user's settings with the defaults (HR on, 10 minute interval).
1024+ // Fields have only ever been appended, so a short record is a valid prefix of the
1025+ // current struct and the trailing fields keep their defaults.
1026+ static void prv_migrate_activity_hrm_prefs (SettingsFile * file ) {
1027+ const size_t key_len = sizeof (PREF_KEY_ACTIVITY_HRM_PREFERENCES );
1028+ const int stored_len = settings_file_get_len (file , PREF_KEY_ACTIVITY_HRM_PREFERENCES , key_len );
1029+ if (stored_len <= 0 || stored_len >= (int )sizeof (ActivityHRMSettings )) {
1030+ return ;
1031+ }
1032+
1033+ ActivityHRMSettings settings = ACTIVITY_HRM_DEFAULT_PREFERENCES ;
1034+ if (settings_file_get (file , PREF_KEY_ACTIVITY_HRM_PREFERENCES , key_len , & settings , stored_len ) !=
1035+ S_SUCCESS ) {
1036+ return ;
1037+ }
1038+
1039+ PBL_LOG_INFO ("Widening %d byte hrmPreferences record" , stored_len );
1040+ settings_file_set (file , PREF_KEY_ACTIVITY_HRM_PREFERENCES , key_len , & settings , sizeof (settings ));
1041+ }
1042+
10121043// ------------------------------------------------------------------------------------
10131044static void prv_pref_set (const char * key , const void * value , size_t val_len );
10141045
@@ -1037,6 +1068,7 @@ void shell_prefs_init(void) {
10371068#ifdef CONFIG_DYNAMIC_BACKLIGHT
10381069 prv_convert_deprecated_dynamic_intensity_key (& file );
10391070#endif
1071+ prv_migrate_activity_hrm_prefs (& file );
10401072
10411073#if !TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED
10421074 {
0 commit comments