【Bluedroid】蓝牙存储模块设置管理:启动、读写、加密与保存流程解析 ...

打印 上一主题 下一主题

主题 1785|帖子 1785|积分 5355

本文围绕蓝牙存储模块睁开,主要解析了蓝牙存储模块(StorageModule)的初始化流程,重点围绕设置文件校验、读取、装备范例修复及加密处理睁开。通过工厂重置检测、校验和验证、多源设置加载、装备范例推断修正等步骤,确保设置完备性,并联合延迟保存机制优化存储性能。
StorageModule::Start

  1. /packages/modules/Bluetooth/system/gd/storage/storage_module.cc
  2. void StorageModule::Start() {
  3.   // 1. 线程同步
  4.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  5.   
  6.   // 2. 处理工厂重置
  7.   std::string file_source;
  8.   if (os::GetSystemProperty(kFactoryResetProperty) == "true") {
  9.     LOG_INFO("%s is true, delete config files", kFactoryResetProperty.c_str());
  10.     LegacyConfigFile::FromPath(config_file_path_).Delete();
  11.     LegacyConfigFile::FromPath(config_backup_path_).Delete();
  12.     os::SetSystemProperty(kFactoryResetProperty, "false");
  13.   }
  14.   
  15.   // 3. 校验配置文件的校验和
  16.   if (!is_config_checksum_pass(kConfigFileComparePass)) {
  17.     LegacyConfigFile::FromPath(config_file_path_).Delete();
  18.   }
  19.   if (!is_config_checksum_pass(kConfigBackupComparePass)) {
  20.     LegacyConfigFile::FromPath(config_backup_path_).Delete();
  21.   }
  22.   
  23.   // 4. 读取配置文件
  24.   bool save_needed = false;
  25.   auto config = LegacyConfigFile::FromPath(config_file_path_).Read(temp_devices_capacity_);
  26.   if (!config || !config->HasSection(kAdapterSection)) {
  27.     LOG_WARN("cannot load config at %s, using backup at %s.", config_file_path_.c_str(), config_backup_path_.c_str());
  28.     config = LegacyConfigFile::FromPath(config_backup_path_).Read(temp_devices_capacity_);
  29.     file_source = "Backup";
  30.     // Make sure to update the file, since it wasn't read from the config_file_path_
  31.     save_needed = true;
  32.   }
  33.   if (!config || !config->HasSection(kAdapterSection)) {
  34.     LOG_WARN("cannot load backup config at %s; creating new empty ones", config_backup_path_.c_str());
  35.     config.emplace(temp_devices_capacity_, Device::kLinkKeyProperties);
  36.     file_source = "Empty";
  37.   }
  38.   
  39.   // 5. 设置文件源信息
  40.   if (!file_source.empty()) {
  41.     config->SetProperty(kInfoSection, kFileSourceProperty, std::move(file_source));
  42.   }
  43.   
  44.   // 6. 清理临时配对信息
  45.   // Cleanup temporary pairings if we have left guest mode
  46.   if (!is_restricted_mode_) {
  47.     config->RemoveSectionWithProperty("Restricted");
  48.   }
  49.   
  50.   // 7. 设置配置文件创建时间戳
  51.   // Read or set config file creation timestamp
  52.   auto time_str = config->GetProperty(kInfoSection, kTimeCreatedProperty);
  53.   if (!time_str) {
  54.     std::stringstream ss;
  55.     auto now = std::chrono::system_clock::now();
  56.     auto now_time_t = std::chrono::system_clock::to_time_t(now);
  57.     ss << std::put_time(std::localtime(&now_time_t), kTimeCreatedFormat.c_str());
  58.     config->SetProperty(kInfoSection, kTimeCreatedProperty, ss.str());
  59.   }
  60.   
  61.   // 8. 修复设备类型不一致问题
  62.   config->FixDeviceTypeInconsistencies();
  63.   
  64.   // 9. 创建 impl 对象
  65.   // TODO (b/158035889) Migrate metrics module to GD
  66.   pimpl_ = std::make_unique<impl>(GetHandler(), std::move(config.value()), temp_devices_capacity_);
  67.   
  68.   // 10. 延迟保存配置文件
  69.   if (save_needed) {
  70.     // Set a timer and write the new config file to disk.
  71.     SaveDelayed();
  72.   }
  73.   
  74.   // 11. 设置持久化配置更改回调
  75.   pimpl_->cache_.SetPersistentConfigChangedCallback(
  76.       [this] { this->CallOn(this, &StorageModule::SaveDelayed); });
  77.       
  78.   // 12. 必要时转换加密或解密密钥
  79.   if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr) {
  80.     bluetooth::os::ParameterProvider::GetBtKeystoreInterface()->ConvertEncryptOrDecryptKeyIfNeeded();
  81.   }
  82. }
