| /* |
| * 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 "storage/storage_module.h" |
| |
| #include <chrono> |
| #include <ctime> |
| #include <iomanip> |
| #include <memory> |
| #include <utility> |
| |
| #include "common/bind.h" |
| #include "os/alarm.h" |
| #include "os/files.h" |
| #include "os/handler.h" |
| #include "os/parameter_provider.h" |
| #include "os/system_properties.h" |
| #include "storage/config_cache.h" |
| #include "storage/legacy_config_file.h" |
| #include "storage/mutation.h" |
| |
| namespace bluetooth { |
| namespace storage { |
| |
| using common::ListMap; |
| using common::LruCache; |
| using os::Alarm; |
| using os::Handler; |
| |
| static const std::string kFactoryResetProperty = "persist.bluetooth.factoryreset"; |
| |
| static const size_t kDefaultTempDeviceCapacity = 10000; |
| // Save config whenever there is a change, but delay it by this value so that burst config change won't overwhelm disk |
| static const std::chrono::milliseconds kDefaultConfigSaveDelay = std::chrono::milliseconds(3000); |
| // Writing a config to disk takes a minimum 10 ms on a decent x86_64 machine, and 20 ms if including backup file |
| // The config saving delay must be bigger than this value to avoid overwhelming the disk |
| static const std::chrono::milliseconds kMinConfigSaveDelay = std::chrono::milliseconds(20); |
| |
| const int kConfigFileComparePass = 1; |
| const int kConfigBackupComparePass = 2; |
| const std::string kConfigFilePrefix = "bt_config-origin"; |
| const std::string kConfigFileHash = "hash"; |
| |
| const std::string StorageModule::kInfoSection = "Info"; |
| const std::string StorageModule::kFileSourceProperty = "FileSource"; |
| const std::string StorageModule::kTimeCreatedProperty = "TimeCreated"; |
| const std::string StorageModule::kTimeCreatedFormat = "%Y-%m-%d %H:%M:%S"; |
| |
| const std::string StorageModule::kAdapterSection = "Adapter"; |
| |
| StorageModule::StorageModule( |
| std::string config_file_path, |
| std::chrono::milliseconds config_save_delay, |
| size_t temp_devices_capacity, |
| bool is_restricted_mode, |
| bool is_single_user_mode) |
| : config_file_path_(std::move(config_file_path)), |
| config_save_delay_(config_save_delay), |
| temp_devices_capacity_(temp_devices_capacity), |
| is_restricted_mode_(is_restricted_mode), |
| is_single_user_mode_(is_single_user_mode) { |
| // e.g. "/data/misc/bluedroid/bt_config.conf" to "/data/misc/bluedroid/bt_config.bak" |
| config_backup_path_ = config_file_path_.substr(0, config_file_path_.find_last_of('.')) + ".bak"; |
| ASSERT_LOG( |
| config_save_delay > kMinConfigSaveDelay, |
| "Config save delay of %lld ms is not enough, must be at least %lld ms to avoid overwhelming the disk", |
| config_save_delay_.count(), |
| kMinConfigSaveDelay.count()); |
| }; |
| |
| StorageModule::~StorageModule() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| pimpl_.reset(); |
| } |
| |
| const ModuleFactory StorageModule::Factory = ModuleFactory([]() { |
| return new StorageModule( |
| os::ParameterProvider::ConfigFilePath(), kDefaultConfigSaveDelay, kDefaultTempDeviceCapacity, false, false); |
| }); |
| |
| struct StorageModule::impl { |
| explicit impl(Handler* handler, ConfigCache cache, size_t in_memory_cache_size_limit) |
| : config_save_alarm_(handler), cache_(std::move(cache)), memory_only_cache_(in_memory_cache_size_limit, {}) {} |
| Alarm config_save_alarm_; |
| ConfigCache cache_; |
| ConfigCache memory_only_cache_; |
| bool has_pending_config_save_ = false; |
| }; |
| |
| Mutation StorageModule::Modify() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return Mutation(&pimpl_->cache_, &pimpl_->memory_only_cache_); |
| } |
| |
| ConfigCache* StorageModule::GetConfigCache() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return &pimpl_->cache_; |
| } |
| |
| ConfigCache* StorageModule::GetMemoryOnlyConfigCache() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return &pimpl_->memory_only_cache_; |
| } |
| |
| void StorageModule::SaveDelayed() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| if (pimpl_->has_pending_config_save_) { |
| return; |
| } |
| pimpl_->config_save_alarm_.Schedule( |
| common::BindOnce(&StorageModule::SaveImmediately, common::Unretained(this)), config_save_delay_); |
| pimpl_->has_pending_config_save_ = true; |
| } |
| |
| void StorageModule::SaveImmediately() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| if (pimpl_->has_pending_config_save_) { |
| pimpl_->config_save_alarm_.Cancel(); |
| pimpl_->has_pending_config_save_ = false; |
| } |
| // 1. rename old config to backup name |
| if (os::FileExists(config_file_path_)) { |
| ASSERT(os::RenameFile(config_file_path_, config_backup_path_)); |
| } |
| // 2. write in-memory config to disk, if failed, backup can still be used |
| ASSERT(LegacyConfigFile::FromPath(config_file_path_).Write(pimpl_->cache_)); |
| // 3. now write back up to disk as well |
| ASSERT(LegacyConfigFile::FromPath(config_backup_path_).Write(pimpl_->cache_)); |
| // 4. save checksum if it is running in common criteria mode |
| if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr && |
| bluetooth::os::ParameterProvider::IsCommonCriteriaMode()) { |
| bluetooth::os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key( |
| kConfigFilePrefix, kConfigFileHash); |
| } |
| } |
| |
| void StorageModule::ListDependencies(ModuleList* list) const { |
| // No dependencies |
| } |
| |
| void StorageModule::Start() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| std::string file_source; |
| if (os::GetSystemProperty(kFactoryResetProperty) == "true") { |
| LegacyConfigFile::FromPath(config_file_path_).Delete(); |
| LegacyConfigFile::FromPath(config_backup_path_).Delete(); |
| } |
| if (!is_config_checksum_pass(kConfigFileComparePass)) { |
| LegacyConfigFile::FromPath(config_file_path_).Delete(); |
| } |
| if (!is_config_checksum_pass(kConfigBackupComparePass)) { |
| LegacyConfigFile::FromPath(config_backup_path_).Delete(); |
| } |
| auto config = LegacyConfigFile::FromPath(config_file_path_).Read(temp_devices_capacity_); |
| if (!config || !config->HasSection(kAdapterSection)) { |
| LOG_WARN("cannot load config at %s, using backup at %s.", config_file_path_.c_str(), config_backup_path_.c_str()); |
| config = LegacyConfigFile::FromPath(config_backup_path_).Read(temp_devices_capacity_); |
| file_source = "Backup"; |
| } |
| if (!config || !config->HasSection(kAdapterSection)) { |
| LOG_WARN("cannot load backup config at %s; creating new empty ones", config_backup_path_.c_str()); |
| config.emplace(temp_devices_capacity_, Device::kLinkKeyProperties); |
| file_source = "Empty"; |
| } |
| if (!file_source.empty()) { |
| config->SetProperty(kInfoSection, kFileSourceProperty, std::move(file_source)); |
| } |
| // Cleanup temporary pairings if we have left guest mode |
| if (!is_restricted_mode_) { |
| config->RemoveSectionWithProperty("Restricted"); |
| } |
| // Read or set config file creation timestamp |
| auto time_str = config->GetProperty(kInfoSection, kTimeCreatedProperty); |
| if (!time_str) { |
| std::stringstream ss; |
| auto now = std::chrono::system_clock::now(); |
| auto now_time_t = std::chrono::system_clock::to_time_t(now); |
| ss << std::put_time(std::localtime(&now_time_t), kTimeCreatedFormat.c_str()); |
| config->SetProperty(kInfoSection, kTimeCreatedProperty, ss.str()); |
| } |
| config->FixDeviceTypeInconsistencies(); |
| config->SetPersistentConfigChangedCallback([this] { this->CallOn(this, &StorageModule::SaveDelayed); }); |
| // TODO (b/158035889) Migrate metrics module to GD |
| pimpl_ = std::make_unique<impl>(GetHandler(), std::move(config.value()), temp_devices_capacity_); |
| SaveDelayed(); |
| if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr) { |
| bluetooth::os::ParameterProvider::GetBtKeystoreInterface()->ConvertEncryptOrDecryptKeyIfNeeded(); |
| } |
| } |
| |
| void StorageModule::Stop() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| SaveImmediately(); |
| if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr) { |
| bluetooth::os::ParameterProvider::GetBtKeystoreInterface()->clear_map(); |
| } |
| pimpl_.reset(); |
| } |
| |
| std::string StorageModule::ToString() const { |
| return "Storage Module"; |
| } |
| |
| Device StorageModule::GetDeviceByLegacyKey(hci::Address legacy_key_address) { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return Device( |
| &pimpl_->cache_, |
| &pimpl_->memory_only_cache_, |
| std::move(legacy_key_address), |
| Device::ConfigKeyAddressType::LEGACY_KEY_ADDRESS); |
| } |
| |
| Device StorageModule::GetDeviceByClassicMacAddress(hci::Address classic_address) { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return Device( |
| &pimpl_->cache_, |
| &pimpl_->memory_only_cache_, |
| std::move(classic_address), |
| Device::ConfigKeyAddressType::CLASSIC_ADDRESS); |
| } |
| |
| Device StorageModule::GetDeviceByLeIdentityAddress(hci::Address le_identity_address) { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return Device( |
| &pimpl_->cache_, |
| &pimpl_->memory_only_cache_, |
| std::move(le_identity_address), |
| Device::ConfigKeyAddressType::LE_IDENTITY_ADDRESS); |
| } |
| |
| AdapterConfig StorageModule::GetAdapterConfig() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| return AdapterConfig(&pimpl_->cache_, &pimpl_->memory_only_cache_, kAdapterSection); |
| } |
| |
| std::vector<Device> StorageModule::GetBondedDevices() { |
| std::lock_guard<std::recursive_mutex> lock(mutex_); |
| auto persistent_sections = GetConfigCache()->GetPersistentSections(); |
| std::vector<Device> result; |
| result.reserve(persistent_sections.size()); |
| for (const auto& section : persistent_sections) { |
| result.emplace_back(&pimpl_->cache_, &pimpl_->memory_only_cache_, section); |
| } |
| return result; |
| } |
| |
| bool StorageModule::is_config_checksum_pass(int check_bit) { |
| return ((os::ParameterProvider::GetCommonCriteriaConfigCompareResult() & check_bit) == check_bit); |
| } |
| |
| } // namespace storage |
| } // namespace bluetooth |