blob: c99b269d5eec274ce0027c2b9764b0a20b00540c [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2014 Google, Inc.
* Copyright (C) 2018 The Linux Foundation
*
* 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.
*
******************************************************************************/
#define LOG_TAG "device_iot_config"
#include "device_iot_config_int.h"
#include <base/logging.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <mutex>
#include <string>
#include "btcore/include/module.h"
#include "btif/include/btif_api.h"
#include "btif/include/btif_util.h"
#include "common/init_flags.h"
#include "device/include/device_iot_config.h"
#include "osi/include/alarm.h"
#include "osi/include/allocator.h"
#include "osi/include/compat.h"
#include "osi/include/config.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/properties.h"
extern enum ConfigSource device_iot_config_source;
extern int device_iot_config_devices_loaded;
extern char device_iot_config_time_created[TIME_STRING_LENGTH];
extern std::mutex config_lock; // protects operations on |config|.
extern std::unique_ptr<config_t> config;
extern alarm_t* config_timer;
using bluetooth::common::InitFlags;
static void cleanup() {
alarm_free(config_timer);
config_timer = NULL;
config.reset();
config = NULL;
device_iot_config_source = NOT_LOADED;
}
// Module lifecycle functions
future_t* device_iot_config_module_init(void) {
LOG_INFO("");
std::unique_lock<std::mutex> lock(config_lock);
config_timer = NULL;
config = NULL;
if (device_iot_config_is_factory_reset()) {
device_iot_config_delete_files();
}
config = config_new(IOT_CONFIG_FILE_PATH);
device_iot_config_source = ORIGINAL;
if (!config) {
LOG_WARN("Unable to load config file: %s; using backup.",
IOT_CONFIG_FILE_PATH);
config = config_new(IOT_CONFIG_BACKUP_PATH);
device_iot_config_source = BACKUP;
}
if (!config) {
LOG_ERROR("Unable to load bak file; creating empty config.");
config = config_new_empty();
device_iot_config_source = NEW_FILE;
}
if (!config) {
LOG_ERROR("Unable to allocate a config object.");
cleanup();
return future_new_immediate(FUTURE_FAIL);
}
int version;
if (device_iot_config_source == NEW_FILE) {
version = DEVICE_IOT_INFO_CURRENT_VERSION;
config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
} else {
version = config_get_int(*config, INFO_SECTION, VERSION_KEY, -1);
if (version == -1) {
version = DEVICE_IOT_INFO_FIRST_VERSION;
config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
}
}
if (version != DEVICE_IOT_INFO_CURRENT_VERSION) {
LOG_INFO("Version in file is %d, CURRENT_VERSION is %d ", version,
DEVICE_IOT_INFO_CURRENT_VERSION);
remove(IOT_CONFIG_FILE_PATH);
remove(IOT_CONFIG_BACKUP_PATH);
config.reset();
config = config_new_empty();
if (!config) {
LOG_ERROR("Unable to allocate a config object.");
cleanup();
return future_new_immediate(FUTURE_FAIL);
}
config_set_int(config.get(), INFO_SECTION, VERSION_KEY,
DEVICE_IOT_INFO_CURRENT_VERSION);
device_iot_config_source = NEW_FILE;
}
device_iot_config_devices_loaded = device_iot_config_get_device_num(*config);
LOG_INFO("Devices loaded %d", device_iot_config_devices_loaded);
// Read or set config file creation timestamp
const std::string* time_str =
config_get_string(*config, INFO_SECTION, FILE_CREATED_TIMESTAMP, NULL);
if (time_str != NULL) {
strncpy(device_iot_config_time_created, time_str->c_str(),
TIME_STRING_LENGTH);
} else {
// Read or set config file creation timestamp
time_t current_time = time(NULL);
struct tm* time_created = localtime(&current_time);
if (time_created) {
strftime(device_iot_config_time_created, TIME_STRING_LENGTH,
TIME_STRING_FORMAT, time_created);
config_set_string(config.get(), INFO_SECTION, FILE_CREATED_TIMESTAMP,
std::string(device_iot_config_time_created));
}
}
// TODO: use a non-wake alarm for this once we have
// API support for it. There's no need to wake the system to
// write back to disk.
config_timer = alarm_new("btif.iot.config");
if (!config_timer) {
LOG_ERROR("Unable to create alarm.");
cleanup();
return future_new_immediate(FUTURE_FAIL);
}
return future_new_immediate(FUTURE_SUCCESS);
}
future_t* device_iot_config_module_start_up(void) {
LOG_INFO("");
return future_new_immediate(FUTURE_SUCCESS);
}
future_t* device_iot_config_module_shut_down(void) {
LOG_INFO("");
device_iot_config_flush();
return future_new_immediate(FUTURE_SUCCESS);
}
future_t* device_iot_config_module_clean_up(void) {
LOG_INFO("");
if (config_timer != NULL && alarm_is_scheduled(config_timer))
device_iot_config_flush();
alarm_free(config_timer);
config_timer = NULL;
std::unique_lock<std::mutex> lock(config_lock);
config.reset();
config = NULL;
return future_new_immediate(FUTURE_SUCCESS);
}
EXPORT_SYMBOL module_t device_iot_config_module = {
.name = DEVICE_IOT_CONFIG_MODULE,
.init = device_iot_config_module_init,
.start_up = device_iot_config_module_start_up,
.shut_down = device_iot_config_module_shut_down,
.clean_up = device_iot_config_module_clean_up};
void device_iot_config_write(uint16_t event, UNUSED_ATTR char* p_param) {
if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return;
CHECK(config != NULL);
CHECK(config_timer != NULL);
LOG_INFO("evt=%d", event);
std::unique_lock<std::mutex> lock(config_lock);
if (event == IOT_CONFIG_SAVE_TIMER_FIRED_EVT) {
device_iot_config_set_modified_time();
}
rename(IOT_CONFIG_FILE_PATH, IOT_CONFIG_BACKUP_PATH);
device_iot_config_restrict_device_num(*config);
device_iot_config_sections_sort_by_entry_key(*config,
device_iot_config_compare_key);
config_save(*config, IOT_CONFIG_FILE_PATH);
}
void device_iot_config_sections_sort_by_entry_key(config_t& config,
compare_func comp) {
for (auto& entry : config.sections) {
entry.entries.sort(comp);
}
}
bool device_iot_config_has_key_value(const std::string& section,
const std::string& key,
const std::string& value_str) {
CHECK(config != NULL);
const std::string* stored_value =
config_get_string(*config, section, key, NULL);
if (!stored_value || value_str.compare(*stored_value) != 0) return false;
return true;
}
void device_iot_config_save_async(void) {
if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return;
CHECK(config != NULL);
CHECK(config_timer != NULL);
LOG_VERBOSE("");
alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS,
device_iot_config_timer_save_cb, NULL);
}
int device_iot_config_get_device_num(const config_t& conf) {
if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return 0;
int devices = 0;
for (const auto& entry : conf.sections) {
if (RawAddress::IsValidAddress(entry.name)) {
devices++;
}
}
return devices;
}
void device_iot_config_restrict_device_num(config_t& config) {
int curr_num = device_iot_config_get_device_num(config);
int removed_devices = 0;
int need_remove_devices_num;
if (curr_num <= DEVICES_MAX_NUM_IN_IOT_INFO_FILE) {
return;
}
need_remove_devices_num =
curr_num - DEVICES_MAX_NUM_IN_IOT_INFO_FILE + DEVICES_NUM_MARGIN;
LOG_INFO("curr_num=%d, need_remove_num=%d", curr_num,
need_remove_devices_num);
std::list<section_t>::iterator i = config.sections.begin();
while (i != config.sections.end()) {
if (!RawAddress::IsValidAddress(i->name)) {
++i;
continue;
}
i = config.sections.erase(i);
if (++removed_devices >= need_remove_devices_num) {
break;
}
}
}
bool device_iot_config_compare_key(const entry_t& first,
const entry_t& second) {
bool first_is_profile_key = strncasecmp(first.key.c_str(), "Profile", 7) == 0;
bool second_is_profile_key =
strncasecmp(second.key.c_str(), "Profile", 7) == 0;
if (!first_is_profile_key && !second_is_profile_key) {
return true;
} else if (first_is_profile_key && second_is_profile_key) {
return strcasecmp(first.key.c_str(), second.key.c_str()) <= 0;
} else {
return !first_is_profile_key;
}
}
void device_iot_config_timer_save_cb(UNUSED_ATTR void* data) {
// Moving file I/O to btif context instead of timer callback because
// it usually takes a lot of time to be completed, introducing
// delays during A2DP playback causing blips or choppiness.
LOG_VERBOSE("");
btif_transfer_context(device_iot_config_write,
IOT_CONFIG_SAVE_TIMER_FIRED_EVT, NULL, 0, NULL);
}
void device_iot_config_set_modified_time() {
time_t current_time = time(NULL);
struct tm* time_modified = localtime(&current_time);
char device_iot_config_time_modified[TIME_STRING_LENGTH];
if (time_modified) {
strftime(device_iot_config_time_modified, TIME_STRING_LENGTH,
TIME_STRING_FORMAT, time_modified);
config_set_string(config.get(), INFO_SECTION, FILE_MODIFIED_TIMESTAMP,
device_iot_config_time_modified);
}
}
bool device_iot_config_is_factory_reset(void) {
return osi_property_get_bool(PROPERTY_FACTORY_RESET, false);
}
void device_iot_config_delete_files(void) {
remove(IOT_CONFIG_FILE_PATH);
remove(IOT_CONFIG_BACKUP_PATH);
}