mirror of
https://github.com/google/pebble.git
synced 2025-05-05 09:21:40 -04:00
290 lines
7.8 KiB
C
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
|