复制代码
StorageModule::Start 函数是 StorageModule 类的启动函数,主要负责初始化存储模块,包括处理工厂重置、校验设置文件的校验和、读取设置文件、清理临时配对信息、设置设置文件创建时间戳等操作,末了创建 impl 对象并在必要时延迟保存设置文件。
LegacyConfigFile::Read

  1. /packages/modules/Bluetooth/system/gd/storage/legacy_config_file.cc
  2. std::optional<ConfigCache> LegacyConfigFile::Read(size_t temp_devices_capacity) {
  3.   // 1. 路径检查与文件打开
  4.   ASSERT(!path_.empty());
  5.   std::ifstream config_file(path_);
  6.   if (!config_file || !config_file.is_open()) {
  7.     LOG_ERROR("unable to open file '%s', error: %s", path_.c_str(), strerror(errno));
  8.     return std::nullopt;
  9.   }
  10.   
  11.   // 2. 初始化变量
  12.   [[maybe_unused]] int line_num = 0;
  13.   ConfigCache cache(temp_devices_capacity, Device::kLinkKeyProperties);
  14.   std::string line;
  15.   std::string section(ConfigCache::kDefaultSectionName);
  16.   
  17.   // 3. 逐行读取文件
  18.   while (std::getline(config_file, line)) {
  19.     ++line_num;
  20.     line = common::StringTrim(std::move(line)); // 去除行首尾的空白字符
  21.     if (line.empty()) {
  22.       continue;
  23.     }
  24.     if (line.front() == '\0' || line.front() == '#') {
  25.       continue;
  26.     }
  27.    
  28.     // 4. 处理配置文件节
  29.     if (line.front() == '[') {
  30.       if (line.back() != ']') {
  31.         LOG_WARN("unterminated section name on line %d", line_num);
  32.         return std::nullopt;
  33.       }
  34.       // Read 'test' from '[text]', hence -2
  35.       section = line.substr(1, line.size() - 2); // 提取方括号内的节名
  36.     }
  37.    
  38.     // 5. 处理键值对
  39.     else {
  40.       auto tokens = common::StringSplit(line, "=", 2);
  41.       if (tokens.size() != 2) {
  42.         LOG_WARN("no key/value separator found on line %d", line_num);
  43.         return std::nullopt;
  44.       }
  45.       tokens[0] = common::StringTrim(std::move(tokens[0]));
  46.       tokens[1] = common::StringTrim(std::move(tokens[1]));
  47.       cache.SetProperty(section, tokens[0], std::move(tokens[1]));
  48.     }
  49.   }
  50.   return cache;
  51. }
复制代码
从指定路径的设置文件中读取设置信息,并将其解析为 ConfigCache 对象。如果文件打开失败、设置文件格式错误,将返回 std::nullopt 表现读取失败;若读取和解析乐成,则返回包罗设置信息的 ConfigCache 对象。
ConfigCache::SetProperty

  1. packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. void ConfigCache::SetProperty(std::string section, std::string property, std::string value) {
  3.   // 1. 线程同步
  4.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  5.   
  6.   // 2.去除 section、property 和 value 字符串中的换行符
  7.   TrimAfterNewLine(section);
  8.   TrimAfterNewLine(property);
  9.   TrimAfterNewLine(value);
  10.   ASSERT_LOG(!section.empty(), "Empty section name not allowed");
  11.   ASSERT_LOG(!property.empty(), "Empty property name not allowed");
  12.   
  13.   // 3. 处理非设备节
  14.   if (!IsDeviceSection(section)) {
  15.     auto section_iter = information_sections_.find(section);
  16.     if (section_iter == information_sections_.end()) {
  17.       section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
  18.     }
  19.     section_iter->second.insert_or_assign(property, std::move(value));
  20.     PersistentConfigChangedCallback();
  21.     return;
  22.   }
  23.   
  24.   // 4. 处理设备节且属性为持久属性的情况
  25.   auto section_iter = persistent_devices_.find(section);
  26.   if (section_iter == persistent_devices_.end() && IsPersistentProperty(property)) {
  27.     // move paired devices or create new paired device when a link key is set
  28.     auto section_properties = temporary_devices_.extract(section);
  29.     if (section_properties) {
  30.       section_iter = persistent_devices_.try_emplace_back(section, std::move(section_properties->second)).first;
  31.     } else { //创建一个空的 common::ListMap 用于存储属性和值
  32.       section_iter = persistent_devices_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
  33.     }
  34.   }
  35.   
  36.   // 5. 持久设备节属性的加密处理与存储
  37.   if (section_iter != persistent_devices_.end()) {
  38.     bool is_encrypted = value == kEncryptedStr;
  39.     // 值不为空、蓝牙密钥库接口可用、系统处于通用准则模式、属性在加密密钥名称列表中且值未加密
  40.     if ((!value.empty()) && os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
  41.         os::ParameterProvider::IsCommonCriteriaMode() && InEncryptKeyNameList(property) && !is_encrypted) {
  42.       // 对值进行加密
  43.       if (os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
  44.               section + "-" + property, value)) {
  45.         value = kEncryptedStr;
  46.       }
  47.     }
  48.     // 将属性和值插入或更新到该节的 common::ListMap 中
  49.     section_iter->second.insert_or_assign(property, std::move(value));
  50.     // 通知配置发生更改
  51.     PersistentConfigChangedCallback();
  52.     return;
  53.   }
  54.   
  55.   // 6. 处理临时设备节
  56.   section_iter = temporary_devices_.find(section);
  57.   if (section_iter == temporary_devices_.end()) {
  58.     auto triple = temporary_devices_.try_emplace(section, common::ListMap<std::string, std::string>{});
  59.     section_iter = std::get<0>(triple);
  60.   }
  61.   section_iter->second.insert_or_assign(property, std::move(value));
  62. }
