1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "preferences_impl.h"
17
18 #include <cinttypes>
19 #include <climits>
20 #include <cstdint>
21 #include <cstdlib>
22 #include <functional>
23 #include <thread>
24 #include <chrono>
25 #include <cinttypes>
26
27 #include "executor_pool.h"
28 #include "log_print.h"
29 #include "preferences_observer_stub.h"
30 #include "preferences_xml_utils.h"
31 #include "preferences_file_operation.h"
32 #include "preferences_anonymous.h"
33 #include "preferences_dfx_adapter.h"
34 #include "preferences_utils.h"
35
36 namespace OHOS {
37 namespace NativePreferences {
38
39 using namespace std::chrono;
40
41 constexpr int32_t WAIT_TIME = 2;
42 constexpr int32_t TASK_EXEC_TIME = 100;
43 constexpr int32_t LOAD_XML_LOG_TIME = 1000;
PreferencesImpl(const Options & options)44 PreferencesImpl::PreferencesImpl(const Options &options) : PreferencesBase(options)
45 {
46 loaded_.store(false);
47 isNeverUnlock_ = false;
48 loadResult_= false;
49 queue_ = std::make_shared<SafeBlockQueue<uint64_t>>(1);
50 isActive_.store(true);
51 isCleared_.store(false);
52 }
53
~PreferencesImpl()54 PreferencesImpl::~PreferencesImpl()
55 {
56 }
57
Init()58 int PreferencesImpl::Init()
59 {
60 if (!StartLoadFromDisk()) {
61 return E_ERROR;
62 }
63 return E_OK;
64 }
65
StartLoadFromDisk()66 bool PreferencesImpl::StartLoadFromDisk()
67 {
68 std::lock_guard<std::mutex> lock(mutex_);
69 loaded_.store(false);
70 isNeverUnlock_ = false;
71 loadResult_ = false;
72
73 ExecutorPool::Task task = [pref = shared_from_this()] { PreferencesImpl::LoadFromDisk(pref); };
74 return (executorPool_.Execute(std::move(task)) == ExecutorPool::INVALID_TASK_ID) ? false : true;
75 }
76
77 /* static */
LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)78 void PreferencesImpl::LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)
79 {
80 if (pref->loaded_.load()) {
81 return;
82 }
83 std::lock_guard<std::mutex> lock(pref->mutex_);
84 if (!pref->loaded_.load()) {
85 std::string::size_type pos = pref->options_.filePath.find_last_of('/');
86 std::string filePath = pref->options_.filePath.substr(0, pos);
87 if (Access(filePath) != 0) {
88 pref->isNeverUnlock_ = true;
89 }
90 std::unordered_map<std::string, PreferencesValue> values;
91 bool loadResult = pref->ReadSettingXml(values);
92 if (!loadResult) {
93 LOG_WARN("The settingXml %{public}s load failed.", ExtractFileName(pref->options_.filePath).c_str());
94 } else {
95 std::unique_lock<decltype(pref->cacheMutex_)> lock(pref->cacheMutex_);
96 pref->valuesCache_ = std::move(values);
97 pref->loadResult_ = true;
98 pref->isNeverUnlock_ = false;
99 }
100 pref->loaded_.store(true);
101 pref->cond_.notify_all();
102 }
103 }
104
ReloadFromDisk()105 bool PreferencesImpl::ReloadFromDisk()
106 {
107 if (loadResult_) {
108 return false;
109 }
110
111 std::unique_lock<decltype(cacheMutex_)> lock(cacheMutex_);
112 std::unordered_map<std::string, PreferencesValue> values = valuesCache_;
113 bool loadResult = ReadSettingXml(values);
114 LOG_WARN("The settingXml %{public}s reload result is %{public}d",
115 ExtractFileName(options_.filePath).c_str(), loadResult);
116 if (loadResult) {
117 valuesCache_ = std::move(values);
118 isNeverUnlock_ = false;
119 loadResult_ = true;
120 return true;
121 }
122 return false;
123 }
124
PreLoad()125 bool PreferencesImpl::PreLoad()
126 {
127 std::lock_guard<std::mutex> lock(mutex_);
128 if (!loaded_.load()) {
129 return true;
130 }
131 if (isNeverUnlock_ || (!isNeverUnlock_ && !loadResult_)) {
132 if (Access(options_.filePath) == 0) {
133 return ReloadFromDisk();
134 }
135 }
136 return true;
137 }
138
AwaitLoadFile()139 void PreferencesImpl::AwaitLoadFile()
140 {
141 if (loaded_.load()) {
142 PreLoad();
143 return;
144 }
145 std::unique_lock<std::mutex> lock(mutex_);
146 if (!loaded_.load()) {
147 cond_.wait_for(lock, std::chrono::seconds(WAIT_TIME), [this] { return loaded_.load(); });
148 }
149
150 if (!loaded_.load()) {
151 LOG_ERROR("The settingXml %{public}s load timeout.", ExtractFileName(options_.filePath).c_str());
152 }
153 }
154
Get(const std::string & key,const PreferencesValue & defValue)155 PreferencesValue PreferencesImpl::Get(const std::string &key, const PreferencesValue &defValue)
156 {
157 if (CheckKey(key) != E_OK) {
158 return defValue;
159 }
160
161 AwaitLoadFile();
162 IsClose(std::string(__FUNCTION__));
163
164 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
165 if (!isCleared_.load()) {
166 auto iter = valuesCache_.find(key);
167 if (iter != valuesCache_.end()) {
168 return iter->second;
169 }
170 }
171 return defValue;
172 }
173
GetAll()174 std::map<std::string, PreferencesValue> PreferencesImpl::GetAll()
175 {
176 AwaitLoadFile();
177 IsClose(std::string(__FUNCTION__));
178 std::map<std::string, PreferencesValue> allDatas;
179 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
180 if (!isCleared_.load()) {
181 for (auto &it : valuesCache_) {
182 allDatas.insert_or_assign(it.first, it.second);
183 }
184 }
185 return allDatas;
186 }
187
GetFileSize(const std::string & path)188 static int64_t GetFileSize(const std::string &path)
189 {
190 int64_t fileSize = -1;
191 struct stat buffer;
192 if (stat(path.c_str(), &buffer) == 0) {
193 fileSize = static_cast<int64_t>(buffer.st_size);
194 }
195 return fileSize;
196 }
197
ReadSettingXml(std::unordered_map<std::string,PreferencesValue> & conMap)198 bool PreferencesImpl::ReadSettingXml(std::unordered_map<std::string, PreferencesValue> &conMap)
199 {
200 auto begin = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
201 if (!PreferencesXmlUtils::ReadSettingXml(options_.filePath, options_.bundleName, conMap)) {
202 return false;
203 }
204 auto end = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
205 if (end - begin > LOAD_XML_LOG_TIME) {
206 LOG_ERROR("The settingXml %{public}s load time exceed 1s, file size:%{public}" PRId64 ".",
207 ExtractFileName(options_.filePath).c_str(), GetFileSize(options_.filePath));
208 }
209 return true;
210 }
211
Close()212 int PreferencesImpl::Close()
213 {
214 isActive_.store(false);
215 return E_OK;
216 }
217
IsClose(const std::string & name)218 bool PreferencesImpl::IsClose(const std::string &name)
219 {
220 if (isActive_.load()) {
221 return false;
222 }
223
224 LOG_WARN("file %{public}s is inactive.", ExtractFileName(options_.filePath).c_str());
225 std::string operationMsg = " use after close.";
226 ReportFaultParam reportParam = { "inactive object", options_.bundleName, NORMAL_DB,
227 ExtractFileName(options_.filePath), E_OBJECT_NOT_ACTIVE, name + operationMsg };
228 PreferencesDfxManager::ReportAbnormalOperation(reportParam, ReportedFaultBitMap::OBJECT_IS_NOT_ACTIVE);
229 return true;
230 }
231
HasKey(const std::string & key)232 bool PreferencesImpl::HasKey(const std::string &key)
233 {
234 if (CheckKey(key) != E_OK) {
235 return false;
236 }
237
238 AwaitLoadFile();
239 IsClose(std::string(__FUNCTION__));
240 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
241 if (isCleared_.load()) {
242 return false;
243 }
244 return valuesCache_.find(key) != valuesCache_.end();
245 }
246
Put(const std::string & key,const PreferencesValue & value)247 int PreferencesImpl::Put(const std::string &key, const PreferencesValue &value)
248 {
249 int errCode = CheckKey(key);
250 if (errCode != E_OK) {
251 return errCode;
252 }
253 errCode = CheckValue(value);
254 if (errCode != E_OK) {
255 return errCode;
256 }
257 AwaitLoadFile();
258 IsClose(std::string(__FUNCTION__));
259
260 std::unique_lock<decltype(cacheMutex_)> lock(cacheMutex_);
261 if (isCleared_.load()) { // has cleared.
262 for (auto &it : valuesCache_) {
263 modifiedKeys_.emplace(it.first);
264 }
265 valuesCache_.clear();
266 valuesCache_.insert_or_assign(key, value);
267 modifiedKeys_.emplace(key);
268 isCleared_.store(false);
269 } else {
270 auto iter = valuesCache_.find(key);
271 if (iter != valuesCache_.end()) {
272 PreferencesValue &val = iter->second;
273 if (val == value) {
274 return E_OK;
275 }
276 }
277 valuesCache_.insert_or_assign(key, value);
278 modifiedKeys_.emplace(key);
279 }
280 return E_OK;
281 }
282
Delete(const std::string & key)283 int PreferencesImpl::Delete(const std::string &key)
284 {
285 int errCode = CheckKey(key);
286 if (errCode != E_OK) {
287 return errCode;
288 }
289 AwaitLoadFile();
290 IsClose(std::string(__FUNCTION__));
291 std::unique_lock<decltype(cacheMutex_)> lock(cacheMutex_);
292 if (isCleared_.load()) {
293 return E_OK;
294 }
295 if (valuesCache_.find(key) != valuesCache_.end()) {
296 valuesCache_.erase(key);
297 modifiedKeys_.emplace(key);
298 }
299 return E_OK;
300 }
301
Clear()302 int PreferencesImpl::Clear()
303 {
304 AwaitLoadFile();
305 IsClose(std::string(__FUNCTION__));
306 isCleared_.store(true);
307 return E_OK;
308 }
309
WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref)310 int PreferencesImpl::WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref)
311 {
312 auto keysModified = std::make_shared<std::unordered_set<std::string>>();
313 auto writeToDiskMap = std::make_shared<std::unordered_map<std::string, PreferencesValue>>();
314 {
315 std::unique_lock<decltype(pref->cacheMutex_)> lock(pref->cacheMutex_);
316 if (pref->isCleared_.load()) {
317 for (auto &it : pref->valuesCache_) {
318 pref->modifiedKeys_.emplace(it.first);
319 }
320 pref->valuesCache_.clear();
321 pref->isCleared_.store(false);
322 }
323 if (!pref->modifiedKeys_.empty()) {
324 *keysModified = std::move(pref->modifiedKeys_);
325 *writeToDiskMap = pref->valuesCache_;
326 } else {
327 // Cache has not changed, Not need to write persistent files.
328 LOG_INFO("No data to update persistent file");
329 return E_OK;
330 }
331 }
332 if (!PreferencesXmlUtils::WriteSettingXml(pref->options_.filePath, pref->options_.bundleName, *writeToDiskMap)) {
333 return E_ERROR;
334 }
335 if (pref->isNeverUnlock_) {
336 pref->isNeverUnlock_ = false;
337 }
338 if (!pref->loadResult_) {
339 pref->loadResult_ = true;
340 }
341
342 NotifyPreferencesObserver(pref, keysModified, writeToDiskMap);
343 return E_OK;
344 }
345
Flush()346 void PreferencesImpl::Flush()
347 {
348 IsClose(std::string(__FUNCTION__));
349 auto success = queue_->PushNoWait(1);
350 if (!success) {
351 return;
352 }
353 std::weak_ptr<SafeBlockQueue<uint64_t>> queue = queue_;
354 ExecutorPool::Task task = [queue, self = weak_from_this()] {
355 auto realQueue = queue.lock();
356 auto realThis = self.lock();
357 if (realQueue == nullptr || realThis == nullptr) {
358 return;
359 }
360 uint64_t value = 0;
361 if (!realThis->PreLoad()) {
362 return;
363 }
364 std::lock_guard<std::mutex> lock(realThis->mutex_);
365 auto has = realQueue->PopNotWait(value);
366 if (has && value == 1) {
367 PreferencesImpl::WriteToDiskFile(realThis);
368 }
369 };
370 executorPool_.Schedule(std::chrono::milliseconds(TASK_EXEC_TIME), std::move(task));
371 }
372
FlushSync()373 int PreferencesImpl::FlushSync()
374 {
375 IsClose(std::string(__FUNCTION__));
376 auto success = queue_->PushNoWait(1);
377 if (success) {
378 if (queue_ == nullptr) {
379 return E_ERROR;
380 }
381 if (!PreLoad()) {
382 return E_OK;
383 }
384 uint64_t value = 0;
385 std::lock_guard<std::mutex> lock(mutex_);
386 auto has = queue_->PopNotWait(value);
387 if (has && value == 1) {
388 return PreferencesImpl::WriteToDiskFile(shared_from_this());
389 }
390 }
391 return E_OK;
392 }
393
GetValue(const std::string & key,const PreferencesValue & defValue)394 std::pair<int, PreferencesValue> PreferencesImpl::GetValue(const std::string &key, const PreferencesValue &defValue)
395 {
396 int errCode = CheckKey(key);
397 if (errCode != E_OK) {
398 return std::make_pair(errCode, defValue);
399 }
400
401 AwaitLoadFile();
402 IsClose(std::string(__FUNCTION__));
403 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
404 if (!isCleared_.load()) {
405 auto iter = valuesCache_.find(key);
406 if (iter != valuesCache_.end()) {
407 return std::make_pair(E_OK, iter->second);
408 }
409 }
410 return std::make_pair(E_NO_DATA, defValue);
411 }
412
GetAllData()413 std::pair<int, std::map<std::string, PreferencesValue>> PreferencesImpl::GetAllData()
414 {
415 AwaitLoadFile();
416 IsClose(std::string(__FUNCTION__));
417 std::map<std::string, PreferencesValue> allDatas;
418 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
419 if (!isCleared_.load()) {
420 for (auto &it : valuesCache_) {
421 allDatas.insert_or_assign(it.first, it.second);
422 }
423 }
424 return std::make_pair(E_OK, allDatas);
425 }
426
GetAllDatas()427 std::unordered_map<std::string, PreferencesValue> PreferencesImpl::GetAllDatas()
428 {
429 AwaitLoadFile();
430 IsClose(std::string(__FUNCTION__));
431 std::shared_lock<decltype(cacheMutex_)> lock(cacheMutex_);
432 if (!isCleared_.load()) {
433 return valuesCache_;
434 }
435 return {};
436 }
437
NotifyPreferencesObserver(std::shared_ptr<PreferencesImpl> pref,std::shared_ptr<std::unordered_set<std::string>> keysModified,std::shared_ptr<std::unordered_map<std::string,PreferencesValue>> writeToDisk)438 void PreferencesImpl::NotifyPreferencesObserver(std::shared_ptr<PreferencesImpl> pref,
439 std::shared_ptr<std::unordered_set<std::string>> keysModified,
440 std::shared_ptr<std::unordered_map<std::string, PreferencesValue>> writeToDisk)
441 {
442 if (keysModified->empty()) {
443 return;
444 }
445 std::shared_lock<std::shared_mutex> autoLock(pref->obseverMetux_);
446 for (const auto &[weakPrt, keys] : pref->dataObserversMap_) {
447 std::map<std::string, PreferencesValue> records;
448 for (auto &key : *keysModified) {
449 auto itKey = keys.find(key);
450 if (itKey == keys.end()) {
451 continue;
452 }
453 PreferencesValue value;
454 auto dataIt = writeToDisk->find(key);
455 if (dataIt != writeToDisk->end()) {
456 value = dataIt->second;
457 }
458 records.insert({key, value});
459 }
460 if (records.empty()) {
461 continue;
462 }
463 if (std::shared_ptr<PreferencesObserver> sharedPtr = weakPrt.lock()) {
464 sharedPtr->OnChange(records);
465 }
466 }
467
468 for (auto &it : pref->localObservers_) {
469 for (auto &key : *keysModified) {
470 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = it;
471 if (std::shared_ptr<PreferencesObserver> sharedPreferencesObserver = weakPreferencesObserver.lock()) {
472 sharedPreferencesObserver->OnChange(key);
473 }
474 }
475 }
476
477 ExecutorPool::Task task = [pref, keysModified] {
478 auto dataObsMgrClient = DataObsMgrClient::GetInstance();
479 if (dataObsMgrClient == nullptr) {
480 return;
481 }
482 for (auto &key : *keysModified) {
483 LOG_INFO("The %{public}s is changed, the observer needs to be triggered.",
484 Anonymous::ToBeAnonymous(key).c_str());
485 dataObsMgrClient->NotifyChange(pref->MakeUri(key));
486 }
487 };
488 executorPool_.Execute(std::move(task));
489 }
490 } // End of namespace NativePreferences
491 } // End of namespace OHOS
492