pebble/src/fw/services/normal/blob_db/weather_db.c
2025-01-27 11:38:16 -08:00

290 lines
7.8 KiB
C

/*
* 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 "weather_db.h"
#include "kernel/pbl_malloc.h"
#include "os/mutex.h"
#include "services/normal/filesystem/pfs.h"
#include "services/normal/settings/settings_file.h"
#include "services/normal/weather/weather_service.h"
#include "services/normal/weather/weather_types.h"
#include "system/passert.h"
#include "util/units.h"
#define SETTINGS_FILE_NAME "weatherdb"
#define SETTINGS_FILE_SIZE (KiBYTES(30))
static struct {
SettingsFile settings_file;
PebbleMutex *mutex;
} s_weather_db;
typedef struct WeatherDBIteratorData {
WeatherDBIteratorCallback cb;
void *cb_ctx;
} WeatherDBIteratorData;
///////////////////////////
// Weather DB API
///////////////////////////
static status_t prv_lock_mutex_and_open_file(void) {
mutex_lock(s_weather_db.mutex);
status_t rv = settings_file_open(&s_weather_db.settings_file,
SETTINGS_FILE_NAME,
SETTINGS_FILE_SIZE);
if (rv != S_SUCCESS) {
mutex_unlock(s_weather_db.mutex);
}
return rv;
}
static void prv_close_file_and_unlock_mutex(void) {
settings_file_close(&s_weather_db.settings_file);
mutex_unlock(s_weather_db.mutex);
}
static bool prv_weather_db_for_each_cb(SettingsFile *file, SettingsRecordInfo *info,
void *context) {
if ((info->val_len == 0) || (info->key_len != sizeof(WeatherDBKey))) {
return true;
}
WeatherDBKey key;
info->get_key(file, &key, info->key_len);
WeatherDBEntry *entry = task_zalloc_check(info->val_len);
info->get_val(file, entry, info->val_len);
if (entry->version != WEATHER_DB_CURRENT_VERSION) {
PBL_LOG(LOG_LEVEL_WARNING, "Version mismatch! Entry version: %" PRIu8 ", WeatherDB version: %u",
entry->version, WEATHER_DB_CURRENT_VERSION);
goto cleanup;
}
const WeatherDBIteratorData *cb_data = context;
cb_data->cb(&key, entry, cb_data->cb_ctx);
cleanup:
task_free(entry);
return true;
}
status_t weather_db_for_each(WeatherDBIteratorCallback callback, void *context) {
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
WeatherDBIteratorData data = (WeatherDBIteratorData) {
.cb = callback,
.cb_ctx = context
};
settings_file_each(&s_weather_db.settings_file,
prv_weather_db_for_each_cb,
&data);
prv_close_file_and_unlock_mutex();
return S_SUCCESS;
}
/////////////////////////
// Blob DB API
/////////////////////////
void weather_db_init(void) {
memset(&s_weather_db, 0, sizeof(s_weather_db));
s_weather_db.mutex = mutex_create();
}
status_t weather_db_flush(void) {
if (!weather_service_supported_by_phone()) {
// return E_RANGE, so the phone receives BLOB_DB_INVALID_DATABASE_ID and stops sending
// unwelcome weather records
return E_RANGE;
}
mutex_lock(s_weather_db.mutex);
pfs_remove(SETTINGS_FILE_NAME);
mutex_unlock(s_weather_db.mutex);
return S_SUCCESS;
}
status_t weather_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) {
if (!weather_service_supported_by_phone()) {
return E_RANGE;
}
if (key_len != sizeof(WeatherDBKey) ||
val_len < (int) MIN_ENTRY_SIZE ||
val_len > (int) MAX_ENTRY_SIZE) {
return E_INVALID_ARGUMENT;
}
const WeatherDBEntry *entry = (WeatherDBEntry *)val;
if (entry->version != WEATHER_DB_CURRENT_VERSION) {
PBL_LOG(LOG_LEVEL_WARNING,
"Version mismatch on insert! Entry version: %" PRIu8 ", WeatherDB version: %u",
entry->version, WEATHER_DB_CURRENT_VERSION);
return E_INVALID_ARGUMENT;
}
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
rv = settings_file_set(&s_weather_db.settings_file, key, key_len, val, val_len);
prv_close_file_and_unlock_mutex();
return rv;
}
int weather_db_get_len(const uint8_t *key, int key_len) {
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return 0;
}
PBL_ASSERTN(key_len == sizeof(WeatherDBKey));
int entry_len = settings_file_get_len(&s_weather_db.settings_file, key, key_len);
prv_close_file_and_unlock_mutex();
return entry_len;
}
status_t weather_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) {
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
PBL_ASSERTN(key_len == sizeof(WeatherDBKey));
rv = settings_file_get(&s_weather_db.settings_file, key, key_len, val_out, val_out_len);
if (((WeatherDBEntry*)val_out)->version != WEATHER_DB_CURRENT_VERSION) {
// We might as well clear out the stale entry
PBL_LOG(LOG_LEVEL_WARNING, "Read an old weather DB entry");
settings_file_delete(&s_weather_db.settings_file, key, key_len);
rv = E_DOES_NOT_EXIST;
}
prv_close_file_and_unlock_mutex();
return rv;
}
status_t weather_db_delete(const uint8_t *key, int key_len) {
if (!weather_service_supported_by_phone()) {
return E_RANGE;
}
if (key_len != sizeof(WeatherDBKey)) {
return E_INVALID_ARGUMENT;
}
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
if (!settings_file_exists(&s_weather_db.settings_file, key, key_len)) {
prv_close_file_and_unlock_mutex();
return E_DOES_NOT_EXIST;
}
rv = settings_file_delete(&s_weather_db.settings_file, key, key_len);
prv_close_file_and_unlock_mutex();
return rv;
}
//-----------------------------------------------------------------------------
// Testing code only
#if UNITTEST
// SettingsFile Helpers
typedef struct {
uint16_t key_count;
WeatherDBKey *keys;
} SettingsFileEachKeyHelper;
static bool prv_each_inspect_keys(SettingsFile *file, SettingsRecordInfo *info, void *context) {
if ((info->val_len == 0) || (info->key_len != sizeof(WeatherDBKey))) {
// Invalid key, continue iterating
return true;
}
SettingsFileEachKeyHelper *key_helper = context;
if (key_helper->keys != NULL) {
info->get_key(file, (uint8_t *)&key_helper->keys[key_helper->key_count], sizeof(WeatherDBKey));
}
key_helper->key_count++;
// Continue iterating
return true;
}
status_t weather_db_get_num_keys(uint16_t *val_out) {
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
SettingsFileEachKeyHelper key_helper = {
.key_count = 0,
.keys = NULL,
};
settings_file_each(&s_weather_db.settings_file, prv_each_inspect_keys, &key_helper);
*val_out = key_helper.key_count;
prv_close_file_and_unlock_mutex();
return S_SUCCESS;
}
status_t weather_db_get_keys(WeatherDBKey *keys) {
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
SettingsFileEachKeyHelper key_helper = {
.key_count = 0,
.keys = keys,
};
settings_file_each(&s_weather_db.settings_file, prv_each_inspect_keys, &key_helper);
prv_close_file_and_unlock_mutex();
return S_SUCCESS;
}
status_t weather_db_insert_stale(const uint8_t *key, int key_len, const uint8_t *val, int val_len) {
// Quick and dirty insert which doesn't do any error checking. Used to insert stale entries
// for testing
status_t rv = prv_lock_mutex_and_open_file();
if (rv != S_SUCCESS) {
return rv;
}
rv = settings_file_set(&s_weather_db.settings_file, key, key_len, val, val_len);
prv_close_file_and_unlock_mutex();
return rv;
}
#endif