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 <sstream>
24 #include <thread>
25 #include <chrono>
26 #include <cinttypes>
27
28 #include "base64_helper.h"
29 #include "executor_pool.h"
30 #include "log_print.h"
31 #include "preferences_observer_stub.h"
32 #include "preferences_xml_utils.h"
33 #include "preferences_file_operation.h"
34 #include "preferences_anonymous.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;
44
45 template<typename T>
GetTypeName()46 std::string GetTypeName()
47 {
48 return "unknown";
49 }
50
51 template<>
GetTypeName()52 std::string GetTypeName<int>()
53 {
54 return "int";
55 }
56
57 template<>
GetTypeName()58 std::string GetTypeName<bool>()
59 {
60 return "bool";
61 }
62
63 template<>
GetTypeName()64 std::string GetTypeName<int64_t>()
65 {
66 return "long";
67 }
68
69 template<>
GetTypeName()70 std::string GetTypeName<uint64_t>()
71 {
72 return "uint64_t";
73 }
74
75 template<>
GetTypeName()76 std::string GetTypeName<float>()
77 {
78 return "float";
79 }
80
81 template<>
GetTypeName()82 std::string GetTypeName<double>()
83 {
84 return "double";
85 }
86
87 template<>
GetTypeName()88 std::string GetTypeName<std::string>()
89 {
90 return "string";
91 }
92
93 template<>
GetTypeName()94 std::string GetTypeName<std::vector<std::string>>()
95 {
96 return "stringArray";
97 }
98
99 template<>
GetTypeName()100 std::string GetTypeName<std::vector<double>>()
101 {
102 return "doubleArray";
103 }
104
105 template<>
GetTypeName()106 std::string GetTypeName<std::vector<bool>>()
107 {
108 return "boolArray";
109 }
110
111 template<>
GetTypeName()112 std::string GetTypeName<std::vector<uint8_t>>()
113 {
114 return "uint8Array";
115 }
116
117 template<>
GetTypeName()118 std::string GetTypeName<Object>()
119 {
120 return "object";
121 }
122
123 template<>
GetTypeName()124 std::string GetTypeName<BigInt>()
125 {
126 return "BigInt";
127 }
128
PreferencesImpl(const Options & options)129 PreferencesImpl::PreferencesImpl(const Options &options) : PreferencesBase(options)
130 {
131 loaded_.store(false);
132 isNeverUnlock_ = false;
133 loadResult_= false;
134 currentMemoryStateGeneration_ = 0;
135 diskStateGeneration_ = 0;
136 queue_ = std::make_shared<SafeBlockQueue<uint64_t>>(1);
137 }
138
~PreferencesImpl()139 PreferencesImpl::~PreferencesImpl()
140 {
141 }
142
Init()143 int PreferencesImpl::Init()
144 {
145 if (!StartLoadFromDisk()) {
146 return E_ERROR;
147 }
148 return E_OK;
149 }
150
StartLoadFromDisk()151 bool PreferencesImpl::StartLoadFromDisk()
152 {
153 std::lock_guard<std::mutex> lock(mutex_);
154 loaded_.store(false);
155 isNeverUnlock_ = false;
156 loadResult_ = false;
157
158 ExecutorPool::Task task = [pref = shared_from_this()] { PreferencesImpl::LoadFromDisk(pref); };
159 return (executorPool_.Execute(std::move(task)) == ExecutorPool::INVALID_TASK_ID) ? false : true;
160 }
161
162 /* static */
LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)163 void PreferencesImpl::LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)
164 {
165 if (pref->loaded_.load()) {
166 return;
167 }
168 std::lock_guard<std::mutex> lock(pref->mutex_);
169 if (!pref->loaded_.load()) {
170 std::string::size_type pos = pref->options_.filePath.find_last_of('/');
171 std::string filePath = pref->options_.filePath.substr(0, pos);
172 if (Access(filePath) != 0) {
173 pref->isNeverUnlock_ = true;
174 }
175 ConcurrentMap<std::string, PreferencesValue> values;
176 bool loadResult = pref->ReadSettingXml(values);
177 if (!loadResult) {
178 LOG_WARN("The settingXml %{public}s load failed.", ExtractFileName(pref->options_.filePath).c_str());
179 } else {
180 pref->valuesCache_ = std::move(values);
181 pref->loadResult_ = true;
182 pref->isNeverUnlock_ = false;
183 }
184 pref->loaded_.store(true);
185 pref->cond_.notify_all();
186 }
187 }
188
ReloadFromDisk()189 bool PreferencesImpl::ReloadFromDisk()
190 {
191 if (loadResult_) {
192 return false;
193 }
194
195 ConcurrentMap<std::string, PreferencesValue> values = valuesCache_;
196 bool loadResult = ReadSettingXml(values);
197 LOG_WARN("The settingXml %{public}s reload result is %{public}d",
198 ExtractFileName(options_.filePath).c_str(), loadResult);
199 if (loadResult) {
200 valuesCache_ = std::move(values);
201 isNeverUnlock_ = false;
202 loadResult_ = true;
203 return true;
204 }
205 return false;
206 }
207
PreLoad()208 bool PreferencesImpl::PreLoad()
209 {
210 std::lock_guard<std::mutex> lock(mutex_);
211 if (!loaded_.load()) {
212 return true;
213 }
214 if (Access(options_.filePath) == 0) {
215 if (isNeverUnlock_ || (!isNeverUnlock_ && !loadResult_)) {
216 return ReloadFromDisk();
217 }
218 }
219 return true;
220 }
221
AwaitLoadFile()222 void PreferencesImpl::AwaitLoadFile()
223 {
224 if (loaded_.load()) {
225 PreLoad();
226 return;
227 }
228 std::unique_lock<std::mutex> lock(mutex_);
229 if (!loaded_.load()) {
230 cond_.wait_for(lock, std::chrono::seconds(WAIT_TIME), [this] { return loaded_.load(); });
231 }
232
233 if (!loaded_.load()) {
234 LOG_ERROR("The settingXml %{public}s load timeout.", ExtractFileName(options_.filePath).c_str());
235 }
236 }
237
Get(const std::string & key,const PreferencesValue & defValue)238 PreferencesValue PreferencesImpl::Get(const std::string &key, const PreferencesValue &defValue)
239 {
240 if (CheckKey(key) != E_OK) {
241 return defValue;
242 }
243
244 AwaitLoadFile();
245
246 auto it = valuesCache_.Find(key);
247 if (it.first) {
248 return it.second;
249 }
250 return defValue;
251 }
252
GetAll()253 std::map<std::string, PreferencesValue> PreferencesImpl::GetAll()
254 {
255 AwaitLoadFile();
256 return valuesCache_.Clone();
257 }
258
259 template<typename T>
Convert2PrefValue(const Element & element,T & value)260 static void Convert2PrefValue(const Element &element, T &value)
261 {
262 if constexpr (std::is_same<T, bool>::value) {
263 value = (element.value_.compare("true") == 0) ? true : false;
264 } else if constexpr (std::is_same<T, std::string>::value) {
265 value = element.value_;
266 } else if constexpr (std::is_same<T, std::monostate>::value) {
267 value = std::monostate();
268 } else {
269 std::stringstream ss;
270 ss << element.value_;
271 ss >> value;
272 }
273 }
274
275 template<typename T>
Convert2PrefValue(const Element & element,std::vector<T> & values)276 static void Convert2PrefValue(const Element &element, std::vector<T> &values)
277 {
278 for (const auto &child : element.children_) {
279 T value;
280 Convert2PrefValue(child, value);
281 values.push_back(value);
282 }
283 }
284
Convert2PrefValue(const Element & element,BigInt & value)285 static void Convert2PrefValue(const Element &element, BigInt &value)
286 {
287 for (const auto &child : element.children_) {
288 uint64_t val;
289 Convert2PrefValue(child, val);
290 value.words_.push_back(val);
291 }
292 value.sign_ = 0;
293 if (!value.words_.empty()) {
294 value.sign_ = static_cast<int>(value.words_[value.words_.size() - 1]);
295 value.words_.pop_back();
296 }
297 }
298
299 template<typename T>
GetPrefValue(const Element & element,T & value)300 bool GetPrefValue(const Element &element, T &value)
301 {
302 LOG_WARN("unknown element type. the key is %{public}s", Anonymous::ToBeAnonymous(element.key_).c_str());
303 return false;
304 }
305
Convert2PrefValue(const Element & element,std::vector<uint8_t> & value)306 static void Convert2PrefValue(const Element &element, std::vector<uint8_t> &value)
307 {
308 if (!Base64Helper::Decode(element.value_, value)) {
309 value.clear();
310 }
311 }
312
Convert2PrefValue(const Element & element,Object & value)313 static void Convert2PrefValue(const Element &element, Object &value)
314 {
315 value.valueStr = element.value_;
316 }
317
318 template<typename T, typename First, typename... Types>
GetPrefValue(const Element & element,T & value)319 bool GetPrefValue(const Element &element, T &value)
320 {
321 if (element.tag_ == GetTypeName<First>()) {
322 First val;
323 Convert2PrefValue(element, val);
324 value = val;
325 return true;
326 }
327 return GetPrefValue<T, Types...>(element, value);
328 }
329
330 template<typename... Types>
Convert2PrefValue(const Element & element,std::variant<Types...> & value)331 bool Convert2PrefValue(const Element &element, std::variant<Types...> &value)
332 {
333 return GetPrefValue<decltype(value), Types...>(element, value);
334 }
335
ReadXmlElement(const Element & element,ConcurrentMap<std::string,PreferencesValue> & prefConMap)336 void ReadXmlElement(const Element &element, ConcurrentMap<std::string, PreferencesValue> &prefConMap)
337 {
338 PreferencesValue value(static_cast<int64_t>(0));
339 if (Convert2PrefValue(element, value.value_)) {
340 prefConMap.Insert(element.key_, value);
341 }
342 }
343
GetFileSize(const std::string & path)344 static int64_t GetFileSize(const std::string &path)
345 {
346 int64_t fileSize = -1;
347 struct stat buffer;
348 if (stat(path.c_str(), &buffer) == 0) {
349 fileSize = static_cast<int64_t>(buffer.st_size);
350 }
351 return fileSize;
352 }
353
ReadSettingXml(ConcurrentMap<std::string,PreferencesValue> & conMap)354 bool PreferencesImpl::ReadSettingXml(ConcurrentMap<std::string, PreferencesValue> &conMap)
355 {
356 std::vector<Element> settings;
357 auto begin = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
358 if (!PreferencesXmlUtils::ReadSettingXml(options_.filePath, options_.bundleName,
359 options_.dataGroupId, settings)) {
360 return false;
361 }
362 auto end = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
363 if (end - begin > LOAD_XML_LOG_TIME) {
364 LOG_ERROR("The settingXml %{public}s load time exceed 1s, file size:%{public}" PRId64 ".",
365 ExtractFileName(options_.filePath).c_str(), GetFileSize(options_.filePath));
366 }
367
368 for (const auto &element : settings) {
369 ReadXmlElement(element, conMap);
370 }
371
372 return true;
373 }
374
375 template<typename T>
Convert2Element(Element & elem,const T & value)376 void Convert2Element(Element &elem, const T &value)
377 {
378 elem.tag_ = GetTypeName<T>();
379 if constexpr (std::is_same<T, bool>::value) {
380 elem.value_ = ((bool)value) ? "true" : "false";
381 } else if constexpr (std::is_same<T, std::string>::value) {
382 elem.value_ = value;
383 } else if constexpr (std::is_same<T, std::monostate>::value) {
384 elem.value_ = {};
385 } else {
386 elem.value_ = std::to_string(value);
387 }
388 }
389
390 template<typename T>
Convert2Element(Element & elem,const std::vector<T> & value)391 void Convert2Element(Element &elem, const std::vector<T> &value)
392 {
393 elem.tag_ = GetTypeName<std::vector<T>>();
394 for (const T &val : value) {
395 Element element;
396 Convert2Element(element, val);
397 elem.children_.push_back(element);
398 }
399 }
400
Convert2Element(Element & elem,const std::vector<uint8_t> & value)401 void Convert2Element(Element &elem, const std::vector<uint8_t> &value)
402 {
403 elem.tag_ = GetTypeName<std::vector<uint8_t>>();
404 elem.value_ = Base64Helper::Encode(value);
405 }
406
Convert2Element(Element & elem,const Object & value)407 void Convert2Element(Element &elem, const Object &value)
408 {
409 elem.tag_ = GetTypeName<Object>();
410 elem.value_ = value.valueStr;
411 }
412
Convert2Element(Element & elem,const BigInt & value)413 void Convert2Element(Element &elem, const BigInt &value)
414 {
415 elem.tag_ = GetTypeName<BigInt>();
416 for (const auto &val : value.words_) {
417 Element element;
418 Convert2Element(element, val);
419 elem.children_.push_back(element);
420 }
421 // place symbol at the end
422 Element symbolElement;
423 Convert2Element(symbolElement, static_cast<uint64_t>(value.sign_));
424 elem.children_.push_back(symbolElement);
425 }
426
GetElement(Element & elem,const T & value)427 template<typename T> void GetElement(Element &elem, const T &value)
428 {
429 LOG_WARN("unknown element type. the key is %{public}s", Anonymous::ToBeAnonymous(elem.key_).c_str());
430 }
431
GetElement(Element & elem,const T & value)432 template<typename T, typename First, typename... Types> void GetElement(Element &elem, const T &value)
433 {
434 auto *val = std::get_if<First>(&value);
435 if (val != nullptr) {
436 return Convert2Element(elem, *val);
437 }
438 return GetElement<T, Types...>(elem, value);
439 }
440
Convert2Element(Element & elem,const std::variant<Types...> & value)441 template<typename... Types> void Convert2Element(Element &elem, const std::variant<Types...> &value)
442 {
443 return GetElement<decltype(value), Types...>(elem, value);
444 }
445
WriteXmlElement(Element & elem,const PreferencesValue & value)446 void WriteXmlElement(Element &elem, const PreferencesValue &value)
447 {
448 Convert2Element(elem, value.value_);
449 }
450
WriteSettingXml(const Options & options,const std::map<std::string,PreferencesValue> & writeToDiskMap)451 bool PreferencesImpl::WriteSettingXml(
452 const Options &options, const std::map<std::string, PreferencesValue> &writeToDiskMap)
453 {
454 std::vector<Element> settings;
455 for (auto it = writeToDiskMap.begin(); it != writeToDiskMap.end(); it++) {
456 Element elem;
457 elem.key_ = it->first;
458 PreferencesValue value = it->second;
459 WriteXmlElement(elem, value);
460 settings.push_back(elem);
461 }
462
463 return PreferencesXmlUtils::WriteSettingXml(options.filePath, options.bundleName, options.dataGroupId, settings);
464 }
465
466
HasKey(const std::string & key)467 bool PreferencesImpl::HasKey(const std::string &key)
468 {
469 if (CheckKey(key) != E_OK) {
470 return false;
471 }
472
473 AwaitLoadFile();
474 return valuesCache_.Contains(key);
475 }
476
Put(const std::string & key,const PreferencesValue & value)477 int PreferencesImpl::Put(const std::string &key, const PreferencesValue &value)
478 {
479 int errCode = CheckKey(key);
480 if (errCode != E_OK) {
481 return errCode;
482 }
483 errCode = CheckValue(value);
484 if (errCode != E_OK) {
485 return errCode;
486 }
487 AwaitLoadFile();
488
489 valuesCache_.Compute(key, [this, &value](auto &key, PreferencesValue &val) {
490 if (val == value) {
491 return true;
492 }
493 val = value;
494 modifiedKeys_.push_back(key);
495 return true;
496 });
497 return E_OK;
498 }
499
Delete(const std::string & key)500 int PreferencesImpl::Delete(const std::string &key)
501 {
502 int errCode = CheckKey(key);
503 if (errCode != E_OK) {
504 return errCode;
505 }
506 AwaitLoadFile();
507 valuesCache_.EraseIf(key, [this](auto &key, PreferencesValue &val) {
508 modifiedKeys_.push_back(key);
509 return true;
510 });
511 return E_OK;
512 }
513
Clear()514 int PreferencesImpl::Clear()
515 {
516 AwaitLoadFile();
517 valuesCache_.EraseIf([this](auto &key, PreferencesValue &val) {
518 modifiedKeys_.push_back(key);
519 return true;
520 });
521 return E_OK;
522 }
523
WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref)524 int PreferencesImpl::WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref)
525 {
526 std::list<std::string> keysModified;
527 std::map<std::string, PreferencesValue> writeToDiskMap;
528 pref->valuesCache_.DoActionWhenClone(
529 [pref, &writeToDiskMap, &keysModified](const std::map<std::string, PreferencesValue> &map) {
530 if (!pref->modifiedKeys_.empty()) {
531 keysModified = std::move(pref->modifiedKeys_);
532 }
533 writeToDiskMap = std::move(map);
534 });
535
536 // Cache has not changed, Not need to write persistent files.
537 if (keysModified.empty()) {
538 LOG_INFO("No data to update persistent file");
539 return E_OK;
540 }
541 if (!pref->WriteSettingXml(pref->options_, writeToDiskMap)) {
542 return E_ERROR;
543 }
544 if (pref->isNeverUnlock_) {
545 pref->isNeverUnlock_ = false;
546 }
547 if (!pref->loadResult_) {
548 pref->loadResult_ = true;
549 }
550 pref->NotifyPreferencesObserver(keysModified, writeToDiskMap);
551 return E_OK;
552 }
553
Flush()554 void PreferencesImpl::Flush()
555 {
556 auto success = queue_->PushNoWait(1);
557 if (!success) {
558 return;
559 }
560 std::weak_ptr<SafeBlockQueue<uint64_t>> queue = queue_;
561 ExecutorPool::Task task = [queue, self = weak_from_this()] {
562 auto realQueue = queue.lock();
563 auto realThis = self.lock();
564 if (realQueue == nullptr || realThis == nullptr) {
565 return;
566 }
567 uint64_t value = 0;
568 if (!realThis->PreLoad()) {
569 return;
570 }
571 std::lock_guard<std::mutex> lock(realThis->mutex_);
572 auto has = realQueue->PopNotWait(value);
573 if (has && value == 1) {
574 PreferencesImpl::WriteToDiskFile(realThis);
575 }
576 };
577 executorPool_.Schedule(std::chrono::milliseconds(TASK_EXEC_TIME), std::move(task));
578 }
579
FlushSync()580 int PreferencesImpl::FlushSync()
581 {
582 auto success = queue_->PushNoWait(1);
583 if (success) {
584 if (queue_ == nullptr) {
585 return E_ERROR;
586 }
587 if (!PreLoad()) {
588 return E_OK;
589 }
590 uint64_t value = 0;
591 std::lock_guard<std::mutex> lock(mutex_);
592 auto has = queue_->PopNotWait(value);
593 if (has && value == 1) {
594 return PreferencesImpl::WriteToDiskFile(shared_from_this());
595 }
596 }
597 return E_OK;
598 }
599
GetValue(const std::string & key,const PreferencesValue & defValue)600 std::pair<int, PreferencesValue> PreferencesImpl::GetValue(const std::string &key, const PreferencesValue &defValue)
601 {
602 int errCode = CheckKey(key);
603 if (errCode != E_OK) {
604 return std::make_pair(errCode, defValue);
605 }
606
607 AwaitLoadFile();
608 auto iter = valuesCache_.Find(key);
609 if (iter.first) {
610 return std::make_pair(E_OK, iter.second);
611 }
612 return std::make_pair(E_NO_DATA, defValue);
613 }
614
GetAllData()615 std::pair<int, std::map<std::string, PreferencesValue>> PreferencesImpl::GetAllData()
616 {
617 AwaitLoadFile();
618 return std::make_pair(E_OK, valuesCache_.Clone());
619 }
620
NotifyPreferencesObserver(const std::list<std::string> & keysModified,const std::map<std::string,PreferencesValue> & writeToDiskMap)621 void PreferencesImpl::NotifyPreferencesObserver(const std::list<std::string> &keysModified,
622 const std::map<std::string, PreferencesValue> &writeToDiskMap)
623 {
624 if (keysModified.empty()) {
625 return;
626 }
627 LOG_DEBUG("notify observer size:%{public}zu", dataObserversMap_.size());
628 std::shared_lock<std::shared_mutex> autoLock(obseverMetux_);
629 for (const auto &[weakPrt, keys] : dataObserversMap_) {
630 std::map<std::string, PreferencesValue> records;
631 for (auto key = keysModified.begin(); key != keysModified.end(); ++key) {
632 auto itKey = keys.find(*key);
633 if (itKey == keys.end()) {
634 continue;
635 }
636 PreferencesValue value;
637 auto dataIt = writeToDiskMap.find(*key);
638 if (dataIt != writeToDiskMap.end()) {
639 value = dataIt->second;
640 }
641 records.insert({*key, value});
642 }
643 if (records.empty()) {
644 continue;
645 }
646 if (std::shared_ptr<PreferencesObserver> sharedPtr = weakPrt.lock()) {
647 LOG_DEBUG("dataChange observer call, resultSize:%{public}zu", records.size());
648 sharedPtr->OnChange(records);
649 }
650 }
651
652 auto dataObsMgrClient = DataObsMgrClient::GetInstance();
653 for (auto key = keysModified.begin(); key != keysModified.end(); ++key) {
654 for (auto it = localObservers_.begin(); it != localObservers_.end(); ++it) {
655 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = *it;
656 if (std::shared_ptr<PreferencesObserver> sharedPreferencesObserver = weakPreferencesObserver.lock()) {
657 sharedPreferencesObserver->OnChange(*key);
658 }
659 }
660
661 if (dataObsMgrClient == nullptr) {
662 continue;
663 }
664 LOG_INFO("The %{public}s is changed, the observer needs to be triggered.",
665 Anonymous::ToBeAnonymous(*key).c_str());
666 dataObsMgrClient->NotifyChange(MakeUri(*key));
667 }
668 }
669 } // End of namespace NativePreferences
670 } // End of namespace OHOS
671