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 <functional>
17 #include <vector>
18 #include "native_parameters_js.h"
19 #include "uv.h"
20 
21 using namespace OHOS::system;
22 static constexpr int ARGC_NUMBER = 2;
23 static constexpr int BUF_LENGTH = 128;
24 
25 static napi_ref g_paramWatchRef;
26 
27 using ParamAsyncContext = struct ParamAsyncContext {
28     napi_env env = nullptr;
29     napi_async_work work = nullptr;
30 
31     char key[BUF_LENGTH] = { 0 };
32     size_t keyLen = 0;
33     char value[BUF_LENGTH] = { 0 };
34     size_t valueLen = 0;
35     int32_t timeout = 0;
36     napi_deferred deferred = nullptr;
37     napi_ref callbackRef = nullptr;
38 
39     int status = -1;
40     std::string getValue;
41 };
42 
43 using ParamWatcher = struct ParamWatcher {
44     napi_env env = nullptr;
45     napi_ref thisVarRef = nullptr;
46     char keyPrefix[BUF_LENGTH] = { 0 };
47     size_t keyLen = 0;
48     bool notifySwitch = false;
49     bool startWatch = false;
50     std::mutex mutex {};
51     napi_ref currCallbackRef = nullptr;
52     std::map<uint32_t, napi_ref> callbackReferences {};
53 };
54 
55 using ParamWatcherWork = struct ParamWatcherWork {
56     napi_async_work work = nullptr;
57     ParamWatcher *watcher = nullptr;
58     bool startWatch = false;
59 };
60 
61 using ParamChangeValue = struct ParamChangeValue {
62     uv_work_t work;
63     ParamWatcher *watcher;
64     std::string key;
65     std::string value;
66 };
67 
68 using ParamAsyncContextPtr = ParamAsyncContext *;
69 using ParamWatcherPtr = ParamWatcher *;
70 
NapiGetNull(napi_env env)71 static napi_value NapiGetNull(napi_env env)
72 {
73     napi_value result = 0;
74     napi_get_null(env, &result);
75     return result;
76 }
77 
GetNapiValue(napi_env env,int val)78 static napi_value GetNapiValue(napi_env env, int val)
79 {
80     napi_value result = nullptr;
81     napi_create_int32(env, val, &result);
82     return result;
83 }
84 
GetParamValue(napi_env env,napi_value arg,napi_valuetype valueType,char * buffer,size_t & buffLen)85 static int GetParamValue(napi_env env, napi_value arg, napi_valuetype valueType, char *buffer, size_t &buffLen)
86 {
87     napi_valuetype type = napi_null;
88     napi_typeof(env, arg, &type);
89     PARAM_JS_CHECK(type == valueType, return -1, "Invalid type %d %d", type, valueType);
90     napi_status status = napi_ok;
91     if (valueType == napi_string) {
92         status = napi_get_value_string_utf8(env, arg, buffer, buffLen, &buffLen);
93     } else if (valueType == napi_number) {
94         status = napi_get_value_int32(env, arg, reinterpret_cast<int *>(buffer));
95     }
96     return status;
97 }
98 
WaitCallbackWork(napi_env env,ParamAsyncContextPtr asyncContext)99 static void WaitCallbackWork(napi_env env, ParamAsyncContextPtr asyncContext)
100 {
101     napi_value resource = nullptr;
102     napi_create_string_utf8(env, "JSStartupGet", NAPI_AUTO_LENGTH, &resource);
103     napi_create_async_work(
104         env, nullptr, resource,
105         [](napi_env env, void *data) {
106             ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data);
107             asyncContext->status = WaitParameter(asyncContext->key, asyncContext->value, asyncContext->timeout);
108             PARAM_JS_LOGV("JSApp Wait status: %d, key: %s", asyncContext->status, asyncContext->key);
109         },
110         [](napi_env env, napi_status status, void *data) {
111             ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data);
112             napi_value result[ARGC_NUMBER] = { 0 };
113             napi_value message = nullptr;
114             napi_create_object(env, &result[0]);
115             napi_create_int32(env, asyncContext->status, &message);
116             napi_set_named_property(env, result[0], "code", message);
117             napi_get_undefined(env, &result[1]); // only one param
118 
119             PARAM_JS_LOGV("JSApp Wait status: %d, key: %s ", asyncContext->status, asyncContext->key);
120             if (asyncContext->deferred) {
121                 if (asyncContext->status == 0) {
122                     napi_resolve_deferred(env, asyncContext->deferred, result[1]);
123                 } else {
124                     napi_reject_deferred(env, asyncContext->deferred, result[0]);
125                 }
126             } else {
127                 napi_value callbackRef = nullptr;
128                 napi_value callResult = nullptr;
129                 napi_status status = napi_get_reference_value(env, asyncContext->callbackRef, &callbackRef);
130                 PARAM_JS_CHECK(status == 0 && callbackRef != nullptr, return, "Failed to get reference ");
131                 napi_value undefined;
132                 napi_get_undefined(env, &undefined);
133                 napi_call_function(env, undefined, callbackRef, ARGC_NUMBER, result, &callResult);
134                 napi_delete_reference(env, asyncContext->callbackRef);
135             }
136             napi_delete_async_work(env, asyncContext->work);
137             delete asyncContext;
138         },
139         reinterpret_cast<void *>(asyncContext), &asyncContext->work);
140     napi_queue_async_work(env, asyncContext->work);
141 }
142 
ParamWait(napi_env env,napi_callback_info info)143 napi_value ParamWait(napi_env env, napi_callback_info info)
144 {
145     constexpr int PARAM_TIMEOUT_INDEX = 2;
146     constexpr int ARGC_THREE_NUMBER = 3;
147     size_t argc = ARGC_THREE_NUMBER + 1;
148     napi_value argv[ARGC_THREE_NUMBER + 1];
149     napi_value thisVar = nullptr;
150     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
151     PARAM_JS_CHECK(status == napi_ok, return GetNapiValue(env, status), "Failed to get cb info");
152     PARAM_JS_CHECK(argc >= ARGC_THREE_NUMBER, return GetNapiValue(env, status), "Failed to get argc");
153 
154     ParamAsyncContextPtr asyncContext = new ParamAsyncContext();
155     PARAM_JS_CHECK(asyncContext != nullptr, return GetNapiValue(env, status), "Failed to create context");
156     asyncContext->env = env;
157 
158     // get param key
159     asyncContext->keyLen = BUF_LENGTH - 1;
160     asyncContext->valueLen = BUF_LENGTH - 1;
161     size_t len = sizeof(asyncContext->timeout);
162     int ret = GetParamValue(env, argv[0], napi_string, asyncContext->key, asyncContext->keyLen);
163     PARAM_JS_CHECK(ret == 0, delete asyncContext;
164         return GetNapiValue(env, ret), "Invalid param for wait");
165     ret = GetParamValue(env, argv[1], napi_string, asyncContext->value, asyncContext->valueLen);
166     PARAM_JS_CHECK(ret == 0, delete asyncContext;
167         return GetNapiValue(env, ret), "Invalid param for wait");
168     ret = GetParamValue(env, argv[PARAM_TIMEOUT_INDEX], napi_number, (char *)&asyncContext->timeout, len);
169     PARAM_JS_CHECK(ret == 0, delete asyncContext;
170         return GetNapiValue(env, ret), "Invalid param for wait");
171     if (argc > ARGC_THREE_NUMBER) {
172         napi_valuetype valueType = napi_null;
173         napi_typeof(env, argv[ARGC_THREE_NUMBER], &valueType);
174         PARAM_JS_CHECK(valueType == napi_function, delete asyncContext;
175             return GetNapiValue(env, ret), "Invalid param for wait callbackRef");
176         napi_create_reference(env, argv[ARGC_THREE_NUMBER], 1, &asyncContext->callbackRef);
177     }
178     PARAM_JS_LOGV("JSApp Wait key: %s, value: %s timeout %d.",
179         asyncContext->key, asyncContext->value, asyncContext->timeout);
180 
181     napi_value result = nullptr;
182     if (asyncContext->callbackRef == nullptr) {
183         napi_create_promise(env, &asyncContext->deferred, &result);
184     } else {
185         result = GetNapiValue(env, 0);
186     }
187     WaitCallbackWork(env, asyncContext);
188     return result;
189 }
190 
GetFristRefence(ParamWatcherPtr watcher,uint32_t & next)191 static bool GetFristRefence(ParamWatcherPtr watcher, uint32_t &next)
192 {
193     std::lock_guard<std::mutex> lock(watcher->mutex);
194     auto iter = watcher->callbackReferences.begin();
195     if (iter != watcher->callbackReferences.end()) {
196         next = watcher->callbackReferences.begin()->first;
197         return true;
198     }
199     return false;
200 }
201 
GetWatcherReference(ParamWatcherPtr watcher,uint32_t next)202 static napi_ref GetWatcherReference(ParamWatcherPtr watcher, uint32_t next)
203 {
204     std::lock_guard<std::mutex> lock(watcher->mutex);
205     auto iter = watcher->callbackReferences.find(next);
206     if (iter != watcher->callbackReferences.end()) {
207         return iter->second;
208     }
209     return nullptr;
210 }
211 
GetNextRefence(ParamWatcherPtr watcher,uint32_t & next)212 static uint32_t GetNextRefence(ParamWatcherPtr watcher, uint32_t &next)
213 {
214     std::lock_guard<std::mutex> lock(watcher->mutex);
215     auto iter = watcher->callbackReferences.upper_bound(next);
216     if (iter == watcher->callbackReferences.end()) {
217         return false;
218     }
219     next = iter->first;
220     return true;
221 }
222 
AddWatcherCallback(ParamWatcherPtr watcher,napi_ref callbackRef)223 static void AddWatcherCallback(ParamWatcherPtr watcher, napi_ref callbackRef)
224 {
225     static uint32_t watcherId = 0;
226     std::lock_guard<std::mutex> lock(watcher->mutex);
227     watcherId++;
228     watcher->callbackReferences[watcherId] = callbackRef;
229     PARAM_JS_LOGV("JSApp watcher add watcher callback %s %u.",
230         watcher->keyPrefix, watcherId);
231 }
232 
DelWatcherCallback(ParamWatcherPtr watcher,uint32_t next)233 static void DelWatcherCallback(ParamWatcherPtr watcher, uint32_t next)
234 {
235     PARAM_JS_LOGV("JSApp watcher key %s delete callback %u", watcher->keyPrefix, next);
236     std::lock_guard<std::mutex> lock(watcher->mutex);
237     watcher->callbackReferences.erase(next);
238 }
239 
DelCallbackRef(napi_env env,ParamWatcherPtr watcher,napi_ref callbackRef,uint32_t id)240 static void DelCallbackRef(napi_env env, ParamWatcherPtr watcher, napi_ref callbackRef, uint32_t id)
241 {
242     std::lock_guard<std::mutex> lock(watcher->mutex);
243     if (callbackRef == watcher->currCallbackRef) {
244         PARAM_JS_LOGV("JSApp watcher key %s has been callbacked %u.",
245             watcher->keyPrefix, id);
246         watcher->currCallbackRef = nullptr;
247     } else {
248         napi_delete_reference(env, callbackRef);
249     }
250     watcher->callbackReferences.erase(id);
251 }
252 
DelCallback(napi_env env,napi_value callback,ParamWatcherPtr watcher)253 static void DelCallback(napi_env env, napi_value callback, ParamWatcherPtr watcher)
254 {
255     bool isEquals = false;
256     uint32_t next = 0;
257     bool ret = GetFristRefence(watcher, next);
258     while (ret) {
259         napi_ref callbackRef = GetWatcherReference(watcher, next);
260         if (callbackRef == nullptr) {
261             PARAM_JS_LOGV("JSApp watcher key %s callbackRef has been deleted %d.", watcher->keyPrefix, next);
262             DelWatcherCallback(watcher, next);
263         } else if (callback != nullptr) {
264             napi_value handler = nullptr;
265             napi_get_reference_value(env, callbackRef, &handler);
266             napi_strict_equals(env, handler, callback, &isEquals);
267             if (isEquals) {
268                 DelCallbackRef(env, watcher, callbackRef, next);
269                 break;
270             }
271         } else {
272             DelCallbackRef(env, watcher, callbackRef, next);
273         }
274         ret = GetNextRefence(watcher, next);
275     }
276 }
277 
CheckCallbackEqual(napi_env env,napi_value callback,ParamWatcherPtr watcher)278 static bool CheckCallbackEqual(napi_env env, napi_value callback, ParamWatcherPtr watcher)
279 {
280     bool isEquals = false;
281     uint32_t next = 0;
282     bool ret = GetFristRefence(watcher, next);
283     while (ret) {
284         napi_ref callbackRef = GetWatcherReference(watcher, next);
285         if (callbackRef != nullptr) {
286             napi_value handler = nullptr;
287             napi_get_reference_value(env, callbackRef, &handler);
288             napi_strict_equals(env, handler, callback, &isEquals);
289             if (isEquals) {
290                 return true;
291             }
292         }
293         ret = GetNextRefence(watcher, next);
294     }
295     return false;
296 }
297 
ParamWatchConstructor(napi_env env,napi_callback_info info)298 static napi_value ParamWatchConstructor(napi_env env, napi_callback_info info)
299 {
300     size_t argc = 1;
301     napi_value argv[1];
302     napi_value thisVar = nullptr;
303     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
304     ParamWatcherPtr watcher = new ParamWatcher();
305     PARAM_JS_CHECK(watcher != nullptr, return NapiGetNull(env), "Failed to create param watcher");
306     napi_status status = napi_create_reference(env, thisVar, 1, &watcher->thisVarRef);
307     PARAM_JS_CHECK(status == 0, delete watcher;
308         return NapiGetNull(env), "Failed to create reference %d", status);
309 
310     napi_wrap(
311         env, thisVar, watcher,
312         [](napi_env env, void *data, void *hint) {
313             ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(data);
314             if (watcher) {
315                 DelCallback(env, nullptr, watcher);
316                 WatchParameter(watcher->keyPrefix, nullptr, nullptr);
317                 delete watcher;
318                 watcher = nullptr;
319             }
320         },
321         nullptr, nullptr);
322     return thisVar;
323 }
324 
GetWatcher(napi_env env,napi_callback_info info)325 napi_value GetWatcher(napi_env env, napi_callback_info info)
326 {
327     size_t argc = 1;
328     napi_value argv[1];
329     napi_value thisVar = nullptr;
330     void *data = nullptr;
331     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
332 
333     napi_value obj = thisVar;
334     ParamWatcherPtr watcher = nullptr;
335     napi_unwrap(env, thisVar, (void **)&watcher);
336     if (watcher == nullptr) { // check if watcher exist
337         napi_value constructor = nullptr;
338         int status = napi_get_reference_value(env, g_paramWatchRef, &constructor);
339         PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to get reference");
340         status = napi_new_instance(env, constructor, 0, nullptr, &obj);
341         PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to create instance for watcher");
342         napi_unwrap(env, obj, (void **)&watcher);
343     }
344     if (watcher != nullptr) {
345         watcher->keyLen = BUF_LENGTH;
346         int ret = GetParamValue(env, argv[0], napi_string, watcher->keyPrefix, watcher->keyLen);
347         PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get key prefix");
348         PARAM_JS_LOGV("JSApp watcher keyPrefix = %s ", watcher->keyPrefix);
349         ret = WatchParameter(watcher->keyPrefix, nullptr, watcher);
350         PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get watcher ret %d", ret);
351     }
352     return obj;
353 }
354 
GetWatcherInfo(napi_env env,napi_callback_info info,napi_value * callback)355 static ParamWatcherPtr GetWatcherInfo(napi_env env, napi_callback_info info, napi_value *callback)
356 {
357     size_t argc = ARGC_NUMBER;
358     napi_value argv[ARGC_NUMBER];
359     napi_value thisVar = nullptr;
360     void *data = nullptr;
361     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
362 
363     size_t typeLen = BUF_LENGTH - 1;
364     std::vector<char> eventType(typeLen, 0);
365     int ret = GetParamValue(env, argv[0], napi_string, eventType.data(), typeLen);
366     PARAM_JS_CHECK(ret == 0, return nullptr, "Failed to get event type");
367     if (strcmp(eventType.data(), "valueChange") != 0) {
368         return nullptr;
369     }
370     // argv[1]:callbackRef
371     if (argc > 1) {
372         napi_valuetype valuetype;
373         napi_status status = napi_typeof(env, argv[1], &valuetype);
374         PARAM_JS_CHECK(status == 0, return nullptr, "Failed to get type");
375         PARAM_JS_CHECK(valuetype == napi_function, return nullptr, "Invalid type %d", valuetype);
376         *callback = argv[1];
377     }
378     ParamWatcherPtr watcher = nullptr;
379     napi_unwrap(env, thisVar, (void **)&watcher);
380     return watcher;
381 }
382 
NotifyValueChange(ParamWatcherPtr watcher,uint32_t id,napi_value thisVar,const napi_value result[])383 static void NotifyValueChange(ParamWatcherPtr watcher, uint32_t id, napi_value thisVar, const napi_value result[])
384 {
385     napi_ref callbackRef = GetWatcherReference(watcher, id);
386     PARAM_JS_CHECK(callbackRef != nullptr, return,
387         "Failed to get callback for %s %d", watcher->keyPrefix, id);
388     napi_value callbackFunc = nullptr;
389     napi_status status = napi_get_reference_value(watcher->env, callbackRef, &callbackFunc);
390     PARAM_JS_CHECK(status == 0 && callbackFunc != nullptr, return,
391         "Failed to get callback for %s %d", watcher->keyPrefix, id);
392     {
393         std::lock_guard<std::mutex> lock(watcher->mutex);
394         watcher->currCallbackRef = callbackRef;
395     }
396 
397     napi_value callbackResult = nullptr;
398     napi_call_function(watcher->env, thisVar, callbackFunc, ARGC_NUMBER, result, &callbackResult);
399 
400     {
401         std::lock_guard<std::mutex> lock(watcher->mutex);
402         if (watcher->currCallbackRef == nullptr) {
403             PARAM_JS_LOGV("JSApp watcher notify key %s callback deleted watcherId %u",
404                 watcher->keyPrefix, id);
405             napi_delete_reference(watcher->env, callbackRef);
406         }
407         watcher->currCallbackRef = nullptr;
408     }
409 }
410 
HandleParameterChange(ParamWatcherPtr watcher,const char * key,const char * value)411 static void HandleParameterChange(ParamWatcherPtr watcher, const char *key, const char *value)
412 {
413     napi_handle_scope scope = nullptr;
414     napi_status status = napi_open_handle_scope(watcher->env, &scope);
415     PARAM_JS_CHECK(status == 0, return, "Failed to get reference ");
416     napi_value result[ARGC_NUMBER] = { 0 };
417     napi_create_string_utf8(watcher->env, key, strlen(key), &result[0]);
418     napi_create_string_utf8(watcher->env, value, strlen(value), &result[1]);
419     napi_value thisVar = nullptr;
420     status = napi_get_reference_value(watcher->env, watcher->thisVarRef, &thisVar);
421     PARAM_JS_CHECK(status == 0 && thisVar != nullptr, napi_close_handle_scope(watcher->env, scope);
422         return, "Failed to get reference ");
423     uint32_t next = 0;
424     bool ret = GetFristRefence(watcher, next);
425     while (ret) {
426         NotifyValueChange(watcher, next, thisVar, result);
427         ret = GetNextRefence(watcher, next);
428     }
429     napi_close_handle_scope(watcher->env, scope);
430     PARAM_JS_LOGV("JSApp watcher ProcessParamChange %s finish", key);
431 }
432 
ProcessParamChange(const char * key,const char * value,void * context)433 static void ProcessParamChange(const char *key, const char *value, void *context)
434 {
435     ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(context);
436     PARAM_JS_CHECK(watcher != nullptr && watcher->env != nullptr, return, "Invalid param");
437     PARAM_JS_CHECK(watcher->callbackReferences.size() > 0, return, "No callback for watcher");
438 
439     uv_loop_s *loop = nullptr;
440     napi_get_uv_event_loop(watcher->env, &loop);
441     PARAM_JS_CHECK(loop != nullptr, return, "Failed to get loop for %s", watcher->keyPrefix);
442     ParamChangeValue *change = new (std::nothrow) ParamChangeValue;
443     PARAM_JS_CHECK(change != nullptr, return, "Failed to get change for %s", watcher->keyPrefix);
444     change->work.data = reinterpret_cast<void *>(change);
445     change->watcher = watcher;
446     change->key = std::string(key);
447     change->value = std::string(value);
448     int ret = uv_queue_work(loop, &change->work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int result) {
449         ParamChangeValue *change = reinterpret_cast<ParamChangeValue *>(work->data);
450         HandleParameterChange(change->watcher, change->key.c_str(), change->value.c_str());
451         delete change;
452         change = nullptr;
453     });
454     PARAM_JS_CHECK(ret == 0, delete change;
455         return, "Failed to start work for %s", watcher->keyPrefix);
456 }
457 
WatchCallbackWork(napi_env env,ParamWatcherPtr watcher)458 static void WatchCallbackWork(napi_env env, ParamWatcherPtr watcher)
459 {
460     PARAM_JS_LOGV("JSApp WatchCallbackWork key: %s", watcher->keyPrefix);
461     ParamWatcherWork *worker = new ParamWatcherWork();
462     PARAM_JS_CHECK(worker != nullptr, return, "Failed to create worker ");
463     worker->watcher = watcher;
464     worker->work = nullptr;
465     worker->startWatch = watcher->startWatch;
466 
467     napi_value resource = nullptr;
468     napi_create_string_utf8(env, "JSStartupWatch", NAPI_AUTO_LENGTH, &resource);
469     napi_create_async_work(env, nullptr, resource,
470         [](napi_env env, void *data) {
471             ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data);
472             PARAM_JS_CHECK(worker != nullptr && worker->watcher != nullptr, return, "Invalid worker ");
473             int status = WatchParameter(worker->watcher->keyPrefix,
474                 worker->startWatch ? ProcessParamChange : nullptr, worker->watcher);
475             PARAM_JS_LOGV("JSApp WatchCallbackWork %s status: %d, key: %s",
476                 worker->startWatch ? "on" : "off", status, worker->watcher->keyPrefix);
477         },
478         [](napi_env env, napi_status status, void *data) {
479             ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data);
480             PARAM_JS_LOGV("JSApp WatchCallbackWork delete %s key: %s",
481                 worker->startWatch ? "on" : "off", worker->watcher->keyPrefix);
482             napi_delete_async_work(env, worker->work);
483             delete worker;
484         },
485         reinterpret_cast<void *>(worker), &worker->work);
486     napi_queue_async_work(env, worker->work);
487 }
488 
SwithWatchOn(napi_env env,napi_callback_info info)489 static napi_value SwithWatchOn(napi_env env, napi_callback_info info)
490 {
491     napi_value callback = nullptr;
492     ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback);
493     PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher switch param");
494 
495     if (CheckCallbackEqual(env, callback, watcher)) {
496         PARAM_JS_LOGE("JSApp watcher repeater switch on %s", watcher->keyPrefix);
497         return 0;
498     }
499     PARAM_JS_LOGV("JSApp watcher on %s", watcher->keyPrefix);
500     // save callback
501     napi_ref callbackRef;
502     napi_create_reference(env, callback, 1, &callbackRef);
503     AddWatcherCallback(watcher, callbackRef);
504     watcher->env = env;
505     {
506         std::lock_guard<std::mutex> lock(watcher->mutex);
507         if (watcher->startWatch) {
508             return GetNapiValue(env, 0);
509         }
510         watcher->startWatch = true;
511     }
512 
513     PARAM_JS_LOGV("JSApp watcher add %s", watcher->keyPrefix);
514     WatchCallbackWork(env, watcher);
515     PARAM_JS_LOGV("JSApp watcher on %s finish", watcher->keyPrefix);
516     return GetNapiValue(env, 0);
517 }
518 
SwithWatchOff(napi_env env,napi_callback_info info)519 static napi_value SwithWatchOff(napi_env env, napi_callback_info info)
520 {
521     napi_value callback = nullptr;
522     ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback);
523     PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher");
524     PARAM_JS_LOGV("JSApp watcher off %s", watcher->keyPrefix);
525     DelCallback(env, callback, watcher);
526     {
527         std::lock_guard<std::mutex> lock(watcher->mutex);
528         if (watcher->callbackReferences.size() == 0) {
529             watcher->startWatch = false;
530             WatchCallbackWork(env, watcher);
531         }
532     }
533     return GetNapiValue(env, 0);
534 }
535 
RegisterWatcher(napi_env env,napi_value exports)536 napi_value RegisterWatcher(napi_env env, napi_value exports)
537 {
538     napi_property_descriptor properties[] = {
539         DECLARE_NAPI_FUNCTION("on", SwithWatchOn),
540         DECLARE_NAPI_FUNCTION("off", SwithWatchOff),
541     };
542 
543     napi_value result = nullptr;
544     NAPI_CALL(env,
545         napi_define_class(env,
546             "paramWatcher",
547             NAPI_AUTO_LENGTH,
548             ParamWatchConstructor,
549             nullptr,
550             sizeof(properties) / sizeof(*properties),
551             properties,
552             &result));
553     napi_set_named_property(env, exports, "paramWatcher", result);
554     napi_create_reference(env, result, 1, &g_paramWatchRef);
555     return exports;
556 }
557