复制代码
在设置缓存中设置指定节(section)下的属性(property)及其对应的值(value)。根据节的范例(是否为装备节)和属性的持久性,将属性和值存储到不同的存储布局中,同时支持在特定条件下对敏感属性值举行加密处理,并在设置发生更改时调用回调函数。
ConfigCache::FixDeviceTypeInconsistencies

  1. packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. bool ConfigCache::FixDeviceTypeInconsistencies() {
  3.   // 1. 线程同步
  4.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  5.   bool persistent_device_changed = false;
  6.   
  7.   // 2. 遍历信息部分和持久设备部分
  8.   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
  9.     for (auto& elem : *config_section) {
  10.       if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
  11.         persistent_device_changed = true;
  12.       }
  13.     }
  14.   }
  15.   
  16.   // 3. 遍历临时设备部分
  17.   bool temp_device_changed = false;
  18.   for (auto& elem : temporary_devices_) {
  19.     if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
  20.       temp_device_changed = true;
  21.     }
  22.   }
  23.   
  24.   // 4. 处理持久设备部分的变更
  25.   if (persistent_device_changed) {
  26.     PersistentConfigChangedCallback();
  27.   }
  28.   return persistent_device_changed || temp_device_changed;
  29. }
复制代码
修复设置缓存中装备范例的不一致题目。遍历设置缓存中的不同部分(包括信息部分、持久装备部分和临时装备部分),针对每个部分调用 FixDeviceTypeInconsistencyInSection 函数来检查并修复装备范例的不一致。若有任何部分的装备范例发生了改变,会调用 PersistentConfigChangedCallback 函数关照设置发生了持久化变更,末了返回是否有装备范例被修改的效果。
FixDeviceTypeInconsistencyInSection

  1. /packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. namespace {
  3. bool FixDeviceTypeInconsistencyInSection(
  4.     const std::string& section_name, common::ListMap<std::string, std::string>& device_section_entries) {
  5.   // 1. 检查节名是否为有效的蓝牙地址
  6.   if (!hci::Address::IsValidAddress(section_name)) {
  7.     return false;
  8.   }
  9.   
  10.   // 2. 处理设备类型为双模蓝牙(DUAL)的情况
  11.   auto device_type_iter = device_section_entries.find("DevType");
  12.   if (device_type_iter != device_section_entries.end() &&
  13.       device_type_iter->second == std::to_string(hci::DeviceType::DUAL)) {
  14.     // We might only have one of classic/LE keys for a dual device, but it is still a dual device,
  15.     // so we should not change the DevType.
  16.     return false;
  17.   }
  18.   // 3. 推断设备的实际类型
  19.   // we will ignore the existing DevType, since it is not known to be a DUAL device so
  20.   // the keys we have should be sufficient to infer the correct DevType
  21.   bool is_le = false;
  22.   bool is_classic = false;
  23.   // default
  24.   hci::DeviceType device_type = hci::DeviceType::BR_EDR;
  25.   for (const auto& entry : device_section_entries) {
  26.     if (kLePropertyNames.find(entry.first) != kLePropertyNames.end()) {
  27.       is_le = true;
  28.     }
  29.     if (kClassicPropertyNames.find(entry.first) != kClassicPropertyNames.end()) {
  30.       is_classic = true;
  31.     }
  32.   }
  33.   if (is_classic && is_le) {
  34.     device_type = hci::DeviceType::DUAL;
  35.   } else if (is_classic) {
  36.     device_type = hci::DeviceType::BR_EDR;
  37.   } else if (is_le) {
  38.     device_type = hci::DeviceType::LE;
  39.   }
  40.   
  41.   // 4. 检查并更新设备类型
  42.   bool inconsistent = true; // 表示默认存在不一致
  43.   std::string device_type_str = std::to_string(device_type);
  44.   if (device_type_iter != device_section_entries.end()) {
  45.     inconsistent = device_type_str != device_type_iter->second;
  46.     if (inconsistent) {
  47.       device_type_iter->second = std::move(device_type_str);
  48.     }
  49.   } else {
  50.     device_section_entries.insert_or_assign("DevType", std::move(device_type_str));
  51.   }
  52.   return inconsistent;
  53. }
  54. }  // namespace
