diff --git a/TEST_MAPPING b/TEST_MAPPING index dc17051c0a3dfc201f133a0ecfc241e0bcb78fe7..85cd1c13a7515938559c8c5f0e4cd95a827a8f2c 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -84,6 +84,10 @@ { "name" : "net_test_stack_a2dp_native", "host" : true + }, + { + "name" : "net_test_btif_config_cache", + "host" : true } ] } diff --git a/system/btif/Android.bp b/system/btif/Android.bp index af5f88120befcca321b69353de20f3fe07fda0e3..e2af2483e88276e00ab2615306db4a34dea7bffd 100644 --- a/system/btif/Android.bp +++ b/system/btif/Android.bp @@ -55,6 +55,7 @@ cc_library_static { "src/btif_ble_scanner.cc", "src/btif_bqr.cc", "src/btif_config.cc", + "src/btif_config_cache.cc", "src/btif_config_transcode.cc", "src/btif_core.cc", "src/btif_debug.cc", @@ -225,3 +226,29 @@ cc_test { misc_undefined: ["bounds"], }, } + +// btif config cache unit tests for target +// ======================================================== +cc_test { + name: "net_test_btif_config_cache", + defaults: ["fluoride_defaults"], + test_suites: ["device-tests"], + host_supported: true, + include_dirs: btifCommonIncludes, + srcs: [ + "src/btif_config_cache.cc", + "test/btif_config_cache_test.cc", + ], + header_libs: ["libbluetooth_headers"], + shared_libs: [ + "liblog", + "libcutils", + ], + static_libs: [ + "libbluetooth-types", + "libosi", + "libgmock", + "libc++fs", + ], + cflags: ["-DBUILDCFG"], +} diff --git a/system/btif/include/btif_config.h b/system/btif/include/btif_config.h index 5c7d433826830385bf8450ce4ae989c8e2f9f94d..d1678cd7e14e3f07fdc9133226ac06a29fefbaee 100644 --- a/system/btif/include/btif_config.h +++ b/system/btif/include/btif_config.h @@ -64,7 +64,7 @@ bool btif_config_remove(const std::string& section, const std::string& key); size_t btif_config_get_bin_length(const std::string& section, const std::string& key); -std::list<section_t>& btif_config_sections(); +const std::list<section_t>& btif_config_sections(); void btif_config_save(void); void btif_config_flush(void); diff --git a/system/btif/include/btif_config_cache.h b/system/btif/include/btif_config_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..aedcd4eb4e7e44c289cd44538c45cc7221724764 --- /dev/null +++ b/system/btif/include/btif_config_cache.h @@ -0,0 +1,60 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include <map> +#include <unordered_set> + +#include "common/lru.h" +#include "osi/include/config.h" +#include "osi/include/log.h" +#include "raw_address.h" + +class BtifConfigCache { + public: + explicit BtifConfigCache(size_t capacity); + ~BtifConfigCache(); + + void Clear(); + void Init(std::unique_ptr<config_t> source); + const std::list<section_t>& GetPersistentSections(); + config_t PersistentSectionCopy(); + bool HasSection(const std::string& section_name); + bool HasUnpairedSection(const std::string& section_name); + bool HasPersistentSection(const std::string& section_name); + bool HasKey(const std::string& section_name, const std::string& key); + bool RemoveKey(const std::string& section_name, const std::string& key); + void RemovePersistentSectionsWithKey(const std::string& key); + + // Setters and getters + void SetString(std::string section_name, std::string key, std::string value); + std::optional<std::string> GetString(const std::string& section_name, + const std::string& key); + void SetInt(std::string section_name, std::string key, int value); + std::optional<int> GetInt(const std::string& section_name, + const std::string& key); + void SetUint64(std::string section_name, std::string key, uint64_t value); + std::optional<uint64_t> GetUint64(const std::string& section_name, + const std::string& key); + void SetBool(std::string section_name, std::string key, bool value); + std::optional<bool> GetBool(const std::string& section_name, + const std::string& key); + + private: + bluetooth::common::LruCache<std::string, section_t> unpaired_devices_cache_; + config_t paired_devices_list_; +}; diff --git a/system/btif/src/btif_config.cc b/system/btif/src/btif_config.cc index b710c2ef3009e46af74c54beb0e22267a9505bd7..d95aa55a01a2ca6811fdc88f04800deca347a6ed 100644 --- a/system/btif/src/btif_config.cc +++ b/system/btif/src/btif_config.cc @@ -40,6 +40,7 @@ #include "btcore/include/module.h" #include "btif_api.h" #include "btif_common.h" +#include "btif_config_cache.h" #include "btif_config_transcode.h" #include "btif_util.h" #include "common/address_obfuscator.h" @@ -56,6 +57,7 @@ #include "raw_address.h" #define BT_CONFIG_SOURCE_TAG_NUM 1010001 +#define TEMPORARY_SECTION_CAPACITY 10000 #define INFO_SECTION "Info" #define FILE_TIMESTAMP "TimeCreated" @@ -89,8 +91,6 @@ static void timer_config_save_cb(void* data); static void btif_config_write(uint16_t event, char* p_param); static bool is_factory_reset(void); static void delete_config_files(void); -static void btif_config_remove_unpaired(config_t* config); -static void btif_config_remove_restricted(config_t* config); static std::unique_ptr<config_t> btif_config_open(const char* filename); // Key attestation @@ -121,7 +121,6 @@ static enum ConfigSource { RESET } btif_config_source = NOT_LOADED; -static int btif_config_devices_loaded = -1; static char btif_config_time_created[TIME_STRING_LENGTH]; static const storage_config_t interface = { @@ -265,13 +264,16 @@ static void init_metric_id_allocator() { } static std::recursive_mutex config_lock; // protects operations on |config|. -static std::unique_ptr<config_t> config; static alarm_t* config_timer; +// limited btif config cache capacity +static BtifConfigCache btif_config_cache(TEMPORARY_SECTION_CAPACITY); + // Module lifecycle functions static future_t* init(void) { std::unique_lock<std::recursive_mutex> lock(config_lock); + std::unique_ptr<config_t> config; if (is_factory_reset()) delete_config_files(); @@ -305,28 +307,29 @@ static future_t* init(void) { file_source = "Empty"; } - if (!file_source.empty()) - storage_config_get_interface()->config_set_string( - config.get(), INFO_SECTION, FILE_SOURCE, file_source); + // move persistent config data from btif_config file to btif config cache + btif_config_cache.Init(std::move(config)); - btif_config_remove_unpaired(config.get()); + if (!file_source.empty()) { + btif_config_cache.SetString(INFO_SECTION, FILE_SOURCE, file_source); + } // Cleanup temporary pairings if we have left guest mode - if (!is_restricted_mode()) btif_config_remove_restricted(config.get()); + if (!is_restricted_mode()) { + btif_config_cache.RemovePersistentSectionsWithKey("Restricted"); + } // Read or set config file creation timestamp - const std::string* time_str; - time_str = storage_config_get_interface()->config_get_string( - *config, INFO_SECTION, FILE_TIMESTAMP, NULL); - if (time_str != NULL) { - strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH); - } else { + auto time_str = btif_config_cache.GetString(INFO_SECTION, FILE_TIMESTAMP); + if (!time_str) { time_t current_time = time(NULL); struct tm* time_created = localtime(¤t_time); strftime(btif_config_time_created, TIME_STRING_LENGTH, TIME_STRING_FORMAT, time_created); - storage_config_get_interface()->config_set_string( - config.get(), INFO_SECTION, FILE_TIMESTAMP, btif_config_time_created); + btif_config_cache.SetString(INFO_SECTION, FILE_TIMESTAMP, + btif_config_time_created); + } else { + strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH); } // Read or set metrics 256 bit hashing salt @@ -351,6 +354,7 @@ static future_t* init(void) { error: alarm_free(config_timer); config.reset(); + btif_config_cache.Clear(); config_timer = NULL; btif_config_source = NOT_LOADED; return future_new_immediate(FUTURE_FAIL); @@ -383,7 +387,7 @@ static future_t* clean_up(void) { std::unique_lock<std::recursive_mutex> lock(config_lock); get_bluetooth_keystore_interface()->clear_map(); MetricIdAllocator::GetInstance().Close(); - config.reset(); + btif_config_cache.Clear(); return future_new_immediate(FUTURE_SUCCESS); } @@ -394,98 +398,74 @@ EXPORT_SYMBOL module_t btif_config_module = {.name = BTIF_CONFIG_MODULE, .clean_up = clean_up}; bool btif_config_has_section(const char* section) { - CHECK(config != NULL); CHECK(section != NULL); std::unique_lock<std::recursive_mutex> lock(config_lock); - return storage_config_get_interface()->config_has_section(*config, section); + return btif_config_cache.HasSection(section); } bool btif_config_exist(const std::string& section, const std::string& key) { - CHECK(config != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - return storage_config_get_interface()->config_has_key(*config, section, key); + return btif_config_cache.HasKey(section, key); } bool btif_config_get_int(const std::string& section, const std::string& key, int* value) { - CHECK(config != NULL); CHECK(value != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - bool ret = - storage_config_get_interface()->config_has_key(*config, section, key); - if (ret) - *value = storage_config_get_interface()->config_get_int(*config, section, - key, *value); - - return ret; + auto ret = btif_config_cache.GetInt(section, key); + if (!ret) { + return false; + } + *value = *ret; + return true; } bool btif_config_set_int(const std::string& section, const std::string& key, int value) { - CHECK(config != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - storage_config_get_interface()->config_set_int(config.get(), section, key, - value); - + btif_config_cache.SetInt(section, key, value); return true; } bool btif_config_get_uint64(const std::string& section, const std::string& key, uint64_t* value) { - CHECK(config != NULL); CHECK(value != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - bool ret = - storage_config_get_interface()->config_has_key(*config, section, key); - if (ret) - *value = storage_config_get_interface()->config_get_uint64(*config, section, - key, *value); - - return ret; + auto ret = btif_config_cache.GetUint64(section, key); + if (!ret) { + return false; + } + *value = *ret; + return true; } bool btif_config_set_uint64(const std::string& section, const std::string& key, uint64_t value) { - CHECK(config != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - storage_config_get_interface()->config_set_uint64(config.get(), section, key, - value); - + btif_config_cache.SetUint64(section, key, value); return true; } bool btif_config_get_str(const std::string& section, const std::string& key, char* value, int* size_bytes) { - CHECK(config != NULL); CHECK(value != NULL); CHECK(size_bytes != NULL); { std::unique_lock<std::recursive_mutex> lock(config_lock); - const std::string* stored_value = - storage_config_get_interface()->config_get_string(*config, section, key, - NULL); + auto stored_value = btif_config_cache.GetString(section, key); if (!stored_value) return false; strlcpy(value, stored_value->c_str(), *size_bytes); } - *size_bytes = strlen(value) + 1; return true; } bool btif_config_set_str(const std::string& section, const std::string& key, const std::string& value) { - CHECK(config != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - storage_config_get_interface()->config_set_string(config.get(), section, key, - value); + btif_config_cache.SetString(section, key, value); return true; } @@ -497,14 +477,12 @@ static bool btif_in_encrypt_key_name_list(std::string key) { bool btif_config_get_bin(const std::string& section, const std::string& key, uint8_t* value, size_t* length) { - CHECK(config != NULL); CHECK(value != NULL); CHECK(length != NULL); std::unique_lock<std::recursive_mutex> lock(config_lock); const std::string* value_str; - const std::string* value_str_from_config = - config_get_string(*config, section, key, NULL); + auto value_str_from_config = btif_config_cache.GetString(section, key); if (!value_str_from_config) { VLOG(1) << __func__ << ": cannot find string for section " << section @@ -521,7 +499,7 @@ bool btif_config_get_bin(const std::string& section, const std::string& key, string = get_bluetooth_keystore_interface()->get_key(section + "-" + key); value_str = &string; } else { - value_str = value_str_from_config; + value_str = &value_str_from_config.value(); } size_t value_len = value_str->length(); @@ -546,11 +524,11 @@ bool btif_config_get_bin(const std::string& section, const std::string& key, !is_key_encrypted) { get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key( section + "-" + key, *value_str_from_config); - config_set_string(config.get(), section, key, ENCRYPTED_STR); + btif_config_cache.SetString(section, key, ENCRYPTED_STR); } } else { if (in_encrypt_key_name_list && is_key_encrypted) { - config_set_string(config.get(), section, key, value_str->c_str()); + btif_config_cache.SetString(section, key, *value_str); } } @@ -559,14 +537,9 @@ bool btif_config_get_bin(const std::string& section, const std::string& key, size_t btif_config_get_bin_length(const std::string& section, const std::string& key) { - CHECK(config != NULL); - std::unique_lock<std::recursive_mutex> lock(config_lock); - const std::string* value_str = - storage_config_get_interface()->config_get_string(*config, section, key, - NULL); + auto value_str = btif_config_cache.GetString(section, key); if (!value_str) return 0; - size_t value_len = value_str->length(); return ((value_len % 2) != 0) ? 0 : (value_len / 2); } @@ -574,9 +547,6 @@ size_t btif_config_get_bin_length(const std::string& section, bool btif_config_set_bin(const std::string& section, const std::string& key, const uint8_t* value, size_t length) { const char* lookup = "0123456789abcdef"; - - CHECK(config != NULL); - if (length > 0) CHECK(value != NULL); size_t max_value = ((size_t)-1); @@ -604,37 +574,33 @@ bool btif_config_set_bin(const std::string& section, const std::string& key, { std::unique_lock<std::recursive_mutex> lock(config_lock); - storage_config_get_interface()->config_set_string(config.get(), section, - key, value_str); + btif_config_cache.SetString(section, key, value_str); } osi_free(str); return true; } -std::list<section_t>& btif_config_sections() { return config->sections; } +const std::list<section_t>& btif_config_sections() { + return btif_config_cache.GetPersistentSections(); +} bool btif_config_remove(const std::string& section, const std::string& key) { - CHECK(config != NULL); - if (is_niap_mode() && btif_in_encrypt_key_name_list(key)) { get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key( section + "-" + key, ""); } std::unique_lock<std::recursive_mutex> lock(config_lock); - return storage_config_get_interface()->config_remove_key(config.get(), - section, key); + return btif_config_cache.RemoveKey(section, key); } void btif_config_save(void) { - CHECK(config != NULL); CHECK(config_timer != NULL); alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS, timer_config_save_cb, NULL); } void btif_config_flush(void) { - CHECK(config != NULL); CHECK(config_timer != NULL); alarm_cancel(config_timer); @@ -642,17 +608,15 @@ void btif_config_flush(void) { } bool btif_config_clear(void) { - CHECK(config != NULL); CHECK(config_timer != NULL); alarm_cancel(config_timer); std::unique_lock<std::recursive_mutex> lock(config_lock); - config = storage_config_get_interface()->config_new_empty(); - - bool ret = - storage_config_get_interface()->config_save(*config, CONFIG_FILE_PATH); + btif_config_cache.Clear(); + bool ret = storage_config_get_interface()->config_save( + btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH); btif_config_source = RESET; return ret; @@ -667,52 +631,18 @@ static void timer_config_save_cb(UNUSED_ATTR void* data) { static void btif_config_write(UNUSED_ATTR uint16_t event, UNUSED_ATTR char* p_param) { - CHECK(config != NULL); CHECK(config_timer != NULL); std::unique_lock<std::recursive_mutex> lock(config_lock); rename(CONFIG_FILE_PATH, CONFIG_BACKUP_PATH); - std::unique_ptr<config_t> config_paired = - storage_config_get_interface()->config_new_clone(*config); - btif_config_remove_unpaired(config_paired.get()); - storage_config_get_interface()->config_save(*config_paired, CONFIG_FILE_PATH); + storage_config_get_interface()->config_save( + btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH); if (btif_is_niap_mode()) { get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key( CONFIG_FILE_PREFIX, CONFIG_FILE_HASH); } } -static void btif_config_remove_unpaired(config_t* conf) { - CHECK(conf != NULL); - int paired_devices = 0; - - // The paired config used to carry information about - // discovered devices during regular inquiry scans. - // We remove these now and cache them in memory instead. - for (auto it = conf->sections.begin(); it != conf->sections.end();) { - std::string& section = it->name; - if (RawAddress::IsValidAddress(section)) { - // TODO: config_has_key loop thorugh all data, maybe just make it so we - // loop just once ? - if (!config_has_key(*conf, section, "LinkKey") && - !config_has_key(*conf, section, "LE_KEY_PENC") && - !config_has_key(*conf, section, "LE_KEY_PID") && - !config_has_key(*conf, section, "LE_KEY_PCSRK") && - !config_has_key(*conf, section, "LE_KEY_LENC") && - !config_has_key(*conf, section, "LE_KEY_LCSRK")) { - it = conf->sections.erase(it); - continue; - } - paired_devices++; - } - it++; - } - - // should only happen once, at initial load time - if (btif_config_devices_loaded == -1) - btif_config_devices_loaded = paired_devices; -} - void btif_debug_config_dump(int fd) { dprintf(fd, "\nBluetooth Config:\n"); @@ -738,30 +668,15 @@ void btif_debug_config_dump(int fd) { break; } - std::string original = "Original"; - dprintf(fd, " Devices loaded: %d\n", btif_config_devices_loaded); - dprintf(fd, " File created/tagged: %s\n", btif_config_time_created); - dprintf(fd, " File source: %s\n", - storage_config_get_interface() - ->config_get_string(*config, INFO_SECTION, FILE_SOURCE, &original) - ->c_str()); -} - -static void btif_config_remove_restricted(config_t* config) { - CHECK(config != NULL); - - for (auto it = config->sections.begin(); it != config->sections.end();) { - const std::string& section = it->name; - if (RawAddress::IsValidAddress(section) && - storage_config_get_interface()->config_has_key(*config, section, - "Restricted")) { - BTIF_TRACE_DEBUG("%s: Removing restricted device %s", __func__, - section.c_str()); - it = config->sections.erase(it); - continue; - } - it++; + auto file_source = btif_config_cache.GetString(INFO_SECTION, FILE_SOURCE); + if (!file_source) { + file_source.emplace("Original"); } + + dprintf(fd, " Devices loaded: %zu\n", + btif_config_cache.GetPersistentSections().size()); + dprintf(fd, " File created/tagged: %s\n", btif_config_time_created); + dprintf(fd, " File source: %s\n", file_source->c_str()); } static bool is_factory_reset(void) { diff --git a/system/btif/src/btif_config_cache.cc b/system/btif/src/btif_config_cache.cc new file mode 100644 index 0000000000000000000000000000000000000000..c5562aa8600f94d29d98d92aef6ca8e6d947a137 --- /dev/null +++ b/system/btif/src/btif_config_cache.cc @@ -0,0 +1,301 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 <limits> + +#include "btif_config_cache.h" + +namespace { + +const std::unordered_set<std::string> kLinkKeyTypes = { + "LinkKey", "LE_KEY_PENC", "LE_KEY_PID", + "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"}; + +const std::unordered_set<std::string> kLocalSectionNames = {"Info", "Metrics", + "Adapter"}; + +bool is_link_key(const std::string& key) { + return kLinkKeyTypes.find(key) != kLinkKeyTypes.end(); +} + +bool has_link_key_in_section(const section_t& section) { + for (const auto& entry : section.entries) { + if (is_link_key(entry.key)) { + return true; + } + } + return false; +} + +bool is_local_section_info(const std::string& section) { + return kLocalSectionNames.find(section) != kLocalSectionNames.end(); +} + +// trim new line in place, return true if newline was found +bool trim_new_line(std::string& value) { + size_t newline_position = value.find_first_of('\n'); + if (newline_position != std::string::npos) { + value.erase(newline_position); + return true; + } + return false; +} + +} // namespace + +BtifConfigCache::BtifConfigCache(size_t capacity) + : unpaired_devices_cache_(capacity, "bt_config_cache") { + LOG(INFO) << __func__ << ", capacity: " << capacity; +} + +BtifConfigCache::~BtifConfigCache() { Clear(); } + +void BtifConfigCache::Clear() { + unpaired_devices_cache_.Clear(); + paired_devices_list_.sections.clear(); +} + +void BtifConfigCache::Init(std::unique_ptr<config_t> source) { + // get the config persistent data from btif_config file + paired_devices_list_ = std::move(*source); + source.reset(); +} + +bool BtifConfigCache::HasPersistentSection(const std::string& section_name) { + return paired_devices_list_.Find(section_name) != + paired_devices_list_.sections.end(); +} + +bool BtifConfigCache::HasUnpairedSection(const std::string& section_name) { + return unpaired_devices_cache_.HasKey(section_name); +} + +bool BtifConfigCache::HasSection(const std::string& section_name) { + return HasUnpairedSection(section_name) || HasPersistentSection(section_name); +} + +bool BtifConfigCache::HasKey(const std::string& section_name, + const std::string& key) { + auto section_iter = paired_devices_list_.Find(section_name); + if (section_iter != paired_devices_list_.sections.end()) { + return section_iter->Has(key); + } + section_t* section = unpaired_devices_cache_.Find(section_name); + if (section == nullptr) { + return false; + } + return section->Has(key); +} + +// remove sections with the restricted key +void BtifConfigCache::RemovePersistentSectionsWithKey(const std::string& key) { + for (auto it = paired_devices_list_.sections.begin(); + it != paired_devices_list_.sections.end();) { + if (it->Has(key)) { + it = paired_devices_list_.sections.erase(it); + continue; + } + it++; + } +} + +/* remove a key from section, section itself is removed when empty */ +bool BtifConfigCache::RemoveKey(const std::string& section_name, + const std::string& key) { + section_t* section = unpaired_devices_cache_.Find(section_name); + if (section != nullptr) { + auto entry_iter = section->Find(key); + if (entry_iter == section->entries.end()) { + return false; + } + section->entries.erase(entry_iter); + if (section->entries.empty()) { + unpaired_devices_cache_.Remove(section_name); + } + return true; + } else { + auto section_iter = paired_devices_list_.Find(section_name); + if (section_iter == paired_devices_list_.sections.end()) { + return false; + } + auto entry_iter = section_iter->Find(key); + if (entry_iter == section_iter->entries.end()) { + return false; + } + section_iter->entries.erase(entry_iter); + if (section_iter->entries.empty()) { + paired_devices_list_.sections.erase(section_iter); + } else if (!has_link_key_in_section(*section_iter)) { + // if no link key in section after removal, move it to unpaired section + auto moved_section = std::move(*section_iter); + paired_devices_list_.sections.erase(section_iter); + unpaired_devices_cache_.Put(section_name, std::move(moved_section)); + } + return true; + } +} + +/* clone persistent sections (Local Adapter sections, remote paired devices + * section,..) */ +config_t BtifConfigCache::PersistentSectionCopy() { + return paired_devices_list_; +} + +const std::list<section_t>& BtifConfigCache::GetPersistentSections() { + return paired_devices_list_.sections; +} + +void BtifConfigCache::SetString(std::string section_name, std::string key, + std::string value) { + if (trim_new_line(section_name) || trim_new_line(key) || + trim_new_line(value)) { + android_errorWriteLog(0x534e4554, "70808273"); + } + if (section_name.empty()) { + LOG(FATAL) << "Empty section not allowed"; + return; + } + if (key.empty()) { + LOG(FATAL) << "Empty key not allowed"; + return; + } + if (!paired_devices_list_.Has(section_name)) { + // section is not in paired_device_list, handle it in unpaired devices cache + section_t section = {}; + bool in_unpaired_cache = true; + if (!unpaired_devices_cache_.Get(section_name, §ion)) { + // it's a new unpaired section, add it to unpaired devices cache + section.name = section_name; + in_unpaired_cache = false; + } + // set key to value and replace existing key if already exist + section.Set(key, value); + + if (is_local_section_info(section_name) || + (is_link_key(key) && RawAddress::IsValidAddress(section_name))) { + // remove this section that has the LinkKey from unpaired devices cache. + if (in_unpaired_cache) { + unpaired_devices_cache_.Remove(section_name); + } + // when a unpaired section got the LinkKey, move this section to the + // paired devices list + paired_devices_list_.sections.emplace_back(std::move(section)); + } else { + // update to the unpaired devices cache + unpaired_devices_cache_.Put(section_name, section); + } + } else { + // already have section in paired device list, add key-value entry. + auto section_found = paired_devices_list_.Find(section_name); + if (section_found == paired_devices_list_.sections.end()) { + LOG(WARNING) << __func__ << " , section_found not found!"; + return; + } + section_found->Set(key, value); + } +} + +std::optional<std::string> BtifConfigCache::GetString( + const std::string& section_name, const std::string& key) { + // Check paired sections first + auto section_iter = paired_devices_list_.Find(section_name); + if (section_iter != paired_devices_list_.sections.end()) { + auto entry_iter = section_iter->Find(key); + if (entry_iter == section_iter->entries.end()) { + return std::nullopt; + } + return entry_iter->value; + } + // Check unpaired sections later + section_t section = {}; + if (!unpaired_devices_cache_.Get(section_name, §ion)) { + return std::nullopt; + } + auto entry_iter = section.Find(key); + if (entry_iter == section.entries.end()) { + return std::nullopt; + } + return entry_iter->value; +} + +void BtifConfigCache::SetInt(std::string section_name, std::string key, + int value) { + SetString(std::move(section_name), std::move(key), std::to_string(value)); +} + +std::optional<int> BtifConfigCache::GetInt(const std::string& section_name, + const std::string& key) { + auto value = GetString(section_name, key); + if (!value) { + return std::nullopt; + } + char* endptr; + long ret_long = strtol(value->c_str(), &endptr, 0); + if (*endptr != '\0') { + LOG(WARNING) << "Failed to parse value to long for section " << section_name + << ", key " << key; + return std::nullopt; + } + if (ret_long >= std::numeric_limits<int>::max()) { + LOG(WARNING) << "Integer overflow when parsing value to int for section " + << section_name << ", key " << key; + return std::nullopt; + } + return static_cast<int>(ret_long); +} + +void BtifConfigCache::SetUint64(std::string section_name, std::string key, + uint64_t value) { + SetString(std::move(section_name), std::move(key), std::to_string(value)); +} + +std::optional<uint64_t> BtifConfigCache::GetUint64( + const std::string& section_name, const std::string& key) { + auto value = GetString(section_name, key); + if (!value) { + return std::nullopt; + } + char* endptr; + uint64_t ret = strtoull(value->c_str(), &endptr, 0); + if (*endptr != '\0') { + LOG(WARNING) << "Failed to parse value to uint64 for section " + << section_name << ", key " << key; + return std::nullopt; + } + return ret; +} + +void BtifConfigCache::SetBool(std::string section_name, std::string key, + bool value) { + SetString(std::move(section_name), std::move(key), value ? "true" : "false"); +} + +std::optional<bool> BtifConfigCache::GetBool(const std::string& section_name, + const std::string& key) { + auto value = GetString(section_name, key); + if (!value) { + return std::nullopt; + } + if (*value == "true") { + return true; + } + if (*value == "false") { + return false; + } + LOG(WARNING) << "Failed to parse value to boolean for section " + << section_name << ", key " << key; + return std::nullopt; +} diff --git a/system/btif/test/btif_config_cache_test.cc b/system/btif/test/btif_config_cache_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..aaccf8c0cbe7953e1e5fe61d418819ab916f0a83 --- /dev/null +++ b/system/btif/test/btif_config_cache_test.cc @@ -0,0 +1,527 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 "btif/include/btif_config_cache.h" + +#include <filesystem> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace { + +const int kCapacity = 3; +const int kTestRepeatCount = 30; +const std::string kBtAddr1 = "11:22:33:44:55:66"; +const std::string kBtAddr2 = "AA:BB:CC:DD:EE:FF"; +const std::string kBtAddr3 = "AB:CD:EF:12:34:56"; +const std::string kBtAddr4 = "11:AA:22:BB:33:CC"; +const std::string kBtAddr5 = "11:AA:22:BB:33:CD"; +const std::string kBtLocalAddr = "12:34:56:78:90:AB"; +const std::string kBtInfo = "Info"; +const std::string kBtMetrics = "Metrics"; +const std::string kBtAdapter = "Adapter"; +const std::string kBtAddrInvalid1 = "AB:CD:EF:12:34"; +const std::string kBtAddrInvalid2 = "AB:CD:EF:12:34:56:78"; +const std::string kBtAddrInvalid3 = "ABCDEF123456"; +const std::string kBtAddrInvalid4 = "AB-CD-EF-12-34-56"; +const std::string kBtSectionInvalid1 = "Invalid Section"; +const std::filesystem::path kTestConfigFile = + std::filesystem::temp_directory_path() / "config_cache_test.conf"; +const char* TEST_CONFIG_FILE = kTestConfigFile.c_str(); + +} // namespace + +namespace testing { + +/* Test to basic btif_config_cache set up + * 1. when received Local device sections information, the sections can be put + * into btif config cache + * 2. the device sections and key-value will be set to Btif config cache when + * receiving different device sections + * 3. limit the capacity of unpacire devices cache to 3, test the oldest device + * section will be ruled out when receiveing 4 different device sections. + */ +TEST(BtifConfigCacheTest, test_setup_btif_config_cache) { + BtifConfigCache test_btif_config_cache(kCapacity); + // Info section + test_btif_config_cache.SetString(kBtInfo, "FileSource", ""); + test_btif_config_cache.SetString(kBtInfo, "TimeCreated", + "2020-06-05 12:12:12"); + // Metrics section + test_btif_config_cache.SetString(kBtMetrics, "Salt256Bit", + "92a331174d20f2bb"); + // Adapter Section + test_btif_config_cache.SetString(kBtAdapter, "Address", kBtLocalAddr); + EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAdapter)); + + // bt_device_1 + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name")); + + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 1); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int")); + + // bt_device_2 + test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "Name")); + + // bt_device_3 + test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "Name")); + + // bt_device_4 + test_btif_config_cache.SetString(kBtAddr4, "Name", "Headset_4"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "Name")); + + // out out the capacty of unpair devices cache, the bt_device_1 be ruled out + EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2)); + EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr3)); + EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr4)); +} + +/* Test to set up btif_config_cache with invalid bt address or section name + * when received Invalid bt address or section, it's not allowed to put invalid + * section to paired devices list section + */ +TEST(BtifConfigCacheTest, test_set_up_config_cache_with_invalid_section) { + BtifConfigCache test_btif_config_cache(kCapacity); + + // kBtAddrInvalid1 + test_btif_config_cache.SetString(kBtAddrInvalid1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "Name")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1)); + // get the LinkKey + test_btif_config_cache.SetString(kBtAddrInvalid1, "LinkKey", + "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid1)); + + // kBtAddrInvalid2 + test_btif_config_cache.SetString(kBtAddrInvalid2, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "Name")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2)); + // get the LinkKey + test_btif_config_cache.SetString(kBtAddrInvalid2, "LinkKey", + "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid2)); + + // kBtAddrInvalid3 + test_btif_config_cache.SetString(kBtAddrInvalid3, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "Name")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3)); + // get the LinkKey + test_btif_config_cache.SetString(kBtAddrInvalid3, "LinkKey", + "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid3)); + + // kBtAddrInvalid4 + test_btif_config_cache.SetString(kBtAddrInvalid4, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "Name")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4)); + // get the LinkKey + test_btif_config_cache.SetString(kBtAddrInvalid4, "LinkKey", + "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid4)); + + // kBtSectionInvalid1 + test_btif_config_cache.SetString(kBtSectionInvalid1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "Name")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1)); + // get the LinkKey + test_btif_config_cache.SetString(kBtSectionInvalid1, "LinkKey", + "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtSectionInvalid1)); +} + +/* Stress test to set and get key values + * 1. stress test to set different type key value to unpaired device cache and + * get the different type key values in the unpaired cache section to check if + * we get the key-values the same with we set in unpaired device cache. + * 2. stress test to set different type key value to paired device section and + * get the different type key values in the paired cache section to check if we + * get the key-values the same with we set in paired device cache. + */ +TEST(BtifConfigCacheTest, test_get_set_key_value_test) { + BtifConfigCache test_btif_config_cache(kCapacity); + // test in unpaired cache + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name")); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_1"))); + + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int")); + EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"), + Optional(Eq(65536))); + + test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64")); + EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"), + Optional(Eq(uint64_t(4294967296)))); + + test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool")); + EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"), + Optional(IsTrue())); + + // empty value + test_btif_config_cache.SetString(kBtAddr1, "Name", ""); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name")); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq(""))); + + // get the LinkKey + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + + // test in unpaired cache + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name")); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_1"))); + + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int")); + EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"), + Optional(Eq(65536))); + + test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64")); + EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"), + Optional(Eq(uint64_t(4294967296)))); + + test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool")); + EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"), + Optional(IsTrue())); + + // empty section is disallowed + EXPECT_DEATH({ test_btif_config_cache.SetString("", "name", "Headset_1"); }, + "Empty section not allowed"); + // empty key is disallowed + EXPECT_DEATH({ test_btif_config_cache.SetString(kBtAddr1, "", "Headset_1"); }, + "Empty key not allowed"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "")); + // empty value is allowed + test_btif_config_cache.SetString(kBtAddr1, "Name", ""); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name")); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq(""))); +} + +/* Test to set values in the same key + * Receiving the same key with different values in a section, the new incoming + * value will be updated but the key will not be added repeatedly. test this + * feature in both unpaired devic cache and paired device list cache + */ +TEST(BtifConfigCacheTest, test_set_values_in_the_same_key) { + BtifConfigCache test_btif_config_cache(kCapacity); + // add new a key "Name" + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_1"))); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + + // add the same key "Name" with different value + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A"); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_1A"))); + + // add the same key "Name" with different value + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A"); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_2A"))); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + + // add new a key "Property_Int" + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536); + EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"), + Optional(Eq(65536))); + + // add the same key "Property_Int" with different value + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 256); + EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"), + Optional(Eq(256))); + + test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296); + EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"), + Optional(Eq(uint64_t(4294967296)))); + + // get the LinkKey and set values in the same key in paired device list + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + + // add the same key "Name" with the different value + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A"); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_1A"))); + + // add the same key "Name" with the value different + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A"); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"), + Optional(StrEq("Headset_2A"))); + + test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 64); + EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"), + Optional(Eq(64))); + + test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 65537); + EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"), + Optional(Eq(uint64_t(65537)))); + + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); +} + +/* Stress test to pair with device then unpair device + * 1. paired with device by adding a "LinKey" to device and check the device be + * moved into paired devices list + * 2. unpaired with the device by removing the "LinkKey" and check the device be + * moved back to unpaired devices cache + * 3. loop for 30 times + */ +TEST(BtifConfigCacheTest, test_pair_unpair_device_stress_test) { + BtifConfigCache test_btif_config_cache(kCapacity); + + // pair with Headset_1 11:22:33:44:55:66 + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + + for (int i = 0; i < kTestRepeatCount; ++i) { + // get the LinkKey, the device will be moved from the unpaired cache to + // paired cache + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + + // remove the LinkKey, the device will be moved from the paired cache to + // unpaired cache + test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + } +} + +/* Stress test to pair with multi-devices and unpair with multi-devices + * 1. Pired with 4 devices with Link-Key type key in order, to check these 4 + * devices are in the paired devices list cache + * 2. unpair with these 4 devices by removed Link-Key type key in order, to + * check the fisrt device was ruled-out from unpaired devices cache due to + * capacity limitation, and other 3 devices are be moved to unpaired device + * cache. + */ +TEST(BtifConfigCacheTest, test_multi_pair_unpair_with_devices) { + BtifConfigCache test_btif_config_cache(kCapacity); + // pair with 4 bt address devices by add different type linkkey. + test_btif_config_cache.SetString(kBtAddr1, "name", "kBtAddr1"); + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + + test_btif_config_cache.SetString(kBtAddr2, "name", "kBtAddr2"); + test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC")); + + test_btif_config_cache.SetString(kBtAddr3, "name", "kBtAddr3"); + test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID")); + + test_btif_config_cache.SetString(kBtAddr4, "LE_KEY_PCSRK", + "aaaabbbbccccdddd"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK")); + + test_btif_config_cache.SetString(kBtAddr5, "name", "kBtAddr5"); + test_btif_config_cache.SetString(kBtAddr5, "LE_KEY_LENC", "jilkjlkjlkn"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC")); + + // checking these 4 devices are in paired list cache and the content are + // correct. + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"), + Optional(StrEq("1122334455667788"))); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"), + Optional(StrEq("aabbccddeeff9900"))); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"), + Optional(StrEq("a1b2c3d4e5feeeee"))); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr4)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr4, "LE_KEY_PCSRK"), + Optional(StrEq("aaaabbbbccccdddd"))); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr5)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "LE_KEY_LENC"), + Optional(StrEq("jilkjlkjlkn"))); + + // unpair with these 4 bt address devices by removed the linkkey. + // unpair kBtAddr1 11:22:33:44:55:66 + test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + // no empty section is moved to unpaired + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "name"), + Optional(StrEq("kBtAddr1"))); + + // unpair with kBtAddr2 aa:bb:cc:dd:ee:ff + test_btif_config_cache.RemoveKey(kBtAddr2, "LE_KEY_PENC"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC")); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr2)); + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr2)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "name"), + Optional(StrEq("kBtAddr2"))); + + // unpair with kBtAddr3 AB:CD:EF:12:34:56 + test_btif_config_cache.RemoveKey(kBtAddr3, "LE_KEY_PID"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID")); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr3)); + // no empty section is moved to unpaired + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr3)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "name"), + Optional(StrEq("kBtAddr3"))); + + // unpair with kBtAddr4 11:AA:22:BB:33:CC + test_btif_config_cache.RemoveKey(kBtAddr4, "LE_KEY_PCSRK"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK")); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr4)); + // empty section is removed + EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr4)); + + // unpair with kBtAddr5 11:AA:22:BB:33:CD + test_btif_config_cache.RemoveKey(kBtAddr5, "LE_KEY_LENC"); + EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC")); + EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr5)); + // no empty section is moved to unpaired + EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr5)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "name"), + Optional(StrEq("kBtAddr5"))); + + // checking the oldest unpaired device kBtAddr1 was ruled out from cache due + // to capacity limitation (3) in unpaired cache. + EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1)); +} + +/* Test to remove sections with the specific key + * paired with sections with the specific "Restricted" key and then removed the + * "Restricted" key, check if the sections with the specific "Restricted" key + * are removed. + */ +TEST(BtifConfigCacheTest, test_remove_sections_with_key) { + BtifConfigCache test_btif_config_cache(kCapacity); + // pair with Headset_1 (kBtAddr1), Headset_2 (kBtAddr1), Heasdet_3 (kBtAddr3) + // , and Headset_1 (kBtAddr1), Headset_3 (kBtAddr3) have sepcific "Restricted" + // key + test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1"); + test_btif_config_cache.SetString(kBtAddr1, "Restricted", "1"); + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2"); + test_btif_config_cache.SetString(kBtAddr2, "LinkKey", "aabbccddeeff9900"); + test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3"); + test_btif_config_cache.SetString(kBtAddr3, "LinkKey", "a1b2c3d4e5feeeee"); + test_btif_config_cache.SetString(kBtAddr3, "Restricted", "1"); + + // remove sections with "Restricted" key + test_btif_config_cache.RemovePersistentSectionsWithKey("Restricted"); + + // checking the kBtAddr1 and kBtAddr3 can not be found in config cache, only + // keep kBtAddr2 in config cache. + EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1)); + EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2)); + EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr3)); +} + +/* Test PersistentSectionCopy and Init */ +TEST(BtifConfigCacheTest, test_PersistentSectionCopy_Init) { + BtifConfigCache test_btif_config_cache(kCapacity); + config_t config_paired = {}; + // pair with 3 bt devices, kBtAddr1, kBtAddr2, kBtAddr3 + test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey")); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"), + Optional(StrEq("1122334455667788"))); + + test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC")); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"), + Optional(StrEq("aabbccddeeff9900"))); + + test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee"); + EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID")); + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3)); + EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"), + Optional(StrEq("a1b2c3d4e5feeeee"))); + + // check GetPersistentSections + int num_of_paired_devices = 0; + for (const section_t& sec : test_btif_config_cache.GetPersistentSections()) { + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name)); + num_of_paired_devices++; + } + EXPECT_EQ(num_of_paired_devices, 3); + + // copy persistent sections + int num_of_copy_paired_devices = 0; + config_paired = test_btif_config_cache.PersistentSectionCopy(); + for (const section_t& sec : config_paired.sections) { + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name)); + num_of_copy_paired_devices++; + } + EXPECT_EQ(num_of_copy_paired_devices, 3); + + // write persistent sections to temp test config file + EXPECT_TRUE(config_save(config_paired, TEST_CONFIG_FILE)); + // get persistent sections from temp test config file + int num_of_save_paired_devices = 0; + std::unique_ptr<config_t> config_source = config_new(TEST_CONFIG_FILE); + for (const section_t& sec : config_paired.sections) { + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name)); + num_of_save_paired_devices++; + } + EXPECT_EQ(num_of_save_paired_devices, 3); + + // Clear all btif config cache sections + test_btif_config_cache.Clear(); + + // move the persistent sections to btif config paired list + int num_of_init_paired_devices = 0; + test_btif_config_cache.Init(std::move(config_source)); + for (const section_t& sec : config_paired.sections) { + EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name)); + num_of_init_paired_devices++; + } + EXPECT_EQ(num_of_init_paired_devices, 3); + + EXPECT_TRUE(std::filesystem::remove(kTestConfigFile)); +} + +} // namespace testing diff --git a/system/test/run_unit_tests.sh b/system/test/run_unit_tests.sh index 8041afcf91aced40f2c5321d7ae959a94e3f3d13..754d9c57f3846cc38e2e6e14dc2b954c5a3c17a7 100755 --- a/system/test/run_unit_tests.sh +++ b/system/test/run_unit_tests.sh @@ -12,6 +12,7 @@ known_tests=( net_test_bta net_test_btif net_test_btif_profile_queue + net_test_btif_config_cache net_test_device net_test_hci net_test_stack