/* * Copyright (c) 2024 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_enhance_impl.h" #include #include #include #include #include #include #include #include "executor_pool.h" #include "preferences_file_operation.h" #include "log_print.h" #include "preferences_observer_stub.h" #include "preferences_value.h" #include "preferences_value_parcel.h" namespace OHOS { namespace NativePreferences { constexpr int32_t CACHED_THRESHOLDS = 512 * 1024; // we will cached big obj(len >= 512k) PreferencesEnhanceImpl::PreferencesEnhanceImpl(const Options &options): PreferencesBase(options) { } PreferencesEnhanceImpl::~PreferencesEnhanceImpl() { std::unique_lock writeLock(dbMutex_); db_ = nullptr; } int PreferencesEnhanceImpl::Init() { std::unique_lock writeLock(dbMutex_); db_ = std::make_shared(); cachedDataVersion_ = 0; int errCode = db_->Init(options_.filePath, options_.bundleName); if (errCode != E_OK) { db_ = nullptr; } return errCode; } PreferencesValue PreferencesEnhanceImpl::Get(const std::string &key, const PreferencesValue &defValue) { if (CheckKey(key) != E_OK) { return defValue; } // write lock here, get not support concurrence std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:Get failed, db has been closed."); return defValue; } int64_t kernelDataVersion = 0; if (db_->GetKernelDataVersion(kernelDataVersion) != E_OK) { LOG_ERROR("PreferencesEnhanceImpl:Get failed, get kernel data version failed."); return defValue; } if (kernelDataVersion == cachedDataVersion_) { auto it = largeCachedData_.find(key); if (it != largeCachedData_.end()) { return it->second; } } std::vector oriKey(key.begin(), key.end()); std::vector oriValue; int errCode = db_->Get(oriKey, oriValue); if (errCode != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", errCode); return defValue; } auto item = PreferencesValueParcel::UnmarshallingPreferenceValue(oriValue); if (item.first != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", item.first); return defValue; } if (oriValue.size() >= CACHED_THRESHOLDS) { largeCachedData_.insert_or_assign(key, item.second); cachedDataVersion_ = kernelDataVersion; } return item.second; } bool PreferencesEnhanceImpl::HasKey(const std::string &key) { if (CheckKey(key) != E_OK) { return false; } std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:HasKey failed, db has been closed."); return false; } int64_t kernelDataVersion = 0; if (db_->GetKernelDataVersion(kernelDataVersion) != E_OK) { LOG_ERROR("PreferencesEnhanceImpl:HasKey failed, get kernel data version failed."); return false; } if (kernelDataVersion == cachedDataVersion_) { auto it = largeCachedData_.find(key); if (it != largeCachedData_.end()) { return true; } } std::vector oriKey(key.begin(), key.end()); std::vector oriValue; int errCode = db_->Get(oriKey, oriValue); if (errCode != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", errCode); return false; } if (oriValue.size() >= CACHED_THRESHOLDS) { auto item = PreferencesValueParcel::UnmarshallingPreferenceValue(oriValue); if (item.first != E_OK) { LOG_WARN("haskey unmarshall failed, haskey return true, errCode=%{public}d", item.first); return true; } largeCachedData_.insert_or_assign(key, item.second); cachedDataVersion_ = kernelDataVersion; } return true; } int PreferencesEnhanceImpl::Put(const std::string &key, const PreferencesValue &value) { int errCode = CheckKey(key); if (errCode != E_OK) { return errCode; } errCode = CheckValue(value); if (errCode != E_OK) { return errCode; } std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:Put failed, db has been closed."); return E_ERROR; } std::vector oriValue; uint32_t oriValueLen = PreferencesValueParcel::CalSize(value); oriValue.resize(oriValueLen); errCode = PreferencesValueParcel::MarshallingPreferenceValue(value, oriValue); if (errCode != E_OK) { LOG_ERROR("marshalling value failed, errCode=%{public}d", errCode); return errCode; } std::vector oriKey(key.begin(), key.end()); errCode = db_->Put(oriKey, oriValue); if (errCode != E_OK) { LOG_ERROR("put data failed, errCode=%{public}d", errCode); return errCode; } // update cached and version if (oriValueLen >= CACHED_THRESHOLDS) { largeCachedData_.insert_or_assign(key, value); cachedDataVersion_ = cachedDataVersion_ == INT64_MAX ? 0 : cachedDataVersion_ + 1; } else { auto pos = largeCachedData_.find(key); if (pos != largeCachedData_.end()) { largeCachedData_.erase(pos); } } ExecutorPool::Task task = [pref = shared_from_this(), key, value] { PreferencesEnhanceImpl::NotifyPreferencesObserver(pref, key, value); }; executorPool_.Execute(std::move(task)); return E_OK; } int PreferencesEnhanceImpl::Delete(const std::string &key) { int errCode = CheckKey(key); if (errCode != E_OK) { return errCode; } std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:Delete failed, db has been closed."); return E_ERROR; } std::vector oriKey(key.begin(), key.end()); errCode = db_->Delete(oriKey); if (errCode != E_OK) { LOG_ERROR("delete data failed, errCode=%{public}d", errCode); return errCode; } // update cached and version auto it = largeCachedData_.find(key); if (it != largeCachedData_.end()) { largeCachedData_.erase(it); cachedDataVersion_ = cachedDataVersion_ == INT64_MAX ? 0 : cachedDataVersion_ + 1; } PreferencesValue value; ExecutorPool::Task task = [pref = shared_from_this(), key, value] { PreferencesEnhanceImpl::NotifyPreferencesObserver(pref, key, value); }; executorPool_.Execute(std::move(task)); return E_OK; } std::pair> PreferencesEnhanceImpl::GetAllInner() { std::map map; if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:GetAll failed, db has been closed."); return std::make_pair(E_ALREADY_CLOSED, map); } int64_t kernelDataVersion = 0; if (db_->GetKernelDataVersion(kernelDataVersion) != E_OK) { LOG_ERROR("PreferencesEnhanceImpl:GetAll failed, get kernel data version failed."); return std::make_pair(E_ERROR, map); } std::map result; std::list, std::vector>> data; int errCode = db_->GetAll(data); if (errCode != E_OK) { LOG_ERROR("get all failed, errCode=%{public}d", errCode); return std::make_pair(errCode, map); } for (auto it = data.begin(); it != data.end(); it++) { std::string key(it->first.begin(), it->first.end()); auto item = PreferencesValueParcel::UnmarshallingPreferenceValue(it->second); result.insert({key, item.second}); if (item.first != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", errCode); return std::make_pair(item.first, map); } if (it->second.size() >= CACHED_THRESHOLDS) { largeCachedData_.insert_or_assign(key, item.second); } } cachedDataVersion_ = kernelDataVersion; return std::make_pair(E_OK, result); } std::map PreferencesEnhanceImpl::GetAll() { std::unique_lock writeLock(dbMutex_); std::pair> res = GetAllInner(); return res.second; } void PreferencesEnhanceImpl::NotifyPreferencesObserver(std::shared_ptr pref, const std::string &key, const PreferencesValue &value) { std::shared_lock readLock(pref->obseverMetux_); LOG_DEBUG("notify observer size:%{public}zu", pref->dataObserversMap_.size()); for (const auto &[weakPrt, keys] : pref->dataObserversMap_) { auto itKey = keys.find(key); if (itKey == keys.end()) { continue; } std::map records = {{key, value}}; if (std::shared_ptr sharedPtr = weakPrt.lock()) { LOG_DEBUG("dataChange observer call, resultSize:%{public}zu", records.size()); sharedPtr->OnChange(records); } } auto dataObsMgrClient = DataObsMgrClient::GetInstance(); for (auto it = pref->localObservers_.begin(); it != pref->localObservers_.end(); ++it) { std::weak_ptr weakPreferencesObserver = *it; if (std::shared_ptr sharedPreferencesObserver = weakPreferencesObserver.lock()) { sharedPreferencesObserver->OnChange(key); } } if (dataObsMgrClient != nullptr) { dataObsMgrClient->NotifyChange(pref->MakeUri(key)); } } void PreferencesEnhanceImpl::NotifyPreferencesObserverBatchKeys(std::shared_ptr pref, const std::map &data) { for (const auto &[key, value] : data) { NotifyPreferencesObserver(pref, key, value); } } int PreferencesEnhanceImpl::Clear() { std::unique_lock writeLock(dbMutex_); LOG_INFO("Clear called, file: %{public}s", ExtractFileName(options_.filePath).c_str()); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:Clear failed, db has been closed."); return E_ERROR; } std::pair> res = GetAllInner(); if (res.first != E_OK) { LOG_ERROR("get all failed when clear, errCode=%{public}d", res.first); return res.first; } std::map allData = res.second; int errCode = db_->DropCollection(); if (errCode != E_OK) { LOG_ERROR("drop collection failed when clear, errCode=%{public}d", errCode); return errCode; } if (!allData.empty()) { ExecutorPool::Task task = [pref = shared_from_this(), allData] { PreferencesEnhanceImpl::NotifyPreferencesObserverBatchKeys(pref, allData); }; executorPool_.Execute(std::move(task)); } errCode = db_->CreateCollection(); if (errCode != E_OK) { LOG_ERROR("create collection failed when clear, errCode=%{public}d", errCode); return errCode; } largeCachedData_.clear(); cachedDataVersion_ = cachedDataVersion_ == INT64_MAX ? 0 : cachedDataVersion_ + 1; return E_OK; } int PreferencesEnhanceImpl::CloseDb() { std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_WARN("PreferencesEnhanceImpl:CloseDb failed, db has been closed, no need to close again."); return E_OK; } int errCode = db_->CloseDb(); if (errCode != E_OK) { LOG_ERROR("PreferencesEnhanceImpl:CloseDb failed."); return errCode; } largeCachedData_.clear(); db_ = nullptr; return E_OK; } std::pair PreferencesEnhanceImpl::GetValue(const std::string &key, const PreferencesValue &defValue) { int errCode = CheckKey(key); if (errCode != E_OK) { return std::make_pair(errCode, defValue); } // write lock here, get not support concurrence std::unique_lock writeLock(dbMutex_); if (db_ == nullptr) { LOG_ERROR("PreferencesEnhanceImpl:Get failed, db has been closed."); return std::make_pair(E_ALREADY_CLOSED, defValue); } int64_t kernelDataVersion = 0; if (db_->GetKernelDataVersion(kernelDataVersion) != E_OK) { LOG_ERROR("PreferencesEnhanceImpl:Get failed, get kernel data version failed."); return std::make_pair(E_ERROR, defValue); } if (kernelDataVersion == cachedDataVersion_) { auto it = largeCachedData_.find(key); if (it != largeCachedData_.end()) { return std::make_pair(E_OK, it->second); } } std::vector oriKey(key.begin(), key.end()); std::vector oriValue; errCode = db_->Get(oriKey, oriValue); if (errCode != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", errCode); return std::make_pair(errCode, defValue); } auto item = PreferencesValueParcel::UnmarshallingPreferenceValue(oriValue); if (item.first != E_OK) { LOG_ERROR("get key failed, errCode=%{public}d", item.first); return std::make_pair(item.first, defValue); } if (oriValue.size() >= CACHED_THRESHOLDS) { largeCachedData_.insert_or_assign(key, item.second); cachedDataVersion_ = kernelDataVersion; } return std::make_pair(E_OK, item.second); } std::pair> PreferencesEnhanceImpl::GetAllData() { std::unique_lock writeLock(dbMutex_); return GetAllInner(); } } // End of namespace NativePreferences } // End of namespace OHOS