复制代码
修复设置节中装备范例的不一致题目。根据装备设置节中的属性信息,推断出装备的实际范例(如经典蓝牙、低功耗蓝牙或双模蓝牙),并与设置节中已有的装备范例举行比较。如果存在不一致,则更新设置节中的装备范例,末了返回是否举行了更新的标志。
ConfigCache::RemoveSectionWithProperty

  1. packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. void ConfigCache::RemoveSectionWithProperty(const std::string& property) {
  3.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  4.   size_t num_persistent_removed = 0;
  5.   
  6.   // 遍历信息部分和持久设备部分
  7.   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
  8.     for (auto it = config_section->begin(); it != config_section->end();) {
  9.       if (it->second.contains(property)) {
  10.         LOG_INFO("Removing persistent section %s with property %s", it->first.c_str(), property.c_str());
  11.         it = config_section->erase(it);
  12.         num_persistent_removed++;
  13.         continue;
  14.       }
  15.       it++;
  16.     }
  17.   }
  18.   
  19.   //  遍历临时设备部分
  20.   for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) {
  21.     if (it->second.contains(property)) {
  22.       LOG_INFO("Removing temporary section %s with property %s", it->first.c_str(), property.c_str());
  23.       it = temporary_devices_.erase(it);
  24.       continue;
  25.     }
  26.     it++;
  27.   }
  28.   
  29.   // 处理持久设备部分的变更
  30.   if (num_persistent_removed > 0) {
  31.     PersistentConfigChangedCallback();
  32.   }
  33. }
复制代码
从设置缓存里移除所有包罗指定属性的节。遍历设置缓存的不同部分,也就是信息部分、持久装备部分以及临时装备部分,一旦发现某个节包罗指定属性,就将该节移除。如果持久装备部分有节被移除,会调用 PersistentConfigChangedCallback 函数来关照设置发生了持久化变更。
ConfigCache::GetProperty

  1. /packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. std::optional<std::string> ConfigCache::GetProperty(const std::string& section, const std::string& property) const {
  3.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  4.   
  5.   //  在信息部分查找属性
  6.   auto section_iter = information_sections_.find(section);
  7.   if (section_iter != information_sections_.end()) {
  8.     auto property_iter = section_iter->second.find(property);
  9.     if (property_iter != section_iter->second.end()) {
  10.       return property_iter->second;
  11.     }
  12.   }
  13.   
  14.   //  在持久设备部分查找属性
  15.   section_iter = persistent_devices_.find(section);
  16.   if (section_iter != persistent_devices_.end()) {
  17.     auto property_iter = section_iter->second.find(property);
  18.     if (property_iter != section_iter->second.end()) {
  19.       std::string value = property_iter->second;
  20.       if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && value == kEncryptedStr) {
  21.         return os::ParameterProvider::GetBtKeystoreInterface()->get_key(section + "-" + property);
  22.       }
  23.       return value;
  24.     }
  25.   }
  26.   
  27.   // 在临时设备部分查找属性
  28.   section_iter = temporary_devices_.find(section);
  29.   if (section_iter != temporary_devices_.end()) {
  30.     auto property_iter = section_iter->second.find(property);
  31.     if (property_iter != section_iter->second.end()) {
  32.       return property_iter->second;
  33.     }
  34.   }
  35.   return std::nullopt;
  36. }
复制代码
从设置缓存中获取指定节(section)下指定属性(property)的值。依次在信息部分、持久装备部分和临时装备部分查找该属性,如果找到且满足特定条件(如加密处理)则返回对应的值,若未找到则返回 std::nullopt。
SaveDelayed

