1 /*
2  * Copyright (c) 2022 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 "js_watcher.h"
17 
18 #include <cstring>
19 
20 #include "anonymous.h"
21 #include "js_common.h"
22 #include "js_util.h"
23 #include "logger.h"
24 #include "notifier_impl.h"
25 #include "objectstore_errors.h"
26 
27 namespace OHOS::ObjectStore {
JSWatcher(const napi_env env,DistributedObjectStore * objectStore,DistributedObject * object)28 JSWatcher::JSWatcher(const napi_env env, DistributedObjectStore *objectStore, DistributedObject *object)
29     : UvQueue(env), env_(env), changeEventListener_(nullptr), statusEventListener_(nullptr)
30 {
31 }
32 
~JSWatcher()33 JSWatcher::~JSWatcher()
34 {
35     delete changeEventListener_;
36     delete statusEventListener_;
37     changeEventListener_ = nullptr;
38     statusEventListener_ = nullptr;
39 }
40 
On(const char * type,napi_value handler)41 bool JSWatcher::On(const char *type, napi_value handler)
42 {
43     EventListener *listener = Find(type);
44     if (listener == nullptr) {
45         LOG_ERROR("error type %{public}s", type);
46         return false;
47     }
48     return listener->Add(env_, handler);
49 }
50 
Off(const char * type,napi_value handler)51 void JSWatcher::Off(const char *type, napi_value handler)
52 {
53     EventListener *listener = Find(type);
54     if (listener == nullptr) {
55         LOG_ERROR("error type %{public}s", type);
56         return;
57     }
58     if (handler == nullptr) {
59         listener->Clear(env_);
60     } else {
61         listener->Del(env_, handler);
62     }
63 }
ProcessChange(napi_env env,std::list<void * > & args)64 void JSWatcher::ProcessChange(napi_env env, std::list<void *> &args)
65 {
66     constexpr static int8_t ARGV_SIZE = 2;
67     napi_value callback = nullptr;
68     napi_value global = nullptr;
69     napi_value param[ARGV_SIZE];
70     napi_value result;
71     napi_status status = napi_get_global(env, &global);
72     NOT_MATCH_GOTO_ERROR(status == napi_ok);
73     for (auto item : args) {
74         ChangeArgs *changeArgs = static_cast<ChangeArgs *>(item);
75         status = napi_get_reference_value(env, changeArgs->callback_, &callback);
76         NOT_MATCH_GOTO_ERROR(status == napi_ok);
77         status = JSUtil::SetValue(env, changeArgs->sessionId_, param[0]);
78         NOT_MATCH_GOTO_ERROR(status == napi_ok);
79         JSUtil::SetValue(env, changeArgs->changeData_, param[1]);
80         NOT_MATCH_GOTO_ERROR(status == napi_ok);
81         LOG_INFO("start %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size());
82         status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result);
83         LOG_INFO("end %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size());
84         NOT_MATCH_GOTO_ERROR(status == napi_ok);
85     }
86 ERROR:
87     for (auto item : args) {
88         ChangeArgs *changeArgs = static_cast<ChangeArgs *>(item);
89         delete changeArgs;
90     }
91     args.clear();
92 }
Emit(const char * type,const std::string & sessionId,const std::vector<std::string> & changeData)93 void JSWatcher::Emit(const char *type, const std::string &sessionId, const std::vector<std::string> &changeData)
94 {
95     if (changeData.empty()) {
96         LOG_ERROR("empty change");
97         return;
98     }
99     LOG_INFO("start %{public}s, %{public}s", sessionId.c_str(), changeData.at(0).c_str());
100     EventListener *listener = Find(type);
101     if (listener == nullptr) {
102         LOG_ERROR("error type %{public}s", type);
103         return;
104     }
105 
106     for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) {
107         ChangeArgs *changeArgs = new (std::nothrow) ChangeArgs(handler->callbackRef, sessionId, changeData);
108         if (changeArgs == nullptr) {
109             LOG_ERROR("JSWatcher::Emit no memory for changeArgs malloc!");
110             return;
111         }
112         CallFunction(ProcessChange, changeArgs);
113     }
114 }
115 
Find(const char * type)116 EventListener *JSWatcher::Find(const char *type)
117 {
118     if (!strcmp(Constants::CHANGE, type)) {
119         return changeEventListener_;
120     }
121     if (!strcmp(Constants::STATUS, type)) {
122         return statusEventListener_;
123     }
124     return nullptr;
125 }
126 
ProcessStatus(napi_env env,std::list<void * > & args)127 void JSWatcher::ProcessStatus(napi_env env, std::list<void *> &args)
128 {
129     constexpr static int8_t ARGV_SIZE = 3;
130     napi_value callback = nullptr;
131     napi_value global = nullptr;
132     napi_value param[ARGV_SIZE];
133     napi_value result;
134     napi_status status = napi_get_global(env, &global);
135     NOT_MATCH_GOTO_ERROR(status == napi_ok);
136     for (auto item : args) {
137         StatusArgs *statusArgs = static_cast<StatusArgs *>(item);
138         status = napi_get_reference_value(env, statusArgs->callback_, &callback);
139         NOT_MATCH_GOTO_ERROR(status == napi_ok);
140         status = JSUtil::SetValue(env, statusArgs->sessionId_, param[0]);
141         NOT_MATCH_GOTO_ERROR(status == napi_ok);
142         status = JSUtil::SetValue(env, statusArgs->networkId_, param[1]);
143         NOT_MATCH_GOTO_ERROR(status == napi_ok);
144         status = JSUtil::SetValue(env, statusArgs->status_, param[2]);
145         NOT_MATCH_GOTO_ERROR(status == napi_ok);
146         LOG_INFO("start %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(),
147             Anonymous::Change(statusArgs->networkId_).c_str(), statusArgs->status_.c_str());
148         status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result);
149         LOG_INFO("end %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(),
150             Anonymous::Change(statusArgs->networkId_).c_str(), statusArgs->status_.c_str());
151         NOT_MATCH_GOTO_ERROR(status == napi_ok);
152     }
153 ERROR:
154     LOG_DEBUG("do clear");
155     for (auto item : args) {
156         StatusArgs *statusArgs = static_cast<StatusArgs *>(item);
157         delete statusArgs;
158     }
159     args.clear();
160 }
161 
Emit(const char * type,const std::string & sessionId,const std::string & networkId,const std::string & status)162 void JSWatcher::Emit(
163     const char *type, const std::string &sessionId, const std::string &networkId, const std::string &status)
164 {
165     if (sessionId.empty() || networkId.empty()) {
166         LOG_ERROR("empty %{public}s  %{public}s", sessionId.c_str(), Anonymous::Change(networkId).c_str());
167         return;
168     }
169     LOG_INFO("status change %{public}s  %{public}s", sessionId.c_str(), Anonymous::Change(networkId).c_str());
170     EventListener *listener = Find(type);
171     if (listener == nullptr) {
172         LOG_ERROR("error type %{public}s", type);
173         return;
174     }
175 
176     for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) {
177         StatusArgs *changeArgs = new (std::nothrow) StatusArgs(handler->callbackRef, sessionId, networkId, status);
178         if (changeArgs == nullptr) {
179             LOG_ERROR("JSWatcher::Emit no memory for StatusArgs malloc!");
180             return;
181         }
182         CallFunction(ProcessStatus, changeArgs);
183     }
184     return;
185 }
186 
IsEmpty()187 bool JSWatcher::IsEmpty()
188 {
189     if (changeEventListener_->IsEmpty() && statusEventListener_->IsEmpty()) {
190         return true;
191     }
192     return false;
193 }
194 
SetListener(ChangeEventListener * changeEventListener,StatusEventListener * statusEventListener)195 void JSWatcher::SetListener(ChangeEventListener *changeEventListener, StatusEventListener *statusEventListener)
196 {
197     changeEventListener_ = changeEventListener;
198     statusEventListener_ = statusEventListener;
199 }
200 
Find(napi_env env,napi_value handler)201 EventHandler *EventListener::Find(napi_env env, napi_value handler)
202 {
203     EventHandler *result = nullptr;
204     for (EventHandler *i = handlers_; i != nullptr; i = i->next) {
205         napi_value callback = nullptr;
206         napi_get_reference_value(env, i->callbackRef, &callback);
207         bool isEquals = false;
208         napi_strict_equals(env, handler, callback, &isEquals);
209         if (isEquals) {
210             result = i;
211         }
212     }
213     return result;
214 }
215 
Clear(napi_env env)216 void EventListener::Clear(napi_env env)
217 {
218     for (EventHandler *i = handlers_; i != nullptr; i = handlers_) {
219         handlers_ = i->next;
220         napi_delete_reference(env, i->callbackRef);
221         delete i;
222     }
223 }
224 
Del(napi_env env,napi_value handler)225 bool EventListener::Del(napi_env env, napi_value handler)
226 {
227     EventHandler *temp = nullptr;
228     napi_status status;
229     for (EventHandler *i = handlers_; i != nullptr;) {
230         napi_value callback = nullptr;
231         status = napi_get_reference_value(env, i->callbackRef, &callback);
232         if (status != napi_ok) {
233             LOG_ERROR("error! %{public}d", status);
234             return false;
235         }
236         bool isEquals = false;
237         napi_strict_equals(env, handler, callback, &isEquals);
238         if (isEquals) {
239             EventHandler *delData = i;
240             i = i->next;
241             if (temp == nullptr) {
242                 handlers_ = delData->next;
243             } else {
244                 temp->next = delData->next;
245             }
246             napi_delete_reference(env, delData->callbackRef);
247             delete delData;
248         } else {
249             temp = i;
250             i = i->next;
251         }
252     }
253     return handlers_ == nullptr;
254 }
255 
Add(napi_env env,napi_value handler)256 bool EventListener::Add(napi_env env, napi_value handler)
257 {
258     if (Find(env, handler) != nullptr) {
259         LOG_ERROR("has added,return");
260         return false;
261     }
262 
263     if (handlers_ == nullptr) {
264         handlers_ = new (std::nothrow) EventHandler();
265         if (handlers_ == nullptr) {
266             LOG_ERROR("EventListener::Add no memory for EventHandler malloc!");
267             return false;
268         }
269         handlers_->next = nullptr;
270     } else {
271         auto temp = new (std::nothrow) EventHandler();
272         if (temp == nullptr) {
273             LOG_ERROR("EventListener::Add no memory for EventHandler malloc!");
274             return false;
275         }
276         temp->next = handlers_;
277         handlers_ = temp;
278     }
279     napi_create_reference(env, handler, 1, &handlers_->callbackRef);
280     return true;
281 }
282 
OnChanged(const std::string & sessionid,const std::vector<std::string> & changedData)283 void WatcherImpl::OnChanged(const std::string &sessionid, const std::vector<std::string> &changedData)
284 {
285     std::shared_ptr<JSWatcher> lockedWatcher = watcher_.lock();
286     if (lockedWatcher) {
287         lockedWatcher->Emit(Constants::CHANGE, sessionid, changedData);
288     } else {
289         LOG_ERROR("watcher expired");
290     }
291 }
292 
~WatcherImpl()293 WatcherImpl::~WatcherImpl()
294 {
295     LOG_ERROR("destroy");
296 }
297 
Add(napi_env env,napi_value handler)298 bool ChangeEventListener::Add(napi_env env, napi_value handler)
299 {
300     if (!isWatched_ && object_ != nullptr) {
301         std::shared_ptr<WatcherImpl> watcher = std::make_shared<WatcherImpl>(watcher_);
302         uint32_t ret = objectStore_->Watch(object_, watcher);
303         if (ret != SUCCESS) {
304             LOG_ERROR("Watch %{public}s error", object_->GetSessionId().c_str());
305         } else {
306             LOG_INFO("Watch %{public}s success", object_->GetSessionId().c_str());
307             isWatched_ = true;
308         }
309     }
310     return EventListener::Add(env, handler);
311 }
312 
Del(napi_env env,napi_value handler)313 bool ChangeEventListener::Del(napi_env env, napi_value handler)
314 {
315     bool isEmpty = EventListener::Del(env, handler);
316     if (isEmpty && isWatched_ && object_ != nullptr) {
317         uint32_t ret = objectStore_->UnWatch(object_);
318         if (ret != SUCCESS) {
319             LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str());
320         } else {
321             LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str());
322             isWatched_ = false;
323         }
324     }
325     return isEmpty;
326 }
327 
Clear(napi_env env)328 void ChangeEventListener::Clear(napi_env env)
329 {
330     EventListener::Clear(env);
331     if (isWatched_ && object_ != nullptr) {
332         uint32_t ret = objectStore_->UnWatch(object_);
333         if (ret != SUCCESS) {
334             LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str());
335         } else {
336             LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str());
337             isWatched_ = false;
338         }
339     }
340 }
341 
ChangeEventListener(std::weak_ptr<JSWatcher> watcher,DistributedObjectStore * objectStore,DistributedObject * object)342 ChangeEventListener::ChangeEventListener(
343     std::weak_ptr<JSWatcher> watcher, DistributedObjectStore *objectStore, DistributedObject *object)
344     : objectStore_(objectStore), object_(object), watcher_(watcher)
345 {
346 }
347 
Add(napi_env env,napi_value handler)348 bool StatusEventListener::Add(napi_env env, napi_value handler)
349 {
350     LOG_INFO("Add status watch %{public}s", sessionId_.c_str());
351     NotifierImpl::GetInstance()->AddWatcher(sessionId_, watcher_);
352     return EventListener::Add(env, handler);
353 }
354 
Del(napi_env env,napi_value handler)355 bool StatusEventListener::Del(napi_env env, napi_value handler)
356 {
357     if (EventListener::Del(env, handler)) {
358         LOG_INFO("Del status watch %{public}s", sessionId_.c_str());
359         NotifierImpl::GetInstance()->DelWatcher(sessionId_);
360         return true;
361     }
362     return false;
363 }
364 
Clear(napi_env env)365 void StatusEventListener::Clear(napi_env env)
366 {
367     NotifierImpl::GetInstance()->DelWatcher(sessionId_);
368     EventListener::Clear(env);
369 }
370 
StatusEventListener(std::weak_ptr<JSWatcher> watcher,const std::string & sessionId)371 StatusEventListener::StatusEventListener(std::weak_ptr<JSWatcher> watcher, const std::string &sessionId)
372     : watcher_(watcher), sessionId_(sessionId)
373 {
374 }
375 
ChangeArgs(const napi_ref callback,const std::string & sessionId,const std::vector<std::string> & changeData)376 JSWatcher::ChangeArgs::ChangeArgs(
377     const napi_ref callback, const std::string &sessionId, const std::vector<std::string> &changeData)
378     : callback_(callback), sessionId_(sessionId), changeData_(changeData)
379 {
380 }
381 
StatusArgs(const napi_ref callback,const std::string & sessionId,const std::string & networkId,const std::string & status)382 JSWatcher::StatusArgs::StatusArgs(
383     const napi_ref callback, const std::string &sessionId, const std::string &networkId, const std::string &status)
384     : callback_(callback), sessionId_(sessionId), networkId_(networkId), status_(status)
385 {
386 }
387 } // namespace OHOS::ObjectStore
388