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 <cstdlib>
21 #include <functional>
22 #include <sstream>
23 #include <thread>
24
25 #include "base64_helper.h"
26 #include "executor_pool.h"
27 #include "log_print.h"
28 #include "preferences_errno.h"
29 #include "preferences_observer_stub.h"
30 #include "preferences_xml_utils.h"
31 #include "securec.h"
32
33 namespace OHOS {
34 namespace NativePreferences {
GetTypeName()35 template<typename T> std::string GetTypeName()
36 {
37 return "unknown";
38 }
39
GetTypeName()40 template<> std::string GetTypeName<int>()
41 {
42 return "int";
43 }
44
GetTypeName()45 template<> std::string GetTypeName<bool>()
46 {
47 return "bool";
48 }
49
GetTypeName()50 template<> std::string GetTypeName<int64_t>()
51 {
52 return "long";
53 }
54
GetTypeName()55 template<> std::string GetTypeName<float>()
56 {
57 return "float";
58 }
59
GetTypeName()60 template<> std::string GetTypeName<double>()
61 {
62 return "double";
63 }
64
GetTypeName()65 template<> std::string GetTypeName<std::string>()
66 {
67 return "string";
68 }
69
GetTypeName()70 template<> std::string GetTypeName<std::vector<std::string>>()
71 {
72 return "stringArray";
73 }
74
GetTypeName()75 template<> std::string GetTypeName<std::vector<double>>()
76 {
77 return "doubleArray";
78 }
79
GetTypeName()80 template<> std::string GetTypeName<std::vector<bool>>()
81 {
82 return "boolArray";
83 }
84
GetTypeName()85 template<> std::string GetTypeName<std::vector<uint8_t>>()
86 {
87 return "uint8Array";
88 }
89
90 ExecutorPool PreferencesImpl::executorPool_ = ExecutorPool(1, 0);
91
PreferencesImpl(const Options & options)92 PreferencesImpl::PreferencesImpl(const Options &options) : loaded_(false), options_(options)
93 {
94 currentMemoryStateGeneration_ = 0;
95 diskStateGeneration_ = 0;
96 }
97
MakeFilePath(const std::string & prefPath,const std::string & suffix)98 std::string PreferencesImpl::MakeFilePath(const std::string &prefPath, const std::string &suffix)
99 {
100 std::string filePath = prefPath;
101 filePath += suffix;
102 return filePath;
103 }
104
~PreferencesImpl()105 PreferencesImpl::~PreferencesImpl()
106 {
107 }
108
Init()109 int PreferencesImpl::Init()
110 {
111 if (!StartLoadFromDisk()) {
112 return E_ERROR;
113 }
114 return E_OK;
115 }
116
StartLoadFromDisk()117 bool PreferencesImpl::StartLoadFromDisk()
118 {
119 {
120 std::lock_guard<std::mutex> lock(mutex_);
121 loaded_ = false;
122 }
123
124 ExecutorPool::Task task = std::bind(PreferencesImpl::LoadFromDisk, shared_from_this());
125 return (executorPool_.Execute(std::move(task)) == ExecutorPool::INVALID_TASK_ID) ? false : true;
126 }
127
CheckKey(const std::string & key)128 int PreferencesImpl::CheckKey(const std::string &key)
129 {
130 if (key.empty()) {
131 LOG_ERROR("The key string is null or empty.");
132 return E_KEY_EMPTY;
133 }
134 if (Preferences::MAX_KEY_LENGTH < key.length()) {
135 LOG_ERROR("The key string length should shorter than 80.");
136 return E_KEY_EXCEED_MAX_LENGTH;
137 }
138 return E_OK;
139 }
140
141 /* static */
LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)142 void PreferencesImpl::LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)
143 {
144 std::lock_guard<std::mutex> lock(pref->mutex_);
145 if (pref->loaded_) {
146 return;
147 }
148 pref->ReadSettingXml(pref);
149 pref->loaded_ = true;
150 pref->cond_.notify_all();
151 }
152
AwaitLoadFile()153 void PreferencesImpl::AwaitLoadFile()
154 {
155 std::unique_lock<std::mutex> lock(mutex_);
156 if (!loaded_) {
157 cond_.wait(lock, [this] { return loaded_; });
158 }
159 }
160
WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref,std::shared_ptr<MemoryToDiskRequest> mcr)161 void PreferencesImpl::WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref, std::shared_ptr<MemoryToDiskRequest> mcr)
162 {
163 std::unique_lock<std::mutex> lock(pref->mutex_);
164 if (!pref->CheckRequestValidForStateGeneration(mcr)) {
165 mcr->SetDiskWriteResult(true, E_OK);
166 return;
167 }
168
169 if (pref->WriteSettingXml(pref, mcr->writeToDiskMap_)) {
170 pref->diskStateGeneration_ = mcr->memoryStateGeneration_;
171 mcr->SetDiskWriteResult(true, E_OK);
172 } else {
173 mcr->SetDiskWriteResult(false, E_ERROR);
174 }
175 }
176
CheckRequestValidForStateGeneration(std::shared_ptr<MemoryToDiskRequest> mcr)177 bool PreferencesImpl::CheckRequestValidForStateGeneration(std::shared_ptr<MemoryToDiskRequest> mcr)
178 {
179 if (diskStateGeneration_ >= mcr->memoryStateGeneration_) {
180 LOG_DEBUG("DiskStateGeneration should be less than memoryStateGeneration.");
181 return false;
182 }
183
184 if (mcr->isSyncRequest_ || currentMemoryStateGeneration_ == mcr->memoryStateGeneration_) {
185 return true;
186 }
187 return false;
188 }
189
Get(const std::string & key,const PreferencesValue & defValue)190 PreferencesValue PreferencesImpl::Get(const std::string &key, const PreferencesValue &defValue)
191 {
192 if (CheckKey(key) != E_OK) {
193 return defValue;
194 }
195
196 AwaitLoadFile();
197 std::lock_guard<std::mutex> lock(mutex_);
198 auto iter = map_.find(key);
199 if (iter != map_.end()) {
200 return iter->second;
201 }
202 return defValue;
203 }
204
GetAll()205 std::map<std::string, PreferencesValue> PreferencesImpl::GetAll()
206 {
207 AwaitLoadFile();
208 return map_;
209 }
210
Convert2PrefValue(const Element & element,T & value)211 template<typename T> static void Convert2PrefValue(const Element &element, T &value)
212 {
213 if constexpr (std::is_same<T, bool>::value) {
214 value = (element.value_.compare("true") == 0) ? true : false;
215 } else if constexpr (std::is_same<T, std::string>::value) {
216 value = element.value_;
217 } else {
218 std::stringstream ss;
219 ss << element.value_;
220 ss >> value;
221 }
222 }
223
Convert2PrefValue(const Element & element,std::vector<T> & values)224 template<typename T> static void Convert2PrefValue(const Element &element, std::vector<T> &values)
225 {
226 for (const auto &child : element.children_) {
227 T value;
228 Convert2PrefValue(child, value);
229 values.push_back(value);
230 }
231 }
232
GetPrefValue(const Element & element,T & value)233 template<typename T> bool GetPrefValue(const Element &element, T &value)
234 {
235 LOG_WARN("unknown element type. the key is %{public}s", element.key_.c_str());
236 return false;
237 }
238
Convert2PrefValue(const Element & element,std::vector<uint8_t> & value)239 static void Convert2PrefValue(const Element &element, std::vector<uint8_t> &value)
240 {
241 if (!Base64Helper::Decode(element.value_, value)) {
242 value.clear();
243 }
244 }
245
GetPrefValue(const Element & element,T & value)246 template<typename T, typename First, typename... Types> bool GetPrefValue(const Element &element, T &value)
247 {
248 if (element.tag_ == GetTypeName<First>()) {
249 First val;
250 Convert2PrefValue(element, val);
251 value = val;
252 return true;
253 }
254 return GetPrefValue<T, Types...>(element, value);
255 }
256
Convert2PrefValue(const Element & element,std::variant<Types...> & value)257 template<typename... Types> bool Convert2PrefValue(const Element &element, std::variant<Types...> &value)
258 {
259 return GetPrefValue<decltype(value), Types...>(element, value);
260 }
261
ReadXmlElement(const Element & element,std::map<std::string,PreferencesValue> & prefMap)262 void ReadXmlElement(const Element &element, std::map<std::string, PreferencesValue> &prefMap)
263 {
264 PreferencesValue value(static_cast<int64_t>(0));
265 if (Convert2PrefValue(element, value.value_)) {
266 prefMap.insert(std::make_pair(element.key_, value));
267 }
268 }
269
ReadSettingXml(std::shared_ptr<PreferencesImpl> pref)270 bool PreferencesImpl::ReadSettingXml(std::shared_ptr<PreferencesImpl> pref)
271 {
272 std::vector<Element> settings;
273 if (!PreferencesXmlUtils::ReadSettingXml(pref->options_.filePath, pref->options_.dataGroupId, settings)) {
274 return false;
275 }
276
277 for (const auto &element : settings) {
278 ReadXmlElement(element, pref->map_);
279 }
280 return true;
281 }
282
Convert2Element(Element & elem,const T & value)283 template<typename T> void Convert2Element(Element &elem, const T &value)
284 {
285 elem.tag_ = GetTypeName<T>();
286 if constexpr (std::is_same<T, bool>::value) {
287 elem.value_ = ((bool)value) ? "true" : "false";
288 } else if constexpr (std::is_same<T, std::string>::value) {
289 elem.value_ = value;
290 } else {
291 elem.value_ = std::to_string(value);
292 }
293 }
294
Convert2Element(Element & elem,const std::vector<T> & value)295 template<typename T> void Convert2Element(Element &elem, const std::vector<T> &value)
296 {
297 elem.tag_ = GetTypeName<std::vector<T>>();
298 for (const T &val : value) {
299 Element element;
300 Convert2Element(element, val);
301 elem.children_.push_back(element);
302 }
303 }
304
Convert2Element(Element & elem,const std::vector<uint8_t> & value)305 void Convert2Element(Element &elem, const std::vector<uint8_t> &value)
306 {
307 elem.tag_ = GetTypeName<std::vector<uint8_t>>();
308 elem.value_ = Base64Helper::Encode(value);
309 }
310
GetElement(Element & elem,const T & value)311 template<typename T> void GetElement(Element &elem, const T &value)
312 {
313 LOG_WARN("unknown element type. the key is %{public}s", elem.key_.c_str());
314 }
315
GetElement(Element & elem,const T & value)316 template<typename T, typename First, typename... Types> void GetElement(Element &elem, const T &value)
317 {
318 auto *val = std::get_if<First>(&value);
319 if (val != nullptr) {
320 return Convert2Element(elem, *val);
321 }
322 return GetElement<T, Types...>(elem, value);
323 }
324
Convert2Element(Element & elem,const std::variant<Types...> & value)325 template<typename... Types> void Convert2Element(Element &elem, const std::variant<Types...> &value)
326 {
327 return GetElement<decltype(value), Types...>(elem, value);
328 }
329
WriteXmlElement(Element & elem,const PreferencesValue & value)330 void WriteXmlElement(Element &elem, const PreferencesValue &value)
331 {
332 Convert2Element(elem, value.value_);
333 }
334
WriteSettingXml(std::shared_ptr<PreferencesImpl> pref,const std::map<std::string,PreferencesValue> & prefMap)335 bool PreferencesImpl::WriteSettingXml(
336 std::shared_ptr<PreferencesImpl> pref, const std::map<std::string, PreferencesValue> &prefMap)
337 {
338 std::vector<Element> settings;
339 for (auto it = prefMap.begin(); it != prefMap.end(); it++) {
340 Element elem;
341 elem.key_ = it->first;
342 PreferencesValue value = it->second;
343 WriteXmlElement(elem, value);
344 settings.push_back(elem);
345 }
346
347 return PreferencesXmlUtils::WriteSettingXml(pref->options_.filePath, pref->options_.dataGroupId, settings);
348 }
349
HasKey(const std::string & key)350 bool PreferencesImpl::HasKey(const std::string &key)
351 {
352 if (CheckKey(key) != E_OK) {
353 return false;
354 }
355
356 AwaitLoadFile();
357
358 std::lock_guard<std::mutex> lock(mutex_);
359 return (map_.find(key) != map_.end());
360 }
361
RegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver,RegisterMode mode)362 int PreferencesImpl::RegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver, RegisterMode mode)
363 {
364 std::lock_guard<std::mutex> lock(mutex_);
365 if (mode == RegisterMode::LOCAL_CHANGE) {
366 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = preferencesObserver;
367 localObservers_.push_back(weakPreferencesObserver);
368 } else {
369 auto dataObsMgrClient = DataObsMgrClient::GetInstance();
370 if (dataObsMgrClient == nullptr) {
371 return E_GET_DATAOBSMGRCLIENT_FAIL;
372 }
373 sptr<DataPreferencesObserverStub> observer(new (std::nothrow) DataPreferencesObserverStub(preferencesObserver));
374 int errcode = dataObsMgrClient->RegisterObserver(MakeUri(), observer);
375 if (errcode != 0) {
376 LOG_ERROR("RegisterObserver multiProcessChange failed, errCode %{public}d", errcode);
377 return errcode;
378 }
379 multiProcessObservers_.push_back(observer);
380 }
381 return E_OK;
382 }
383
UnRegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver,RegisterMode mode)384 int PreferencesImpl::UnRegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver, RegisterMode mode)
385 {
386 std::lock_guard<std::mutex> lock(mutex_);
387 if (mode == RegisterMode::LOCAL_CHANGE) {
388 for (auto it = localObservers_.begin(); it != localObservers_.end(); ++it) {
389 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = *it;
390 std::shared_ptr<PreferencesObserver> sharedObserver = weakPreferencesObserver.lock();
391 if (!sharedObserver || sharedObserver == preferencesObserver) {
392 localObservers_.erase(it);
393 break;
394 }
395 }
396 return E_OK;
397 }
398 for (auto it = multiProcessObservers_.begin(); it != multiProcessObservers_.end(); ++it) {
399 std::shared_ptr<PreferencesObserver> sharedObserver = (*it)->preferencesObserver_.lock();
400 if (!sharedObserver || sharedObserver == preferencesObserver) {
401 auto dataObsMgrClient = DataObsMgrClient::GetInstance();
402 if (dataObsMgrClient == nullptr) {
403 return E_GET_DATAOBSMGRCLIENT_FAIL;
404 }
405 int errcode = dataObsMgrClient->UnregisterObserver(MakeUri(), *it);
406 if (errcode != 0) {
407 LOG_ERROR("RegisterObserver multiProcessChange failed, errCode %{public}d", errcode);
408 return errcode;
409 }
410 multiProcessObservers_.erase(it);
411 break;
412 }
413 }
414 return E_OK;
415 }
416
Put(const std::string & key,const PreferencesValue & value)417 int PreferencesImpl::Put(const std::string &key, const PreferencesValue &value)
418 {
419 int errCode = CheckKey(key);
420 if (errCode != E_OK) {
421 return errCode;
422 }
423 if (value.IsString()) {
424 errCode = CheckStringValue(value);
425 if (errCode != E_OK) {
426 LOG_ERROR("PreferencesImpl::Put string value length should shorter than 8*1024");
427 return errCode;
428 }
429 }
430 AwaitLoadFile();
431
432 std::lock_guard<std::mutex> lock(mutex_);
433
434 auto iter = map_.find(key);
435 if (iter != map_.end()) {
436 PreferencesValue &val = iter->second;
437 if (val == value) {
438 return E_OK;
439 }
440 }
441 map_.insert_or_assign(key, value);
442 modifiedKeys_.push_back(key);
443 return E_OK;
444 }
445
CheckStringValue(const std::string & value)446 int PreferencesImpl::CheckStringValue(const std::string &value)
447 {
448 if (Preferences::MAX_VALUE_LENGTH < value.length()) {
449 LOG_ERROR("The value string length should shorter than 8 * 1024.");
450 return E_VALUE_EXCEED_MAX_LENGTH;
451 }
452 return E_OK;
453 }
454
MakeUri(const std::string & key)455 Uri PreferencesImpl::MakeUri(const std::string &key)
456 {
457 std::string uriStr;
458 if (options_.dataGroupId.empty()) {
459 uriStr = STR_SCHEME + options_.bundleName + STR_SLASH + options_.filePath;
460 } else {
461 uriStr = STR_SCHEME + options_.dataGroupId + STR_SLASH + options_.filePath;
462 }
463
464 if (!key.empty()) {
465 uriStr = uriStr + STR_QUERY + key;
466 }
467 return Uri(uriStr);
468 }
469
Delete(const std::string & key)470 int PreferencesImpl::Delete(const std::string &key)
471 {
472 int errCode = CheckKey(key);
473 if (errCode != E_OK) {
474 return errCode;
475 }
476
477 std::lock_guard<std::mutex> lock(mutex_);
478
479 auto pos = map_.find(key);
480 if (pos != map_.end()) {
481 map_.erase(pos);
482 modifiedKeys_.push_back(key);
483 }
484
485 return E_OK;
486 }
487
Clear()488 int PreferencesImpl::Clear()
489 {
490 std::lock_guard<std::mutex> lock(mutex_);
491
492 if (!map_.empty()) {
493 for (auto &kv : map_) {
494 modifiedKeys_.push_back(kv.first);
495 }
496 map_.clear();
497 }
498 return E_OK;
499 }
500
Flush()501 void PreferencesImpl::Flush()
502 {
503 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> request = commitToMemory();
504 request->isSyncRequest_ = false;
505 ExecutorPool::Task task = std::bind(PreferencesImpl::WriteToDiskFile, shared_from_this(), request);
506 executorPool_.Execute(std::move(task));
507
508 notifyPreferencesObserver(*request);
509 }
510
FlushSync()511 int PreferencesImpl::FlushSync()
512 {
513 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> request = commitToMemory();
514 request->isSyncRequest_ = true;
515 PreferencesImpl::WriteToDiskFile(shared_from_this(), request);
516 if (request->wasWritten_) {
517 LOG_DEBUG("Successfully written to disk file, memory state generation is %{public}" PRId64 "",
518 request->memoryStateGeneration_);
519 }
520 notifyPreferencesObserver(*request);
521 return request->writeToDiskResult_;
522 }
523
commitToMemory()524 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> PreferencesImpl::commitToMemory()
525 {
526 std::lock_guard<std::mutex> lock(mutex_);
527 int64_t memoryStateGeneration = -1;
528 std::list<std::string> keysModified;
529 std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers;
530 std::map<std::string, PreferencesValue> writeToDiskMap;
531 writeToDiskMap = map_;
532 if (!modifiedKeys_.empty()) {
533 currentMemoryStateGeneration_++;
534 keysModified = modifiedKeys_;
535 modifiedKeys_.clear();
536 }
537 memoryStateGeneration = currentMemoryStateGeneration_;
538 preferencesObservers = localObservers_;
539 return std::make_shared<MemoryToDiskRequest>(
540 writeToDiskMap, keysModified, preferencesObservers, memoryStateGeneration);
541 }
542
notifyPreferencesObserver(const PreferencesImpl::MemoryToDiskRequest & request)543 void PreferencesImpl::notifyPreferencesObserver(const PreferencesImpl::MemoryToDiskRequest &request)
544 {
545 if (request.keysModified_.empty()) {
546 return;
547 }
548
549 auto dataObsMgrClient = DataObsMgrClient::GetInstance();
550 for (auto key = request.keysModified_.begin(); key != request.keysModified_.end(); ++key) {
551 for (auto it = request.localObservers_.begin(); it != request.localObservers_.end(); ++it) {
552 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = *it;
553 if (std::shared_ptr<PreferencesObserver> sharedPreferencesObserver = weakPreferencesObserver.lock()) {
554 sharedPreferencesObserver->OnChange(*key);
555 }
556 }
557
558 if (dataObsMgrClient == nullptr) {
559 continue;
560 }
561 dataObsMgrClient->NotifyChange(MakeUri(*key));
562 }
563 }
564
MemoryToDiskRequest(const std::map<std::string,PreferencesValue> & writeToDiskMap,const std::list<std::string> & keysModified,const std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers,int64_t memStataGeneration)565 PreferencesImpl::MemoryToDiskRequest::MemoryToDiskRequest(
566 const std::map<std::string, PreferencesValue> &writeToDiskMap, const std::list<std::string> &keysModified,
567 const std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers, int64_t memStataGeneration)
568 {
569 writeToDiskMap_ = writeToDiskMap;
570 keysModified_ = keysModified;
571 localObservers_ = preferencesObservers;
572 memoryStateGeneration_ = memStataGeneration;
573 isSyncRequest_ = false;
574 wasWritten_ = false;
575 writeToDiskResult_ = E_ERROR;
576 }
577
SetDiskWriteResult(bool wasWritten,int result)578 void PreferencesImpl::MemoryToDiskRequest::SetDiskWriteResult(bool wasWritten, int result)
579 {
580 writeToDiskResult_ = result;
581 wasWritten_ = wasWritten;
582 reqCond_.notify_one();
583 }
584 } // End of namespace NativePreferences
585 } // End of namespace OHOS
586