延迟保存存储模块的设置信息。安排一个延迟任务,在指定的时间后调用 SaveImmediately 函数来实际保存设置。通过这种方式,可以避免频仍地保存设置,减少对存储装备的读写操作,提高性能。
StorageModule::SaveImmediately

  1. /packages/modules/Bluetooth/system/gd/storage/config_cache.cc
  2. void StorageModule::SaveImmediately() {
  3.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  4.   
  5.   //  处理待执行的保存任务
  6.   if (pimpl_->has_pending_config_save_) {
  7.     pimpl_->config_save_alarm_.Cancel();
  8.     pimpl_->has_pending_config_save_ = false;
  9.   }
  10.   
  11.   // 重命名旧的配置文件为备份文件
  12.   // 1. rename old config to backup name
  13.   if (os::FileExists(config_file_path_)) {
  14.     ASSERT(os::RenameFile(config_file_path_, config_backup_path_));
  15.   }
  16.   
  17.   // 将内存中的配置信息写入主配置文件
  18.   // 2. write in-memory config to disk, if failed, backup can still be used
  19.   ASSERT(LegacyConfigFile::FromPath(config_file_path_).Write(pimpl_->cache_));
  20.   
  21.   // 将内存中的配置信息写入备份文件
  22.   // 3. now write back up to disk as well
  23.   if (!LegacyConfigFile::FromPath(config_backup_path_).Write(pimpl_->cache_)) {
  24.     LOG_ERROR("Unable to write backup config file");
  25.   }
  26.   
  27.   // 在特定条件下保存配置文件的校验和
  28.   // 4. save checksum if it is running in common criteria mode
  29.   if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
  30.       bluetooth::os::ParameterProvider::IsCommonCriteriaMode()) {
  31.     bluetooth::os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
  32.         kConfigFilePrefix, kConfigFileHash);
  33.   }
  34. }
复制代码
立即将存储模块中的设置信息保存到磁盘上。处理待执行的保存任务、对旧设置文件举行重命名、将内存中的设置信息写入主设置文件和备份文件,而且在满足特定条件时保存设置文件的校验和。
FileExists

  1. packages/modules/Bluetooth/system/gd/os/linux_generic/files.cc
  2. bool FileExists(const std::string& path) {
  3.   // 尝试打开文件
  4.   std::ifstream input(path, std::ios::binary | std::ios::ate);
  5.   return input.good(); // 返回文件流状态
  6. }
复制代码
input(path, std::ios::binary | std::ios::ate):调用 std::ifstream 的构造函数来尝试打开指定路径的文件。


  • path:要打开的文件的路径。
  • std::ios::binary:以二进制模式打开文件,这样可以避免在读取文件时举行文本模式的转换,确保数据的原始性。
  • std::ios::ate:打开文件后将文件指针定位到文件末端,ate 是 at end 的缩写。此模式在判断文件是否存在时并非必需,但它允许在后续需要时获取文件的巨细。
RenameFile

  1. /packages/modules/Bluetooth/system/gd/os/linux_generic/files.cc
  2. bool RenameFile(const std::string& from, const std::string& to) {
  3.   if (std::rename(from.c_str(), to.c_str()) != 0) {
  4.     LOG_ERROR("unable to rename file from '%s' to '%s', error: %s", from.c_str(), to.c_str(), strerror(errno));
  5.     return false;
  6.   }
  7.   return true;
  8. }
复制代码
LegacyConfigFile::Write

  1. /packages/modules/Bluetooth/system/gd/storage/legacy_config_file.cc
  2. bool LegacyConfigFile::Write(const ConfigCache& cache) {
  3.   return os::WriteToFile(path_, cache.SerializeToLegacyFormat());
  4. }
