Loading system/gd/os/linux_generic/files.cc +10 −11 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <sys/stat.h> #include <unistd.h> #include <cctype> #include <cerrno> #include <cstring> #include <fstream> Loading Loading @@ -108,27 +107,27 @@ bool WriteToFile(const std::string& path, const std::string& data) { return false; } int dir_fd = open(directory_path.c_str(), O_RDONLY); int dir_fd = open(directory_path.c_str(), O_RDONLY | O_DIRECTORY); if (dir_fd < 0) { LOG_ERROR("unable to open dir '%s', error: %s", directory_path.c_str(), strerror(errno)); return false; } FILE* fp = fopen(temp_path.c_str(), "wt"); FILE* fp = std::fopen(temp_path.c_str(), "wt"); if (!fp) { LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } if (fprintf(fp, "%s", data.c_str()) < 0) { if (std::fprintf(fp, "%s", data.c_str()) < 0) { LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // Flush the stream buffer to the temp file. if (fflush(fp) < 0) { if (std::fflush(fp) != 0) { LOG_ERROR("unable to write flush buffer to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading @@ -136,12 +135,12 @@ bool WriteToFile(const std::string& path, const std::string& data) { // Sync written temp file out to disk. fsync() is blocking until data makes it // to disk. if (fsync(fileno(fp)) < 0) { if (fsync(fileno(fp)) != 0) { LOG_WARN("unable to fsync file '%s', error: %s", temp_path.c_str(), strerror(errno)); // Allow fsync to fail and continue } if (fclose(fp) != 0) { if (std::fclose(fp) != 0) { LOG_ERROR("unable to close file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading @@ -149,25 +148,25 @@ bool WriteToFile(const std::string& path, const std::string& data) { fp = nullptr; // Change the file's permissions to Read/Write by User and Group if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) { if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) { LOG_ERROR("unable to change file permissions '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // Rename written temp file to the actual config file. if (rename(temp_path.c_str(), path.c_str()) == -1) { if (std::rename(temp_path.c_str(), path.c_str()) == -1) { LOG_ERROR("unable to commit file from '%s' to '%s', error: %s", temp_path.c_str(), path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // This should ensure the directory is updated as well. if (fsync(dir_fd) < 0) { if (fsync(dir_fd) != 0) { LOG_WARN("unable to fsync dir '%s', error: %s", directory_path.c_str(), strerror(errno)); } if (close(dir_fd) < 0) { if (close(dir_fd) != 0) { LOG_ERROR("unable to close dir '%s', error: %s", directory_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading system/gd/os/linux_generic/files_test.cc +3 −4 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <cstdio> #include <filesystem> namespace testing { Loading @@ -33,7 +32,7 @@ TEST(FilesTest, write_read_loopback_test) { std::string text = "Hello world!\n"; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, overwrite_test) { Loading @@ -45,7 +44,7 @@ TEST(FilesTest, overwrite_test) { text = "Foo bar!\n"; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, write_read_empty_string_test) { Loading @@ -54,7 +53,7 @@ TEST(FilesTest, write_read_empty_string_test) { std::string text; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, read_non_existing_file_test) { Loading system/gd/storage/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ filegroup { "config_cache.cc", "config_cache_helper.cc", "legacy.cc", "legacy_config_file.cc", "legacy_osi_config.cc", "mutation_entry.cc", ], Loading @@ -14,6 +15,7 @@ filegroup { srcs: [ "config_cache_test.cc", "config_cache_helper_test.cc", "legacy_config_file_test.cc", "legacy_test.cc", "legacy_osi_config.cc", "mutation_test.cc", Loading system/gd/storage/config_cache.cc +93 −13 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #include "storage/config_cache.h" #include <ios> #include <sstream> #include <utility> #include "storage/mutation.h" Loading @@ -40,29 +42,64 @@ namespace storage { const std::unordered_set<std::string_view> kLinkKeyPropertyNames = { "LinkKey", "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"}; const std::string ConfigCache::kDefaultSectionName = "Global"; ConfigCache::ConfigCache(size_t temp_device_capacity) : information_sections_(), persistent_devices_(), temporary_devices_(temp_device_capacity) {} void ConfigCache::SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback) { std::lock_guard<std::recursive_mutex> lock(mutex_); persistent_config_changed_callback_ = std::move(persistent_config_changed_callback); } ConfigCache::ConfigCache(ConfigCache&& other) noexcept : information_sections_(std::move(other.information_sections_)), : persistent_config_changed_callback_(std::move(other.persistent_config_changed_callback_)), information_sections_(std::move(other.information_sections_)), persistent_devices_(std::move(other.persistent_devices_)), temporary_devices_(std::move(other.temporary_devices_)) {} temporary_devices_(std::move(other.temporary_devices_)) { // std::function will be in a valid but unspecified state after std::move(), hence resetting it other.persistent_config_changed_callback_ = {}; } ConfigCache& ConfigCache::operator=(ConfigCache&& other) noexcept { if (&other == this) { return *this; } std::lock_guard<std::recursive_mutex> my_lock(mutex_); std::lock_guard<std::recursive_mutex> others_lock(other.mutex_); persistent_config_changed_callback_.swap(other.persistent_config_changed_callback_); other.persistent_config_changed_callback_ = {}; information_sections_ = std::move(other.information_sections_); persistent_devices_ = std::move(other.persistent_devices_); temporary_devices_ = std::move(other.temporary_devices_); return *this; } bool ConfigCache::operator==(const ConfigCache& rhs) const { std::lock_guard<std::recursive_mutex> my_lock(mutex_); std::lock_guard<std::recursive_mutex> others_lock(rhs.mutex_); return information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ && temporary_devices_ == rhs.temporary_devices_; } bool ConfigCache::operator!=(const ConfigCache& rhs) const { return !(*this == rhs); } void ConfigCache::Clear() { std::lock_guard<std::recursive_mutex> lock(mutex_); if (information_sections_.size() > 0) { information_sections_.clear(); PersistentConfigChangedCallback(); } if (persistent_devices_.size() > 0) { persistent_devices_.clear(); PersistentConfigChangedCallback(); } if (temporary_devices_.size() > 0) { temporary_devices_.clear(); } } bool ConfigCache::HasSection(const std::string& section) const { std::lock_guard<std::recursive_mutex> lock(mutex_); Loading Loading @@ -124,6 +161,7 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first; } section_iter->second.insert_or_assign(property, std::move(value)); PersistentConfigChangedCallback(); return; } auto section_iter = persistent_devices_.find(section); Loading @@ -138,6 +176,7 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st } if (section_iter != persistent_devices_.end()) { section_iter->second.insert_or_assign(property, std::move(value)); PersistentConfigChangedCallback(); return; } section_iter = temporary_devices_.find(section); Loading @@ -151,8 +190,12 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st bool ConfigCache::RemoveSection(const std::string& section) { std::lock_guard<std::recursive_mutex> lock(mutex_); // sections are unique among all three maps, hence removing from one of them is enough return information_sections_.extract(section) || persistent_devices_.extract(section) || temporary_devices_.extract(section); if (information_sections_.extract(section) || persistent_devices_.extract(section)) { PersistentConfigChangedCallback(); return true; } else { return temporary_devices_.extract(section).has_value(); } } bool ConfigCache::RemoveProperty(const std::string& section, const std::string& property) { Loading @@ -169,7 +212,12 @@ bool ConfigCache::RemoveProperty(const std::string& section, const std::string& auto section_properties = persistent_devices_.extract(section); temporary_devices_.insert_or_assign(section, std::move(section_properties->second)); } return value.has_value(); if (value.has_value()) { PersistentConfigChangedCallback(); return true; } else { return false; } } section_iter = temporary_devices_.find(section); if (section_iter != temporary_devices_.end()) { Loading @@ -186,13 +234,30 @@ bool ConfigCache::IsLinkKeyProperty(const std::string& property) { return kLinkKeyPropertyNames.find(property) != kLinkKeyPropertyNames.end(); } void ConfigCache::RemoveRestricted() { void ConfigCache::RemoveSectionWithProperty(const std::string& property) { std::lock_guard<std::recursive_mutex> lock(mutex_); for (auto& section : persistent_devices_) { if (section.second.contains("Restricted")) { LOG_DEBUG("Removing restricted device %s", section.first.c_str()); persistent_devices_.extract(section.first); size_t num_persistent_removed = 0; for (auto* config_section : {&information_sections_, &persistent_devices_}) { for (auto it = config_section->begin(); it != config_section->end();) { if (it->second.contains(property)) { LOG_DEBUG("Removing persistent section %s with property %s", it->first.c_str(), property.c_str()); it = config_section->erase(it); num_persistent_removed++; continue; } it++; } } for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) { if (it->second.contains(property)) { LOG_DEBUG("Removing temporary section %s with property %s", it->first.c_str(), property.c_str()); it = temporary_devices_.erase(it); continue; } it++; } if (num_persistent_removed > 0) { PersistentConfigChangedCallback(); } } Loading Loading @@ -224,5 +289,20 @@ void ConfigCache::Commit(Mutation& mutation) { } } std::string ConfigCache::SerializeToLegacyFormat() const { std::lock_guard<std::recursive_mutex> lock(mutex_); std::stringstream serialized; for (const auto* config_section : {&information_sections_, &persistent_devices_}) { for (const auto& section : *config_section) { serialized << "[" << section.first << "]" << std::endl; for (const auto& property : section.second) { serialized << property.first << " = " << property.second << std::endl; } serialized << std::endl; } } return serialized.str(); } } // namespace storage } // namespace bluetooth No newline at end of file system/gd/storage/config_cache.h +20 −11 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #pragma once #include <functional> #include <list> #include <mutex> #include <optional> Loading Loading @@ -49,14 +50,9 @@ class ConfigCache { ConfigCache(ConfigCache&& other) noexcept; ConfigCache& operator=(ConfigCache&& other) noexcept; // comparison operators bool operator==(const ConfigCache& rhs) const { return information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ && temporary_devices_ == rhs.temporary_devices_; } bool operator!=(const ConfigCache& rhs) const { return !(*this == rhs); } // comparison operators, callback doesn't count bool operator==(const ConfigCache& rhs) const; bool operator!=(const ConfigCache& rhs) const; // observers virtual bool HasSection(const std::string& section) const; Loading @@ -65,6 +61,8 @@ class ConfigCache { virtual std::optional<std::string> GetProperty(const std::string& section, const std::string& property) const; // Returns a copy of persistent device MAC addresses virtual std::vector<std::string> GetPersistentDevices() const; // Serialize to legacy config format virtual std::string SerializeToLegacyFormat() const; // modifiers // Commit all mutation entries in sequence while holding the config mutex Loading @@ -73,10 +71,12 @@ class ConfigCache { virtual bool RemoveSection(const std::string& section); virtual bool RemoveProperty(const std::string& section, const std::string& property); // TODO: have a systematic way of doing this instead of specialized methods // Remove devices with "Restricted" property virtual void RemoveRestricted(); // Remove sections with |property| set virtual void RemoveSectionWithProperty(const std::string& property); // remove all content in this config cache, restore it to the state after the explicit constructor virtual void Clear(); // Set a callback to notify interested party that a persistent config change has just happened virtual void SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback); // static methods // Check if section is formatted as a MAC address Loading @@ -85,10 +85,12 @@ class ConfigCache { static bool IsLinkKeyProperty(const std::string& property); // constants static constexpr std::string_view kDefaultSectionName = "Global"; static const std::string kDefaultSectionName; private: mutable std::recursive_mutex mutex_; // A callback to notify interested party that a persistent config change has just happened, empty by default std::function<void()> persistent_config_changed_callback_; // Common section that does not relate to remote device, will be written to disk common::ListMap<std::string, common::ListMap<std::string, std::string>> information_sections_; // Information about persistent devices, normally paired, will be written to disk Loading @@ -96,6 +98,13 @@ class ConfigCache { // Information about temporary devices, normally unpaired, will not be written to disk, will be evicted automatically // if capacity exceeds given value during initialization common::LruCache<std::string, common::ListMap<std::string, std::string>> temporary_devices_; // Convenience method to check if the callback is valid before calling it inline void PersistentConfigChangedCallback() const { if (persistent_config_changed_callback_) { persistent_config_changed_callback_(); } } }; } // namespace storage Loading Loading
system/gd/os/linux_generic/files.cc +10 −11 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <sys/stat.h> #include <unistd.h> #include <cctype> #include <cerrno> #include <cstring> #include <fstream> Loading Loading @@ -108,27 +107,27 @@ bool WriteToFile(const std::string& path, const std::string& data) { return false; } int dir_fd = open(directory_path.c_str(), O_RDONLY); int dir_fd = open(directory_path.c_str(), O_RDONLY | O_DIRECTORY); if (dir_fd < 0) { LOG_ERROR("unable to open dir '%s', error: %s", directory_path.c_str(), strerror(errno)); return false; } FILE* fp = fopen(temp_path.c_str(), "wt"); FILE* fp = std::fopen(temp_path.c_str(), "wt"); if (!fp) { LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } if (fprintf(fp, "%s", data.c_str()) < 0) { if (std::fprintf(fp, "%s", data.c_str()) < 0) { LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // Flush the stream buffer to the temp file. if (fflush(fp) < 0) { if (std::fflush(fp) != 0) { LOG_ERROR("unable to write flush buffer to file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading @@ -136,12 +135,12 @@ bool WriteToFile(const std::string& path, const std::string& data) { // Sync written temp file out to disk. fsync() is blocking until data makes it // to disk. if (fsync(fileno(fp)) < 0) { if (fsync(fileno(fp)) != 0) { LOG_WARN("unable to fsync file '%s', error: %s", temp_path.c_str(), strerror(errno)); // Allow fsync to fail and continue } if (fclose(fp) != 0) { if (std::fclose(fp) != 0) { LOG_ERROR("unable to close file '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading @@ -149,25 +148,25 @@ bool WriteToFile(const std::string& path, const std::string& data) { fp = nullptr; // Change the file's permissions to Read/Write by User and Group if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) { if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) { LOG_ERROR("unable to change file permissions '%s', error: %s", temp_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // Rename written temp file to the actual config file. if (rename(temp_path.c_str(), path.c_str()) == -1) { if (std::rename(temp_path.c_str(), path.c_str()) == -1) { LOG_ERROR("unable to commit file from '%s' to '%s', error: %s", temp_path.c_str(), path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; } // This should ensure the directory is updated as well. if (fsync(dir_fd) < 0) { if (fsync(dir_fd) != 0) { LOG_WARN("unable to fsync dir '%s', error: %s", directory_path.c_str(), strerror(errno)); } if (close(dir_fd) < 0) { if (close(dir_fd) != 0) { LOG_ERROR("unable to close dir '%s', error: %s", directory_path.c_str(), strerror(errno)); HandleError(temp_path, &dir_fd, &fp); return false; Loading
system/gd/os/linux_generic/files_test.cc +3 −4 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <cstdio> #include <filesystem> namespace testing { Loading @@ -33,7 +32,7 @@ TEST(FilesTest, write_read_loopback_test) { std::string text = "Hello world!\n"; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, overwrite_test) { Loading @@ -45,7 +44,7 @@ TEST(FilesTest, overwrite_test) { text = "Foo bar!\n"; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, write_read_empty_string_test) { Loading @@ -54,7 +53,7 @@ TEST(FilesTest, write_read_empty_string_test) { std::string text; ASSERT_TRUE(WriteToFile(temp_file.string(), text)); EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text))); EXPECT_EQ(remove(temp_file.c_str()), 0); EXPECT_TRUE(std::filesystem::remove(temp_file)); } TEST(FilesTest, read_non_existing_file_test) { Loading
system/gd/storage/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ filegroup { "config_cache.cc", "config_cache_helper.cc", "legacy.cc", "legacy_config_file.cc", "legacy_osi_config.cc", "mutation_entry.cc", ], Loading @@ -14,6 +15,7 @@ filegroup { srcs: [ "config_cache_test.cc", "config_cache_helper_test.cc", "legacy_config_file_test.cc", "legacy_test.cc", "legacy_osi_config.cc", "mutation_test.cc", Loading
system/gd/storage/config_cache.cc +93 −13 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #include "storage/config_cache.h" #include <ios> #include <sstream> #include <utility> #include "storage/mutation.h" Loading @@ -40,29 +42,64 @@ namespace storage { const std::unordered_set<std::string_view> kLinkKeyPropertyNames = { "LinkKey", "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"}; const std::string ConfigCache::kDefaultSectionName = "Global"; ConfigCache::ConfigCache(size_t temp_device_capacity) : information_sections_(), persistent_devices_(), temporary_devices_(temp_device_capacity) {} void ConfigCache::SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback) { std::lock_guard<std::recursive_mutex> lock(mutex_); persistent_config_changed_callback_ = std::move(persistent_config_changed_callback); } ConfigCache::ConfigCache(ConfigCache&& other) noexcept : information_sections_(std::move(other.information_sections_)), : persistent_config_changed_callback_(std::move(other.persistent_config_changed_callback_)), information_sections_(std::move(other.information_sections_)), persistent_devices_(std::move(other.persistent_devices_)), temporary_devices_(std::move(other.temporary_devices_)) {} temporary_devices_(std::move(other.temporary_devices_)) { // std::function will be in a valid but unspecified state after std::move(), hence resetting it other.persistent_config_changed_callback_ = {}; } ConfigCache& ConfigCache::operator=(ConfigCache&& other) noexcept { if (&other == this) { return *this; } std::lock_guard<std::recursive_mutex> my_lock(mutex_); std::lock_guard<std::recursive_mutex> others_lock(other.mutex_); persistent_config_changed_callback_.swap(other.persistent_config_changed_callback_); other.persistent_config_changed_callback_ = {}; information_sections_ = std::move(other.information_sections_); persistent_devices_ = std::move(other.persistent_devices_); temporary_devices_ = std::move(other.temporary_devices_); return *this; } bool ConfigCache::operator==(const ConfigCache& rhs) const { std::lock_guard<std::recursive_mutex> my_lock(mutex_); std::lock_guard<std::recursive_mutex> others_lock(rhs.mutex_); return information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ && temporary_devices_ == rhs.temporary_devices_; } bool ConfigCache::operator!=(const ConfigCache& rhs) const { return !(*this == rhs); } void ConfigCache::Clear() { std::lock_guard<std::recursive_mutex> lock(mutex_); if (information_sections_.size() > 0) { information_sections_.clear(); PersistentConfigChangedCallback(); } if (persistent_devices_.size() > 0) { persistent_devices_.clear(); PersistentConfigChangedCallback(); } if (temporary_devices_.size() > 0) { temporary_devices_.clear(); } } bool ConfigCache::HasSection(const std::string& section) const { std::lock_guard<std::recursive_mutex> lock(mutex_); Loading Loading @@ -124,6 +161,7 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first; } section_iter->second.insert_or_assign(property, std::move(value)); PersistentConfigChangedCallback(); return; } auto section_iter = persistent_devices_.find(section); Loading @@ -138,6 +176,7 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st } if (section_iter != persistent_devices_.end()) { section_iter->second.insert_or_assign(property, std::move(value)); PersistentConfigChangedCallback(); return; } section_iter = temporary_devices_.find(section); Loading @@ -151,8 +190,12 @@ void ConfigCache::SetProperty(std::string section, std::string property, std::st bool ConfigCache::RemoveSection(const std::string& section) { std::lock_guard<std::recursive_mutex> lock(mutex_); // sections are unique among all three maps, hence removing from one of them is enough return information_sections_.extract(section) || persistent_devices_.extract(section) || temporary_devices_.extract(section); if (information_sections_.extract(section) || persistent_devices_.extract(section)) { PersistentConfigChangedCallback(); return true; } else { return temporary_devices_.extract(section).has_value(); } } bool ConfigCache::RemoveProperty(const std::string& section, const std::string& property) { Loading @@ -169,7 +212,12 @@ bool ConfigCache::RemoveProperty(const std::string& section, const std::string& auto section_properties = persistent_devices_.extract(section); temporary_devices_.insert_or_assign(section, std::move(section_properties->second)); } return value.has_value(); if (value.has_value()) { PersistentConfigChangedCallback(); return true; } else { return false; } } section_iter = temporary_devices_.find(section); if (section_iter != temporary_devices_.end()) { Loading @@ -186,13 +234,30 @@ bool ConfigCache::IsLinkKeyProperty(const std::string& property) { return kLinkKeyPropertyNames.find(property) != kLinkKeyPropertyNames.end(); } void ConfigCache::RemoveRestricted() { void ConfigCache::RemoveSectionWithProperty(const std::string& property) { std::lock_guard<std::recursive_mutex> lock(mutex_); for (auto& section : persistent_devices_) { if (section.second.contains("Restricted")) { LOG_DEBUG("Removing restricted device %s", section.first.c_str()); persistent_devices_.extract(section.first); size_t num_persistent_removed = 0; for (auto* config_section : {&information_sections_, &persistent_devices_}) { for (auto it = config_section->begin(); it != config_section->end();) { if (it->second.contains(property)) { LOG_DEBUG("Removing persistent section %s with property %s", it->first.c_str(), property.c_str()); it = config_section->erase(it); num_persistent_removed++; continue; } it++; } } for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) { if (it->second.contains(property)) { LOG_DEBUG("Removing temporary section %s with property %s", it->first.c_str(), property.c_str()); it = temporary_devices_.erase(it); continue; } it++; } if (num_persistent_removed > 0) { PersistentConfigChangedCallback(); } } Loading Loading @@ -224,5 +289,20 @@ void ConfigCache::Commit(Mutation& mutation) { } } std::string ConfigCache::SerializeToLegacyFormat() const { std::lock_guard<std::recursive_mutex> lock(mutex_); std::stringstream serialized; for (const auto* config_section : {&information_sections_, &persistent_devices_}) { for (const auto& section : *config_section) { serialized << "[" << section.first << "]" << std::endl; for (const auto& property : section.second) { serialized << property.first << " = " << property.second << std::endl; } serialized << std::endl; } } return serialized.str(); } } // namespace storage } // namespace bluetooth No newline at end of file
system/gd/storage/config_cache.h +20 −11 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #pragma once #include <functional> #include <list> #include <mutex> #include <optional> Loading Loading @@ -49,14 +50,9 @@ class ConfigCache { ConfigCache(ConfigCache&& other) noexcept; ConfigCache& operator=(ConfigCache&& other) noexcept; // comparison operators bool operator==(const ConfigCache& rhs) const { return information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ && temporary_devices_ == rhs.temporary_devices_; } bool operator!=(const ConfigCache& rhs) const { return !(*this == rhs); } // comparison operators, callback doesn't count bool operator==(const ConfigCache& rhs) const; bool operator!=(const ConfigCache& rhs) const; // observers virtual bool HasSection(const std::string& section) const; Loading @@ -65,6 +61,8 @@ class ConfigCache { virtual std::optional<std::string> GetProperty(const std::string& section, const std::string& property) const; // Returns a copy of persistent device MAC addresses virtual std::vector<std::string> GetPersistentDevices() const; // Serialize to legacy config format virtual std::string SerializeToLegacyFormat() const; // modifiers // Commit all mutation entries in sequence while holding the config mutex Loading @@ -73,10 +71,12 @@ class ConfigCache { virtual bool RemoveSection(const std::string& section); virtual bool RemoveProperty(const std::string& section, const std::string& property); // TODO: have a systematic way of doing this instead of specialized methods // Remove devices with "Restricted" property virtual void RemoveRestricted(); // Remove sections with |property| set virtual void RemoveSectionWithProperty(const std::string& property); // remove all content in this config cache, restore it to the state after the explicit constructor virtual void Clear(); // Set a callback to notify interested party that a persistent config change has just happened virtual void SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback); // static methods // Check if section is formatted as a MAC address Loading @@ -85,10 +85,12 @@ class ConfigCache { static bool IsLinkKeyProperty(const std::string& property); // constants static constexpr std::string_view kDefaultSectionName = "Global"; static const std::string kDefaultSectionName; private: mutable std::recursive_mutex mutex_; // A callback to notify interested party that a persistent config change has just happened, empty by default std::function<void()> persistent_config_changed_callback_; // Common section that does not relate to remote device, will be written to disk common::ListMap<std::string, common::ListMap<std::string, std::string>> information_sections_; // Information about persistent devices, normally paired, will be written to disk Loading @@ -96,6 +98,13 @@ class ConfigCache { // Information about temporary devices, normally unpaired, will not be written to disk, will be evicted automatically // if capacity exceeds given value during initialization common::LruCache<std::string, common::ListMap<std::string, std::string>> temporary_devices_; // Convenience method to check if the callback is valid before calling it inline void PersistentConfigChangedCallback() const { if (persistent_config_changed_callback_) { persistent_config_changed_callback_(); } } }; } // namespace storage Loading