/* * Copyright (c) 2021 Huawei Device Co., Ltd. * 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 "preferences_impl.h" #include #include #include #include #include #include #include "base64_helper.h" #include "executor_pool.h" #include "log_print.h" #include "preferences_errno.h" #include "preferences_observer_stub.h" #include "preferences_xml_utils.h" #include "securec.h" namespace OHOS { namespace NativePreferences { template std::string GetTypeName() { return "unknown"; } template<> std::string GetTypeName() { return "int"; } template<> std::string GetTypeName() { return "bool"; } template<> std::string GetTypeName() { return "long"; } template<> std::string GetTypeName() { return "float"; } template<> std::string GetTypeName() { return "double"; } template<> std::string GetTypeName() { return "string"; } template<> std::string GetTypeName>() { return "stringArray"; } template<> std::string GetTypeName>() { return "doubleArray"; } template<> std::string GetTypeName>() { return "boolArray"; } template<> std::string GetTypeName>() { return "uint8Array"; } ExecutorPool PreferencesImpl::executorPool_ = ExecutorPool(1, 0); PreferencesImpl::PreferencesImpl(const Options &options) : loaded_(false), options_(options) { currentMemoryStateGeneration_ = 0; diskStateGeneration_ = 0; } std::string PreferencesImpl::MakeFilePath(const std::string &prefPath, const std::string &suffix) { std::string filePath = prefPath; filePath += suffix; return filePath; } PreferencesImpl::~PreferencesImpl() { } int PreferencesImpl::Init() { if (!StartLoadFromDisk()) { return E_ERROR; } return E_OK; } bool PreferencesImpl::StartLoadFromDisk() { { std::lock_guard lock(mutex_); loaded_ = false; } ExecutorPool::Task task = std::bind(PreferencesImpl::LoadFromDisk, shared_from_this()); return (executorPool_.Execute(std::move(task)) == ExecutorPool::INVALID_TASK_ID) ? false : true; } int PreferencesImpl::CheckKey(const std::string &key) { if (key.empty()) { LOG_ERROR("The key string is null or empty."); return E_KEY_EMPTY; } if (Preferences::MAX_KEY_LENGTH < key.length()) { LOG_ERROR("The key string length should shorter than 80."); return E_KEY_EXCEED_MAX_LENGTH; } return E_OK; } /* static */ void PreferencesImpl::LoadFromDisk(std::shared_ptr pref) { std::lock_guard lock(pref->mutex_); if (pref->loaded_) { return; } pref->ReadSettingXml(pref); pref->loaded_ = true; pref->cond_.notify_all(); } void PreferencesImpl::AwaitLoadFile() { std::unique_lock lock(mutex_); if (!loaded_) { cond_.wait(lock, [this] { return loaded_; }); } } void PreferencesImpl::WriteToDiskFile(std::shared_ptr pref, std::shared_ptr mcr) { std::unique_lock lock(pref->mutex_); if (!pref->CheckRequestValidForStateGeneration(mcr)) { mcr->SetDiskWriteResult(true, E_OK); return; } if (pref->WriteSettingXml(pref, mcr->writeToDiskMap_)) { pref->diskStateGeneration_ = mcr->memoryStateGeneration_; mcr->SetDiskWriteResult(true, E_OK); } else { mcr->SetDiskWriteResult(false, E_ERROR); } } bool PreferencesImpl::CheckRequestValidForStateGeneration(std::shared_ptr mcr) { if (diskStateGeneration_ >= mcr->memoryStateGeneration_) { LOG_DEBUG("DiskStateGeneration should be less than memoryStateGeneration."); return false; } if (mcr->isSyncRequest_ || currentMemoryStateGeneration_ == mcr->memoryStateGeneration_) { return true; } return false; } PreferencesValue PreferencesImpl::Get(const std::string &key, const PreferencesValue &defValue) { if (CheckKey(key) != E_OK) { return defValue; } AwaitLoadFile(); std::lock_guard lock(mutex_); auto iter = map_.find(key); if (iter != map_.end()) { return iter->second; } return defValue; } std::map PreferencesImpl::GetAll() { AwaitLoadFile(); return map_; } template static void Convert2PrefValue(const Element &element, T &value) { if constexpr (std::is_same::value) { value = (element.value_.compare("true") == 0) ? true : false; } else if constexpr (std::is_same::value) { value = element.value_; } else { std::stringstream ss; ss << element.value_; ss >> value; } } template static void Convert2PrefValue(const Element &element, std::vector &values) { for (const auto &child : element.children_) { T value; Convert2PrefValue(child, value); values.push_back(value); } } template bool GetPrefValue(const Element &element, T &value) { LOG_WARN("unknown element type. the key is %{public}s", element.key_.c_str()); return false; } static void Convert2PrefValue(const Element &element, std::vector &value) { if (!Base64Helper::Decode(element.value_, value)) { value.clear(); } } template bool GetPrefValue(const Element &element, T &value) { if (element.tag_ == GetTypeName()) { First val; Convert2PrefValue(element, val); value = val; return true; } return GetPrefValue(element, value); } template bool Convert2PrefValue(const Element &element, std::variant &value) { return GetPrefValue(element, value); } void ReadXmlElement(const Element &element, std::map &prefMap) { PreferencesValue value(static_cast(0)); if (Convert2PrefValue(element, value.value_)) { prefMap.insert(std::make_pair(element.key_, value)); } } bool PreferencesImpl::ReadSettingXml(std::shared_ptr pref) { std::vector settings; if (!PreferencesXmlUtils::ReadSettingXml(pref->options_.filePath, pref->options_.dataGroupId, settings)) { return false; } for (const auto &element : settings) { ReadXmlElement(element, pref->map_); } return true; } template void Convert2Element(Element &elem, const T &value) { elem.tag_ = GetTypeName(); if constexpr (std::is_same::value) { elem.value_ = ((bool)value) ? "true" : "false"; } else if constexpr (std::is_same::value) { elem.value_ = value; } else { elem.value_ = std::to_string(value); } } template void Convert2Element(Element &elem, const std::vector &value) { elem.tag_ = GetTypeName>(); for (const T &val : value) { Element element; Convert2Element(element, val); elem.children_.push_back(element); } } void Convert2Element(Element &elem, const std::vector &value) { elem.tag_ = GetTypeName>(); elem.value_ = Base64Helper::Encode(value); } template void GetElement(Element &elem, const T &value) { LOG_WARN("unknown element type. the key is %{public}s", elem.key_.c_str()); } template void GetElement(Element &elem, const T &value) { auto *val = std::get_if(&value); if (val != nullptr) { return Convert2Element(elem, *val); } return GetElement(elem, value); } template void Convert2Element(Element &elem, const std::variant &value) { return GetElement(elem, value); } void WriteXmlElement(Element &elem, const PreferencesValue &value) { Convert2Element(elem, value.value_); } bool PreferencesImpl::WriteSettingXml( std::shared_ptr pref, const std::map &prefMap) { std::vector settings; for (auto it = prefMap.begin(); it != prefMap.end(); it++) { Element elem; elem.key_ = it->first; PreferencesValue value = it->second; WriteXmlElement(elem, value); settings.push_back(elem); } return PreferencesXmlUtils::WriteSettingXml(pref->options_.filePath, pref->options_.dataGroupId, settings); } bool PreferencesImpl::HasKey(const std::string &key) { if (CheckKey(key) != E_OK) { return false; } AwaitLoadFile(); std::lock_guard lock(mutex_); return (map_.find(key) != map_.end()); } int PreferencesImpl::RegisterObserver(std::shared_ptr preferencesObserver, RegisterMode mode) { std::lock_guard lock(mutex_); if (mode == RegisterMode::LOCAL_CHANGE) { std::weak_ptr weakPreferencesObserver = preferencesObserver; localObservers_.push_back(weakPreferencesObserver); } else { auto dataObsMgrClient = DataObsMgrClient::GetInstance(); if (dataObsMgrClient == nullptr) { return E_GET_DATAOBSMGRCLIENT_FAIL; } sptr observer(new (std::nothrow) DataPreferencesObserverStub(preferencesObserver)); int errcode = dataObsMgrClient->RegisterObserver(MakeUri(), observer); if (errcode != 0) { LOG_ERROR("RegisterObserver multiProcessChange failed, errCode %{public}d", errcode); return errcode; } multiProcessObservers_.push_back(observer); } return E_OK; } int PreferencesImpl::UnRegisterObserver(std::shared_ptr preferencesObserver, RegisterMode mode) { std::lock_guard lock(mutex_); if (mode == RegisterMode::LOCAL_CHANGE) { for (auto it = localObservers_.begin(); it != localObservers_.end(); ++it) { std::weak_ptr weakPreferencesObserver = *it; std::shared_ptr sharedObserver = weakPreferencesObserver.lock(); if (!sharedObserver || sharedObserver == preferencesObserver) { localObservers_.erase(it); break; } } return E_OK; } for (auto it = multiProcessObservers_.begin(); it != multiProcessObservers_.end(); ++it) { std::shared_ptr sharedObserver = (*it)->preferencesObserver_.lock(); if (!sharedObserver || sharedObserver == preferencesObserver) { auto dataObsMgrClient = DataObsMgrClient::GetInstance(); if (dataObsMgrClient == nullptr) { return E_GET_DATAOBSMGRCLIENT_FAIL; } int errcode = dataObsMgrClient->UnregisterObserver(MakeUri(), *it); if (errcode != 0) { LOG_ERROR("RegisterObserver multiProcessChange failed, errCode %{public}d", errcode); return errcode; } multiProcessObservers_.erase(it); break; } } return E_OK; } int PreferencesImpl::Put(const std::string &key, const PreferencesValue &value) { int errCode = CheckKey(key); if (errCode != E_OK) { return errCode; } if (value.IsString()) { errCode = CheckStringValue(value); if (errCode != E_OK) { LOG_ERROR("PreferencesImpl::Put string value length should shorter than 8*1024"); return errCode; } } AwaitLoadFile(); std::lock_guard lock(mutex_); auto iter = map_.find(key); if (iter != map_.end()) { PreferencesValue &val = iter->second; if (val == value) { return E_OK; } } map_.insert_or_assign(key, value); modifiedKeys_.push_back(key); return E_OK; } int PreferencesImpl::CheckStringValue(const std::string &value) { if (Preferences::MAX_VALUE_LENGTH < value.length()) { LOG_ERROR("The value string length should shorter than 8 * 1024."); return E_VALUE_EXCEED_MAX_LENGTH; } return E_OK; } Uri PreferencesImpl::MakeUri(const std::string &key) { std::string uriStr; if (options_.dataGroupId.empty()) { uriStr = STR_SCHEME + options_.bundleName + STR_SLASH + options_.filePath; } else { uriStr = STR_SCHEME + options_.dataGroupId + STR_SLASH + options_.filePath; } if (!key.empty()) { uriStr = uriStr + STR_QUERY + key; } return Uri(uriStr); } int PreferencesImpl::Delete(const std::string &key) { int errCode = CheckKey(key); if (errCode != E_OK) { return errCode; } std::lock_guard lock(mutex_); auto pos = map_.find(key); if (pos != map_.end()) { map_.erase(pos); modifiedKeys_.push_back(key); } return E_OK; } int PreferencesImpl::Clear() { std::lock_guard lock(mutex_); if (!map_.empty()) { for (auto &kv : map_) { modifiedKeys_.push_back(kv.first); } map_.clear(); } return E_OK; } void PreferencesImpl::Flush() { std::shared_ptr request = commitToMemory(); request->isSyncRequest_ = false; ExecutorPool::Task task = std::bind(PreferencesImpl::WriteToDiskFile, shared_from_this(), request); executorPool_.Execute(std::move(task)); notifyPreferencesObserver(*request); } int PreferencesImpl::FlushSync() { std::shared_ptr request = commitToMemory(); request->isSyncRequest_ = true; PreferencesImpl::WriteToDiskFile(shared_from_this(), request); if (request->wasWritten_) { LOG_DEBUG("Successfully written to disk file, memory state generation is %{public}" PRId64 "", request->memoryStateGeneration_); } notifyPreferencesObserver(*request); return request->writeToDiskResult_; } std::shared_ptr PreferencesImpl::commitToMemory() { std::lock_guard lock(mutex_); int64_t memoryStateGeneration = -1; std::list keysModified; std::vector> preferencesObservers; std::map writeToDiskMap; writeToDiskMap = map_; if (!modifiedKeys_.empty()) { currentMemoryStateGeneration_++; keysModified = modifiedKeys_; modifiedKeys_.clear(); } memoryStateGeneration = currentMemoryStateGeneration_; preferencesObservers = localObservers_; return std::make_shared( writeToDiskMap, keysModified, preferencesObservers, memoryStateGeneration); } void PreferencesImpl::notifyPreferencesObserver(const PreferencesImpl::MemoryToDiskRequest &request) { if (request.keysModified_.empty()) { return; } auto dataObsMgrClient = DataObsMgrClient::GetInstance(); for (auto key = request.keysModified_.begin(); key != request.keysModified_.end(); ++key) { for (auto it = request.localObservers_.begin(); it != request.localObservers_.end(); ++it) { std::weak_ptr weakPreferencesObserver = *it; if (std::shared_ptr sharedPreferencesObserver = weakPreferencesObserver.lock()) { sharedPreferencesObserver->OnChange(*key); } } if (dataObsMgrClient == nullptr) { continue; } dataObsMgrClient->NotifyChange(MakeUri(*key)); } } PreferencesImpl::MemoryToDiskRequest::MemoryToDiskRequest( const std::map &writeToDiskMap, const std::list &keysModified, const std::vector> preferencesObservers, int64_t memStataGeneration) { writeToDiskMap_ = writeToDiskMap; keysModified_ = keysModified; localObservers_ = preferencesObservers; memoryStateGeneration_ = memStataGeneration; isSyncRequest_ = false; wasWritten_ = false; writeToDiskResult_ = E_ERROR; } void PreferencesImpl::MemoryToDiskRequest::SetDiskWriteResult(bool wasWritten, int result) { writeToDiskResult_ = result; wasWritten_ = wasWritten; reqCond_.notify_one(); } } // End of namespace NativePreferences } // End of namespace OHOS