复制代码
WriteToFile

  1. packages/modules/Bluetooth/system/gd/os/linux_generic/files.cc
  2. bool WriteToFile(const std::string& path, const std::string& data) {
  3.   ASSERT(!path.empty());
  4.   // Steps to ensure content of data gets to disk:
  5.   //
  6.   // 1) Open and write to temp file (e.g. bt_config.conf.new).
  7.   // 2) Flush the stream buffer to the temp file.
  8.   // 3) Sync the tempReadSmallFile file to disk with fsync().
  9.   // 4) Rename temp file to actual config file (e.g. bt_config.conf).
  10.   //    This ensures atomic update.
  11.   // 5) Sync directory that has the conf file with fsync().
  12.   //    This ensures directory entries are up-to-date.
  13.   //
  14.   // We are using traditional C type file methods because C++ std::filesystem and std::ofstream do not support:
  15.   // - Operation on directories
  16.   // - fsync() to ensure content is written to disk
  17.   // 1. 构建临时文件路径和提取目录路径
  18.   // Build temp config file based on config file (e.g. bt_config.conf.new).
  19.   const std::string temp_path = path + ".new"; // 临时文件的路径,在原文件路径后加上 .new
  20.   // Extract directory from file path (e.g. /data/misc/bluedroid).
  21.   // libc++fs is not supported in APEX yet and hence cannot use std::filesystem::path::parent_path
  22.   std::string directory_path; // 提取文件路径中的目录部分
  23.   {
  24.     // Make a temporary variable as inputs to dirname() will be modified and return value points to input char array
  25.     // temp_path_for_dir must not be destroyed until results from dirname is appended to directory_path
  26.     std::string temp_path_for_dir(path);
  27.     directory_path.append(dirname(temp_path_for_dir.data())); //获取目录路径
  28.   }
  29.   if (directory_path.empty()) {
  30.     LOG_ERROR("error extracting directory from '%s', error: %s", path.c_str(), strerror(errno));
  31.     return false;
  32.   }
  33.   // 2. 打开目录文件描述符
  34.   int dir_fd = open(directory_path.c_str(), O_RDONLY | O_DIRECTORY);
  35.   if (dir_fd < 0) {
  36.     LOG_ERROR("unable to open dir '%s', error: %s", directory_path.c_str(), strerror(errno));
  37.     return false;
  38.   }
  39.   // 3. 打开临时文件并写入数据
  40.   FILE* fp = std::fopen(temp_path.c_str(), "wt");
  41.   if (!fp) {
  42.     LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno));
  43.     HandleError(temp_path, &dir_fd, &fp);
  44.     return false;
  45.   }
  46.   if (std::fprintf(fp, "%s", data.c_str()) < 0) {
  47.     LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno));
  48.     HandleError(temp_path, &dir_fd, &fp);
  49.     return false;
  50.   }
  51.   // 4. 刷新缓冲区并同步到磁盘
  52.   // Flush the stream buffer to the temp file.
  53.   if (std::fflush(fp) != 0) {
  54.     LOG_ERROR("unable to write flush buffer to file '%s', error: %s", temp_path.c_str(), strerror(errno));
  55.     HandleError(temp_path, &dir_fd, &fp);
  56.     return false;
  57.   }
  58.   // Sync written temp file out to disk. fsync() is blocking until data makes it
  59.   // to disk.
  60.   if (fsync(fileno(fp)) != 0) { // 将临时文件同步到磁盘
  61.     LOG_WARN("unable to fsync file '%s', error: %s", temp_path.c_str(), strerror(errno));
  62.     // Allow fsync to fail and continue
  63.   }
  64.   // 5. 关闭文件并更改文件权限
  65.   if (std::fclose(fp) != 0) {
  66.     LOG_ERROR("unable to close file '%s', error: %s", temp_path.c_str(), strerror(errno));
  67.     HandleError(temp_path, &dir_fd, &fp);
  68.     return false;
  69.   }
  70.   fp = nullptr;
  71.   // Change the file's permissions to Read/Write by User and Group
  72.   if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) {
  73.     LOG_ERROR("unable to change file permissions '%s', error: %s", temp_path.c_str(), strerror(errno));
  74.     struct stat dirstat {};
  75.     if (fstat(dir_fd, &dirstat) == 0) {
  76.       LOG_ERROR("dir st_mode = 0x%02x", dirstat.st_mode);
  77.       LOG_ERROR("dir uid = %d", dirstat.st_uid);
  78.       LOG_ERROR("dir gid = %d", dirstat.st_gid);
  79.     } else {
  80.       LOG_ERROR("unable to call fstat on the directory, error: %s", strerror(errno));
  81.     }
  82.     struct stat filestat {};
  83.     if (stat(temp_path.c_str(), &filestat) == 0) {
  84.       LOG_ERROR("file st_mode = 0x%02x", filestat.st_mode);
  85.       LOG_ERROR("file uid = %d", filestat.st_uid);
  86.       LOG_ERROR("file gid = %d", filestat.st_gid);
  87.     } else {
  88.       LOG_ERROR("unable to call stat, error: %s", strerror(errno));
  89.     }
  90.     HandleError(temp_path, &dir_fd, &fp);
  91.     return false;
  92.   }
  93.   // 6. 重命名临时文件
  94.   // Rename written temp file to the actual config file.
  95.   if (std::rename(temp_path.c_str(), path.c_str()) != 0) {
  96.     LOG_ERROR("unable to commit file from '%s' to '%s', error: %s", temp_path.c_str(), path.c_str(), strerror(errno));
  97.     HandleError(temp_path, &dir_fd, &fp);
  98.     return false;
  99.   }
  100.   // 7. 同步目录并关闭目录文件描述符
  101.   // This should ensure the directory is updated as well.
  102.   if (fsync(dir_fd) != 0) {
  103.     LOG_WARN("unable to fsync dir '%s', error: %s", directory_path.c_str(), strerror(errno));
  104.   }
  105.   if (close(dir_fd) != 0) {
  106.     LOG_ERROR("unable to close dir '%s', error: %s", directory_path.c_str(), strerror(errno));
  107.     HandleError(temp_path, &dir_fd, &fp);
  108.     return false;
  109.   }
  110.   return true;
  111. }
复制代码
通过一系列步骤确保数据能安全地写入磁盘,利用临时文件和重命名操作实现原子更新,同时处理了多种大概出现的错误环境,包管了数据写入的可靠性。具体为:

  • 把数据写入临时文件。
  • 把流缓冲区的数据刷新到临时文件。
  • 运用 fsync() 函数将临时文件同步到磁盘。
  • 把临时文件重命名为实际的设置文件,以此实现原子更新。
  • 利用 fsync() 函数同步包罗设置文件的目录,包管目录条目是最新的。
