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