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 "napi_preferences.h"
17 
18 #include <algorithm>
19 #include <cerrno>
20 #include <climits>
21 #include <cmath>
22 #include <list>
23 
24 #include "js_sendable_utils.h"
25 #include "napi_async_call.h"
26 #include "napi_preferences_error.h"
27 #include "preferences.h"
28 #include "preferences_errno.h"
29 #include "preferences_value.h"
30 
31 using namespace OHOS::NativePreferences;
32 using namespace OHOS::PreferencesJsKit;
33 namespace OHOS::Sendable::JSPreferences {
34 #define MAX_KEY_LENGTH Preferences::MAX_KEY_LENGTH
35 #define MAX_VALUE_LENGTH Preferences::MAX_VALUE_LENGTH
36 
37 struct PreferencesAysncContext : public BaseContext {
38     std::weak_ptr<Preferences> instance_;
39     std::string key;
40     PreferencesValue defValue = PreferencesValue(static_cast<int64_t>(0));
41     napi_ref inputValueRef = nullptr;
42     std::map<std::string, PreferencesValue> allElements;
43     bool hasKey = false;
44     std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers;
45 
PreferencesAysncContextOHOS::Sendable::JSPreferences::PreferencesAysncContext46     PreferencesAysncContext()
47     {
48     }
~PreferencesAysncContextOHOS::Sendable::JSPreferences::PreferencesAysncContext49     virtual ~PreferencesAysncContext(){};
50 };
51 
52 static thread_local napi_ref constructor_ = nullptr;
53 
PreferencesProxy()54 PreferencesProxy::PreferencesProxy()
55 {
56 }
57 
~PreferencesProxy()58 PreferencesProxy::~PreferencesProxy()
59 {
60     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
61     observers_.clear();
62 }
63 
Destructor(napi_env env,void * nativeObject,void * finalize_hint)64 void PreferencesProxy::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
65 {
66     PreferencesProxy *obj = static_cast<PreferencesProxy *>(nativeObject);
67     delete obj;
68 }
69 
Init(napi_env env,napi_value exports)70 void PreferencesProxy::Init(napi_env env, napi_value exports)
71 {
72     napi_property_descriptor descriptors[] = {
73         DECLARE_NAPI_FUNCTION_WITH_DATA("put", SetValue, ASYNC),
74         DECLARE_NAPI_FUNCTION_WITH_DATA("putSync", SetValue, SYNC),
75         DECLARE_NAPI_FUNCTION_WITH_DATA("get", GetValue, ASYNC),
76         DECLARE_NAPI_FUNCTION_WITH_DATA("getSync", GetValue, SYNC),
77         DECLARE_NAPI_FUNCTION_WITH_DATA("getAll", GetAll, ASYNC),
78         DECLARE_NAPI_FUNCTION_WITH_DATA("getAllSync", GetAll, SYNC),
79         DECLARE_NAPI_FUNCTION_WITH_DATA("delete", Delete, ASYNC),
80         DECLARE_NAPI_FUNCTION_WITH_DATA("deleteSync", Delete, SYNC),
81         DECLARE_NAPI_FUNCTION_WITH_DATA("clear", Clear, ASYNC),
82         DECLARE_NAPI_FUNCTION_WITH_DATA("clearSync", Clear, SYNC),
83         DECLARE_NAPI_FUNCTION_WITH_DATA("has", HasKey, ASYNC),
84         DECLARE_NAPI_FUNCTION_WITH_DATA("hasSync", HasKey, SYNC),
85         DECLARE_NAPI_FUNCTION_WITH_DATA("flush", Flush, ASYNC),
86         DECLARE_NAPI_FUNCTION_WITH_DATA("flushSync", Flush, SYNC),
87         DECLARE_NAPI_FUNCTION("on", RegisterObserver),
88         DECLARE_NAPI_FUNCTION("off", UnregisterObserver),
89     };
90 
91     napi_value cons = nullptr;
92     napi_define_sendable_class(env, "Preferences", NAPI_AUTO_LENGTH, New, nullptr,
93         sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors, nullptr, &cons);
94 
95     napi_create_reference(env, cons, 1, &constructor_);
96 }
97 
NewInstance(napi_env env,std::shared_ptr<OHOS::NativePreferences::Preferences> value,napi_value * instance)98 napi_status PreferencesProxy::NewInstance(
99     napi_env env, std::shared_ptr<OHOS::NativePreferences::Preferences> value, napi_value *instance)
100 {
101     if (value == nullptr) {
102         LOG_ERROR("PreferencesProxy::NewInstance get native preferences is null");
103         return napi_invalid_arg;
104     }
105     napi_value cons;
106     napi_status status = napi_get_reference_value(env, constructor_, &cons);
107     if (status != napi_ok) {
108         return status;
109     }
110 
111     status = napi_new_instance(env, cons, 0, nullptr, instance);
112     if (status != napi_ok) {
113         return status;
114     }
115 
116     PreferencesProxy *obj = new (std::nothrow) PreferencesProxy();
117     if (obj == nullptr) {
118         LOG_ERROR("PreferencesProxy::New new failed, obj is nullptr");
119         return napi_invalid_arg;
120     }
121     obj->SetInstance(value);
122     status = napi_wrap_sendable(env, *instance, obj, PreferencesProxy::Destructor, nullptr);
123     if (status != napi_ok) {
124         delete obj;
125         return status;
126     }
127 
128     return napi_ok;
129 }
130 
New(napi_env env,napi_callback_info info)131 napi_value PreferencesProxy::New(napi_env env, napi_callback_info info)
132 {
133     napi_value thiz = nullptr;
134     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thiz, nullptr));
135     if (thiz == nullptr) {
136         LOG_WARN("get this failed");
137         return nullptr;
138     }
139     return thiz;
140 }
141 
ParseKey(napi_env env,const napi_value arg,std::shared_ptr<PreferencesAysncContext> context)142 int ParseKey(napi_env env, const napi_value arg, std::shared_ptr<PreferencesAysncContext> context)
143 {
144     int32_t rc = Utils::ConvertFromSendable(env, arg, context->key);
145     PRE_CHECK_RETURN_ERR_SET(rc == napi_ok, std::make_shared<ParamTypeError>("The key must be string."));
146     PRE_CHECK_RETURN_ERR_SET(context->key.length() <= MAX_KEY_LENGTH, std::make_shared<ParamTypeError>("The key must "
147                                                                                                        "be less than "
148                                                                                                        "80 bytes."));
149     return OK;
150 }
151 
ParseDefValue(const napi_env env,const napi_value jsVal,std::shared_ptr<PreferencesAysncContext> context)152 int ParseDefValue(const napi_env env, const napi_value jsVal, std::shared_ptr<PreferencesAysncContext> context)
153 {
154     int32_t rc = Utils::ConvertFromSendable(env, jsVal, context->defValue.value_);
155     if (rc == EXCEED_MAX_LENGTH) {
156         PRE_CHECK_RETURN_ERR_SET(rc == napi_ok,
157             std::make_shared<ParamTypeError>("The type of value must be less then 16 * 1024 * 1024 btyes."));
158     }
159     PRE_CHECK_RETURN_ERR_SET(rc == napi_ok, std::make_shared<ParamTypeError>("The type of value must be ValueType."));
160     return OK;
161 }
162 
GetSelfInstance(napi_env env,napi_value self)163 std::pair<PreferencesProxy *, std::weak_ptr<Preferences>> PreferencesProxy::GetSelfInstance(
164     napi_env env, napi_value self)
165 {
166     void *boundObj = nullptr;
167     napi_unwrap_sendable(env, self, &boundObj);
168     if (boundObj != nullptr) {
169         PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(boundObj);
170         return { obj, obj->GetInstance() };
171     }
172     return { nullptr, std::weak_ptr<Preferences>() };
173 }
174 
GetAllExecute(napi_env env,std::shared_ptr<PreferencesAysncContext> context,napi_value & result)175 int GetAllExecute(napi_env env, std::shared_ptr<PreferencesAysncContext> context, napi_value &result)
176 {
177     std::vector<napi_property_descriptor> descriptors;
178     for (const auto &[key, value] : context->allElements) {
179         descriptors.push_back(napi_property_descriptor(
180             DECLARE_NAPI_DEFAULT_PROPERTY(key.c_str(), Utils::ConvertToSendable(env, value.value_))));
181     }
182     napi_create_sendable_object_with_properties(env, descriptors.size(), descriptors.data(), &result);
183     return OK;
184 }
185 
GetAll(napi_env env,napi_callback_info info)186 napi_value PreferencesProxy::GetAll(napi_env env, napi_callback_info info)
187 {
188     LOG_DEBUG("GetAll start");
189     auto context = std::make_shared<PreferencesAysncContext>();
190     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
191         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
192         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
193         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
194                                                                                              "getting all."));
195     };
196     auto exec = [context]() -> int {
197         auto instance = context->instance_.lock();
198         if (instance == nullptr) {
199             return E_INNER_ERROR;
200         }
201         context->allElements = instance->GetAll();
202         return OK;
203     };
204     auto output = [context](napi_env env, napi_value &result) { GetAllExecute(env, context, result); };
205     context->SetAction(env, info, input, exec, output);
206 
207     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
208     return AsyncCall::Call(env, context, "GetAll");
209 }
210 
GetValue(napi_env env,napi_callback_info info)211 napi_value PreferencesProxy::GetValue(napi_env env, napi_callback_info info)
212 {
213     LOG_DEBUG("GetValue start");
214     auto context = std::make_shared<PreferencesAysncContext>();
215     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
216         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
217         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
218         napi_create_reference(env, argv[1], 1, &context->inputValueRef);
219         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
220         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
221                                                                                              "getting value."));
222     };
223     auto exec = [context]() -> int {
224         auto instance = context->instance_.lock();
225         if (instance == nullptr) {
226             return E_INNER_ERROR;
227         }
228         context->defValue = instance->Get(context->key, context->defValue);
229         return OK;
230     };
231     auto output = [context](napi_env env, napi_value &result) {
232         if (context->defValue.IsLong()) {
233             LOG_DEBUG("GetValue get default value.");
234             napi_get_reference_value(env, context->inputValueRef, &result);
235         } else {
236             result = Utils::ConvertToSendable(env, context->defValue.value_);
237         }
238         napi_delete_reference(env, context->inputValueRef);
239         PRE_CHECK_RETURN_VOID_SET(result != nullptr, std::make_shared<InnerError>("Failed to delete reference when "
240                                                                                   "getting value."));
241     };
242     context->SetAction(env, info, input, exec, output);
243 
244     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
245     return AsyncCall::Call(env, context, "GetValue");
246 }
247 
SetValue(napi_env env,napi_callback_info info)248 napi_value PreferencesProxy::SetValue(napi_env env, napi_callback_info info)
249 {
250     LOG_DEBUG("SetValue start");
251     auto context = std::make_shared<PreferencesAysncContext>();
252     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
253         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
254         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
255         PRE_CHECK_RETURN_VOID(ParseDefValue(env, argv[1], context) == OK);
256         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
257         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
258                                                                                              "setting value."));
259     };
260     auto exec = [context]() -> int {
261         auto instance = context->instance_.lock();
262         if (instance == nullptr) {
263             return E_INNER_ERROR;
264         }
265         return instance->Put(context->key, context->defValue);
266     };
267     auto output = [context](napi_env env, napi_value &result) {
268         napi_status status = napi_get_undefined(env, &result);
269         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
270                                                                                   "setting value."));
271         LOG_DEBUG("SetValue end.");
272     };
273     context->SetAction(env, info, input, exec, output);
274 
275     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
276     return AsyncCall::Call(env, context, "SetValue");
277 }
278 
Delete(napi_env env,napi_callback_info info)279 napi_value PreferencesProxy::Delete(napi_env env, napi_callback_info info)
280 {
281     LOG_DEBUG("Delete start");
282     auto context = std::make_shared<PreferencesAysncContext>();
283     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
284         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
285         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
286         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
287         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
288                                                                                              "deleting value."));
289     };
290     auto exec = [context]() -> int {
291         auto instance = context->instance_.lock();
292         if (instance == nullptr) {
293             return E_INNER_ERROR;
294         }
295         return instance->Delete(context->key);
296     };
297     auto output = [context](napi_env env, napi_value &result) {
298         napi_status status = napi_get_undefined(env, &result);
299         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
300                                                                                   "deleting value."));
301         LOG_DEBUG("Delete end.");
302     };
303     context->SetAction(env, info, input, exec, output);
304 
305     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
306     return AsyncCall::Call(env, context, "Delete");
307 }
308 
HasKey(napi_env env,napi_callback_info info)309 napi_value PreferencesProxy::HasKey(napi_env env, napi_callback_info info)
310 {
311     LOG_DEBUG("HasKey start");
312     auto context = std::make_shared<PreferencesAysncContext>();
313     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
314         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
315         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
316         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
317         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
318                                                                                              "having key."));
319     };
320     auto exec = [context]() -> int {
321         auto instance = context->instance_.lock();
322         if (instance == nullptr) {
323             return E_INNER_ERROR;
324         }
325         context->hasKey = instance->HasKey(context->key);
326         return OK;
327     };
328     auto output = [context](napi_env env, napi_value &result) {
329         napi_status status = napi_get_boolean(env, context->hasKey, &result);
330         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get boolean when having "
331                                                                                   "key."));
332         LOG_DEBUG("HasKey end.");
333     };
334     context->SetAction(env, info, input, exec, output);
335 
336     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
337     return AsyncCall::Call(env, context, "HasKey");
338 }
339 
Flush(napi_env env,napi_callback_info info)340 napi_value PreferencesProxy::Flush(napi_env env, napi_callback_info info)
341 {
342     LOG_DEBUG("Flush start");
343     auto context = std::make_shared<PreferencesAysncContext>();
344     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
345         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
346         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
347         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
348                                                                                              "flushing."));
349     };
350     auto exec = [context]() -> int {
351         auto instance = context->instance_.lock();
352         if (instance == nullptr) {
353             return E_INNER_ERROR;
354         }
355         return instance->FlushSync();
356     };
357     auto output = [context](napi_env env, napi_value &result) {
358         napi_status status = napi_get_undefined(env, &result);
359         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
360                                                                                   "flushing."));
361         LOG_DEBUG("Flush end.");
362     };
363     context->SetAction(env, info, input, exec, output);
364 
365     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
366     return AsyncCall::Call(env, context, "Flush");
367 }
368 
Clear(napi_env env,napi_callback_info info)369 napi_value PreferencesProxy::Clear(napi_env env, napi_callback_info info)
370 {
371     LOG_DEBUG("Clear start");
372     auto context = std::make_shared<PreferencesAysncContext>();
373     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
374         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
375         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
376         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap unwrap "
377                                                                                              "when clearing."));
378     };
379     auto exec = [context]() -> int {
380         auto instance = context->instance_.lock();
381         if (instance == nullptr) {
382             return E_INNER_ERROR;
383         }
384         return instance->Clear();
385     };
386     auto output = [context](napi_env env, napi_value &result) {
387         napi_status status = napi_get_undefined(env, &result);
388         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
389                                                                                   "clearing."));
390         LOG_DEBUG("Clear end.");
391     };
392     context->SetAction(env, info, input, exec, output);
393 
394     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
395     return AsyncCall::Call(env, context, "Clear");
396 }
397 
RegisterObserver(napi_env env,napi_callback_info info)398 napi_value PreferencesProxy::RegisterObserver(napi_env env, napi_callback_info info)
399 {
400     napi_value thiz = nullptr;
401     size_t argc = 3;            // 3 is specifies the length of the provided argc array
402     napi_value args[3] = { 0 }; // 3 is the max args length
403 
404     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
405     // This interface must have 2 or 3 parameters.
406     PRE_NAPI_ASSERT(env, argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 or 3"));
407     napi_valuetype type;
408     NAPI_CALL(env, napi_typeof(env, args[0], &type));
409     PRE_NAPI_ASSERT(env, type == napi_string, std::make_shared<ParamTypeError>("The registerMode must be string."));
410     std::string registerMode;
411     Utils::ConvertFromSendable(env, args[0], registerMode);
412     PRE_NAPI_ASSERT(env,
413         registerMode == STR_CHANGE || registerMode == STR_MULTI_PRECESS_CHANGE || registerMode == STR_DATA_CHANGE,
414         std::make_shared<ParamTypeError>("The registerMode must be 'change' or 'multiProcessChange' or "
415                                          "'dataChange'."));
416 
417     size_t funIndex = 1;
418     std::vector<std::string> keys;
419     auto mode = ConvertToRegisterMode(registerMode);
420     if (mode == Observer::DATA_CHANGE) {
421         int errCode = Utils::ConvertFromSendable(env, args[funIndex], keys);
422         PRE_NAPI_ASSERT(env, errCode == napi_ok && !keys.empty(),
423             std::make_shared<ParamTypeError>("The keys must be Array<string>."));
424         funIndex++;
425     }
426 
427     PRE_NAPI_ASSERT(env, argc == funIndex + 1, std::make_shared<ParamNumError>("2 or 3"));
428     NAPI_CALL(env, napi_typeof(env, args[funIndex], &type));
429     PRE_NAPI_ASSERT(env, type == napi_function, std::make_shared<ParamTypeError>("The callback must be function."));
430 
431     auto [obj, instance] = GetSelfInstance(env, thiz);
432     PRE_NAPI_ASSERT(env, obj != nullptr && obj->GetInstance() != nullptr,
433         std::make_shared<InnerError>("Failed to unwrap when register callback"));
434     int errCode = obj->RegisteredObserver(env, args[funIndex], mode, keys);
435     LOG_DEBUG("The observer subscribe %{public}d.", errCode);
436     PRE_NAPI_ASSERT(env, errCode == OK, std::make_shared<InnerError>(errCode));
437     return nullptr;
438 }
439 
UnregisterObserver(napi_env env,napi_callback_info info)440 napi_value PreferencesProxy::UnregisterObserver(napi_env env, napi_callback_info info)
441 {
442     napi_value thiz = nullptr;
443     size_t argc = 3;            // 3 is specifies the length of the provided argc array
444     napi_value args[3] = { 0 }; // 3 is the max args length
445 
446     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
447     PRE_NAPI_ASSERT(env, argc > 0, std::make_shared<ParamNumError>("more than 1"));
448 
449     napi_valuetype type;
450     NAPI_CALL(env, napi_typeof(env, args[0], &type));
451     PRE_NAPI_ASSERT(env, type == napi_string, std::make_shared<ParamTypeError>("The registerMode must be string."));
452 
453     std::string registerMode;
454     Utils::ConvertFromSendable(env, args[0], registerMode);
455     PRE_NAPI_ASSERT(env,
456         registerMode == STR_CHANGE || registerMode == STR_MULTI_PRECESS_CHANGE || registerMode == STR_DATA_CHANGE,
457         std::make_shared<ParamTypeError>("The unRegisterMode must be 'change' or 'multiProcessChange' or "
458                                          "'dataChange'."));
459 
460     size_t funIndex = 1;
461     napi_value callback = nullptr;
462     std::vector<std::string> keys;
463     auto mode = ConvertToRegisterMode(registerMode);
464     if (mode == Observer::DATA_CHANGE) {
465         int errCode = Utils::ConvertFromSendable(env, args[funIndex], keys);
466         PRE_NAPI_ASSERT(env, errCode == napi_ok && !keys.empty(),
467             std::make_shared<ParamTypeError>("The keys must be Array<string>."));
468         funIndex++;
469     }
470 
471     PRE_NAPI_ASSERT(env, argc <= funIndex + 1, std::make_shared<ParamNumError>("1 or 2 or 3"));
472     if (argc == funIndex + 1) {
473         NAPI_CALL(env, napi_typeof(env, args[funIndex], &type));
474         PRE_NAPI_ASSERT(env, type == napi_function || type == napi_undefined || type == napi_null,
475             std::make_shared<ParamTypeError>("The callback must be function."));
476         callback = args[funIndex];
477     }
478 
479     auto [obj, instance] = GetSelfInstance(env, thiz);
480     PRE_NAPI_ASSERT(env, obj != nullptr, std::make_shared<InnerError>("Failed to unwrap when unregister callback"));
481 
482     int errCode = obj->UnregisteredObserver(env, callback, mode, keys);
483     LOG_DEBUG("The observer unsubscribe 0x%{public}x.", errCode);
484     PRE_NAPI_ASSERT(env, errCode == OK, std::make_shared<InnerError>(errCode));
485     return nullptr;
486 }
487 
ConvertToRegisterMode(const std::string & mode)488 RegisterMode PreferencesProxy::ConvertToRegisterMode(const std::string &mode)
489 {
490     if (mode == STR_CHANGE) {
491         return RegisterMode::LOCAL_CHANGE;
492     } else if (mode == STR_MULTI_PRECESS_CHANGE) {
493         return RegisterMode::MULTI_PRECESS_CHANGE;
494     } else {
495         return RegisterMode::DATA_CHANGE;
496     }
497 }
498 
RegisteredObserver(napi_env env,napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)499 int PreferencesProxy::RegisteredObserver(
500     napi_env env, napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
501 {
502     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
503     auto it = observers_.find(env);
504     if (it == observers_.end()) {
505         it = observers_.emplace(
506             std::piecewise_construct, std::forward_as_tuple(env), std::forward_as_tuple(env, this)).first;
507     }
508     if (it == observers_.end()) {
509         return E_INNER_ERROR;
510     }
511     return it->second.Subscribe(callback, mode, keys);
512 }
513 
UnregisteredObserver(napi_env env,napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)514 int PreferencesProxy::UnregisteredObserver(
515     napi_env env, napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
516 {
517     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
518     auto it = observers_.find(env);
519     if (it == observers_.end()) {
520         return E_OK;
521     }
522     return it->second.Unsubscribe(callback, mode, keys);
523 }
524 
~JSObservers()525 PreferencesProxy::JSObservers::~JSObservers()
526 {
527     for (int mode = 0; mode < RegisterMode::CHANGE_BUTT; ++mode) {
528         Unsubscribe(nullptr, RegisterMode(mode), {});
529     }
530     napi_remove_env_cleanup_hook(env_, &CleanEnv, this);
531     uvQueue_ = nullptr;
532     env_ = nullptr;
533     proxy_ = nullptr;
534 }
535 
JSObservers(napi_env env,PreferencesProxy * proxy)536 PreferencesProxy::JSObservers::JSObservers(napi_env env, PreferencesProxy *proxy) : env_(env), proxy_(proxy)
537 {
538     uvQueue_ = std::make_shared<UvQueue>(env);
539     napi_add_env_cleanup_hook(env_, &CleanEnv, this);
540 }
541 
CleanEnv(void * obj)542 void PreferencesProxy::JSObservers::CleanEnv(void *obj)
543 {
544     auto *realObj = reinterpret_cast<JSObservers *>(obj);
545     if (realObj == nullptr) {
546         return;
547     }
548     auto proxy = realObj->proxy_;
549     auto env = realObj->env_;
550     if (proxy == nullptr || env == nullptr) {
551         return;
552     }
553 
554     std::lock_guard<decltype(proxy->mutex_)> lockGuard(proxy->mutex_);
555     proxy->observers_.erase(env);
556 }
557 
Subscribe(napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)558 int PreferencesProxy::JSObservers::Subscribe(
559     napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
560 {
561     auto &observers = observers_[mode];
562     auto observerIt = std::find_if(
563         observers.begin(), observers.end(), [env = env_, callback](std::shared_ptr<JSObserverImpl> observer) {
564             if (observer == nullptr) {
565                 return false;
566             }
567             return JSUtils::Equals(env, callback, observer->GetCallback());
568         });
569     if (observerIt != observers.end()) {
570         return E_OK;
571     }
572 
573     auto jsObserver = std::make_shared<JSObserverImpl>(uvQueue_, callback);
574     auto instance = proxy_->GetInstance();
575     if (instance == nullptr) {
576         return E_INNER_ERROR;
577     }
578     auto errCode = instance->Subscribe(jsObserver, mode, keys);
579     if (errCode != E_OK) {
580         return errCode;
581     }
582     observers.push_back(jsObserver);
583     return errCode;
584 }
585 
Unsubscribe(napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)586 int PreferencesProxy::JSObservers::Unsubscribe(
587     napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
588 {
589     auto instance = proxy_->GetInstance();
590     if (instance == nullptr) {
591         return E_INNER_ERROR;
592     }
593     int errCode = E_OK;
594     auto &observers = observers_[mode];
595     for (auto observer = observers.begin(); observer != observers.end();) {
596         if (callback == nullptr || JSUtils::Equals(env_, callback, (*observer)->GetCallback())) {
597             int status = instance->Unsubscribe(*observer, mode, keys);
598             if (status == E_OK) {
599                 (*observer)->ClearCallback();
600                 observer = observers.erase(observer);
601                 continue;
602             }
603             errCode = status;
604         }
605         ++observer;
606     }
607 
608     return errCode;
609 }
610 } // namespace OHOS::Sendable::JSPreferences
611