set_encrypt_key_or_remove_key

  1. bool set_encrypt_key_or_remove_key(std::string prefix,
  2.                                    std::string decryptedString) override {
  3.     log::verbose("prefix: {}", prefix);
  4.    
  5.     if (!callbacks) {
  6.       log::warn("callback isn't ready. prefix: {}", prefix);
  7.       return false;
  8.     }
  9.    
  10.     // Save the value into a map.
  11.     key_map[prefix] = decryptedString;
  12.    
  13.     //  在 JNI 线程中调用回调函数
  14.     do_in_jni_thread(base::BindOnce(
  15.         &bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks::
  16.             set_encrypt_key_or_remove_key,
  17.         base::Unretained(callbacks), prefix, decryptedString));
  18.     return true;
  19. }
复制代码
将一个解密后的字符串与特定前缀关联起来,并保存到一个映射中,同时通过 JNI 线程调用回调函数来进一步处理这个关联信息。用于处理密钥的加密存储或移除操作,根据传入的前缀和已解密的字符串举行相应处理。
JNI: set_encrypt_key_or_remove_key

  1. /packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp
  2.   void set_encrypt_key_or_remove_key(
  3.       const std::string prefixString,
  4.       const std::string decryptedString) override {
  5.     log::info("");
  6.     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
  7.     CallbackEnv sCallbackEnv(__func__);
  8.     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
  9.     jstring j_prefixString = sCallbackEnv->NewStringUTF(prefixString.c_str());
  10.     jstring j_decryptedString =
  11.         sCallbackEnv->NewStringUTF(decryptedString.c_str());
  12.     // 调用 Java 层的回调方法
  13.     sCallbackEnv->CallVoidMethod(mCallbacksObj,
  14.                                  method_setEncryptKeyOrRemoveKeyCallback,
  15.                                  j_prefixString, j_decryptedString);
  16.   }
复制代码
将 C++ 层的 prefixString 和 decryptedString 传递到 Java 层的回调方法中。通过 JNI 环境创建对应的 Java 字符串对象,然后调用 Java 层的 setEncryptKeyOrRemoveKeyCallback 方法,完成从 C++ 到 Java 的跨语言交互。
setEncryptKeyOrRemoveKeyCallback

  1. packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
  2.    // Callbacks from the native stack back into the Java framework.
  3.     // All callbacks are routed via the Service which will disambiguate which
  4.     // state machine the message should be routed to.
  5.     private void setEncryptKeyOrRemoveKeyCallback(String prefixString, String decryptedString) {
  6.         final BluetoothKeystoreService service = mBluetoothKeystoreService;
  7.         if (service == null) {
  8.             Log.e(
  9.                     TAG,
  10.                     "setEncryptKeyOrRemoveKeyCallback: Event ignored, service not available: "
  11.                             + prefixString);
  12.             return;
  13.         }
  14.         try {
  15.             service.setEncryptKeyOrRemoveKey(prefixString, decryptedString);
  16.         } catch (InterruptedException e) {
  17.             Log.e(TAG, "Interrupted while operating.");
  18.         } catch (IOException e) {
  19.             Log.e(TAG, "IO error while file operating.");
  20.         } catch (NoSuchAlgorithmException e) {
  21.             Log.e(TAG, "encrypt could not find the algorithm: SHA256");
  22.         }
  23.     }
复制代码
从本地(Native)层回调到 Java 框架层的接口。当本地层调用 set_encrypt_key_or_remove_key 方法并通过 JNI 将信息传递到 Java 层时,会触发这个回调方法。主要功能是将接收到的 prefixString 和 decryptedString 传递给 BluetoothKeystoreService 举行进一步处理。
setEncryptKeyOrRemoveKey

  1. packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java
  2. /**
  3.      * Sets or removes the encryption key value.
  4.      *
  5.      * <p>If the value of decryptedString matches {@link #CONFIG_FILE_HASH} then
  6.      * read the hash file and decrypt the keys and place them into {@link mPendingEncryptKey}
  7.      * otherwise cleanup all data and remove the keys.
  8.      *
  9.      * @param prefixString key to use
  10.      * @param decryptedString string to decrypt
  11.      */
  12.     public void setEncryptKeyOrRemoveKey(String prefixString, String decryptedString)
  13.             throws InterruptedException, IOException, NoSuchAlgorithmException {
  14.         infoLog("setEncryptKeyOrRemoveKey: prefix: " + prefixString);
  15.         if (prefixString == null || decryptedString == null) {
  16.             return;
  17.         }
  18.         if (prefixString.equals(CONFIG_FILE_PREFIX)) {
  19.             if (decryptedString.isEmpty()) {
  20.                 cleanupAll();
  21.             } else if (decryptedString.equals(CONFIG_FILE_HASH)) {
  22.                 readHashFile(CONFIG_FILE_PATH, CONFIG_FILE_PREFIX);
  23.                 mPendingEncryptKey.put(CONFIG_FILE_PREFIX);
  24.                 readHashFile(CONFIG_BACKUP_PATH, CONFIG_BACKUP_PREFIX);
  25.                 mPendingEncryptKey.put(CONFIG_BACKUP_PREFIX);
  26.                 saveEncryptedKey();
  27.             }
  28.             return;
  29.         }
  30.         if (decryptedString.isEmpty()) {
  31.             // clear the item by prefixString.
  32.             mNameDecryptKey.remove(prefixString);
  33.             mNameEncryptKey.remove(prefixString);
  34.         } else {
  35.             mNameDecryptKey.put(prefixString, decryptedString);
  36.             mPendingEncryptKey.put(prefixString);
  37.         }
  38.     }
