• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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