复制代码
根据传入的 prefixString(键)和 decryptedString(待解密的字符串)来设置或移除加密密钥值。根据不同的条件举行不同的操作,比方清空数据、读取哈希文件、保存加密密钥等
BtifConfigInterface::ConvertEncryptOrDecryptKeyIfNeeded

  1. /packages/modules/Bluetooth/system/main/shim/config.cc
  2. void BtifConfigInterface::ConvertEncryptOrDecryptKeyIfNeeded() {
  3.   GetStorage()->ConvertEncryptOrDecryptKeyIfNeeded();
  4. }
复制代码
StorageModule::ConvertEncryptOrDecryptKeyIfNeeded

  1. void StorageModule::ConvertEncryptOrDecryptKeyIfNeeded() {
  2.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  3.   pimpl_->cache_.ConvertEncryptOrDecryptKeyIfNeeded();
  4. }
复制代码
ConfigCache::ConvertEncryptOrDecryptKeyIfNeeded

  1. void ConfigCache::ConvertEncryptOrDecryptKeyIfNeeded() {
  2.   std::lock_guard<std::recursive_mutex> lock(mutex_);
  3.   LOG_INFO("%s", __func__);
  4.   
  5.   //  获取持久设备的节
  6.   auto persistent_sections = GetPersistentSections();
  7.   // 遍历持久设备的节
  8.   for (const auto& section : persistent_sections) {
  9.     auto section_iter = persistent_devices_.find(section);
  10.     //  遍历需要加密或解密的属性列表
  11.     for (const auto& property : kEncryptKeyNameList) {
  12.       auto property_iter = section_iter->second.find(std::string(property));
  13.       if (property_iter != section_iter->second.end()) {
  14.         // 加密操作
  15.         bool is_encrypted = property_iter->second == kEncryptedStr;
  16.         if ((!property_iter->second.empty()) && os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
  17.             os::ParameterProvider::IsCommonCriteriaMode() && !is_encrypted) {
  18.           // 对属性值进行加密
  19.           if (os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
  20.                   section + "-" + std::string(property), property_iter->second)) {
  21.             // 表示该属性已加密
  22.             SetProperty(section, std::string(property), kEncryptedStr);
  23.           }
  24.         }
  25.         
  26.         //  解密操作
  27.         if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && is_encrypted) {
  28.           // 获取解密后的字符串 value_str
  29.           std::string value_str =
  30.               os::ParameterProvider::GetBtKeystoreInterface()->get_key(section + "-" + std::string(property));
  31.           if (!os::ParameterProvider::IsCommonCriteriaMode()) {
  32.             SetProperty(section, std::string(property), value_str); // 将该属性的值更新为解密后的字符串
  33.           }
  34.         }
  35.       }
  36.     }
  37.   }
  38. }
复制代码
对设置缓存中持久装备部分的指定属性举行加密或解密操作。遍历持久装备的各个节,检查每个节中特定属性的加密状态,并根据体系设置和属性值的环境决定是否举行加密或解密,然后更新设置缓存中的属性值。
总结


  • 初始化流程:处理工厂重置、校验设置文件,优先加载主设置,失败则利用备份或新建空设置。
  • 设置解析:逐行读取设置文件,区分装备节与非装备节,加密敏感属性(如链接密钥),支持动态回调关照变更。
  • 装备范例修复:通过属性特性推断装备范例(经典/低功耗/双模),主动修正设置不一致。
  • 加密管理:在通用准则模式下,对持久化属性加密存储,支持按需解密或密钥移除。
  • 延迟保存机制:合并频仍的设置变更操作,定时批量写入主备份文件,减少I/O开销。
流程图


[code][/code]
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莫张周刘王

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表