1 /*
2  * Copyright (c) 2022-2024 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 #include "napi_app_event_watcher.h"
16 
17 #include "app_event_store.h"
18 #include "ffrt.h"
19 #include "hiappevent_base.h"
20 #include "hilog/log.h"
21 #include "napi_util.h"
22 #include "uv.h"
23 
24 #undef LOG_DOMAIN
25 #define LOG_DOMAIN 0xD002D07
26 
27 #undef LOG_TAG
28 #define LOG_TAG "NapiWatcher"
29 
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace {
33 constexpr size_t CALLBACK_PARAM_NUM = 3;
34 constexpr size_t RECEIVE_PARAM_NUM = 2;
35 
SafeDeleteWork(uv_work_t * work)36 void SafeDeleteWork(uv_work_t* work)
37 {
38     if (work != nullptr) {
39         delete work;
40     }
41 }
42 
DeleteEventMappingAsync(int64_t observerSeq,const std::vector<std::shared_ptr<AppEventPack>> & events)43 void DeleteEventMappingAsync(int64_t observerSeq, const std::vector<std::shared_ptr<AppEventPack>>& events)
44 {
45     std::vector<int64_t> eventSeqs;
46     for (const auto& event : events) {
47         eventSeqs.emplace_back(event->GetSeq());
48     }
49     ffrt::submit([observerSeq, eventSeqs]() {
50         if (!AppEventStore::GetInstance().DeleteData(observerSeq, eventSeqs)) {
51             HILOG_ERROR(LOG_CORE, "failed to delete mapping data, seq=%{public}" PRId64 ", event num=%{public}zu",
52                 observerSeq, eventSeqs.size());
53         }
54         }, {}, {}, ffrt::task_attr().name("appevent_del_map"));
55 }
56 
OnTriggerWork(uv_work_t * work,int status)57 void OnTriggerWork(uv_work_t* work, int status)
58 {
59     auto context = static_cast<OnTriggerContext*>(work->data);
60     napi_handle_scope scope = nullptr;
61     napi_open_handle_scope(context->env, &scope);
62     if (scope == nullptr) {
63         HILOG_ERROR(LOG_CORE, "failed to open handle scope");
64         SafeDeleteWork(work);
65         return;
66     }
67     napi_value callback = NapiUtil::GetReferenceValue(context->env, context->onTrigger);
68     if (callback == nullptr) {
69         HILOG_ERROR(LOG_CORE, "failed to get callback from the context");
70         SafeDeleteWork(work);
71         napi_close_handle_scope(context->env, scope);
72         return;
73     }
74     napi_value argv[CALLBACK_PARAM_NUM] = {
75         NapiUtil::CreateInt32(context->env, context->row),
76         NapiUtil::CreateInt32(context->env, context->size),
77         NapiUtil::GetReferenceValue(context->env, context->holder)
78     };
79     napi_value ret = nullptr;
80     if (napi_call_function(context->env, nullptr, callback, CALLBACK_PARAM_NUM, argv, &ret) != napi_ok) {
81         HILOG_ERROR(LOG_CORE, "failed to call onTrigger function");
82     }
83     napi_close_handle_scope(context->env, scope);
84     SafeDeleteWork(work);
85 }
86 
OnReceiveWork(uv_work_t * work,int status)87 void OnReceiveWork(uv_work_t* work, int status)
88 {
89     auto context = static_cast<OnReceiveContext*>(work->data);
90     napi_handle_scope scope = nullptr;
91     napi_open_handle_scope(context->env, &scope);
92     if (scope == nullptr) {
93         HILOG_ERROR(LOG_CORE, "failed to open handle scope");
94         SafeDeleteWork(work);
95         return;
96     }
97     napi_value callback = NapiUtil::GetReferenceValue(context->env, context->onReceive);
98     if (callback == nullptr) {
99         HILOG_ERROR(LOG_CORE, "failed to get callback from the context");
100         SafeDeleteWork(work);
101         napi_close_handle_scope(context->env, scope);
102         return;
103     }
104     napi_value argv[RECEIVE_PARAM_NUM] = {
105         NapiUtil::CreateString(context->env, context->domain),
106         NapiUtil::CreateEventGroups(context->env, context->events)
107     };
108     napi_value ret = nullptr;
109     if (napi_call_function(context->env, nullptr, callback, RECEIVE_PARAM_NUM, argv, &ret) == napi_ok) {
110         DeleteEventMappingAsync(context->observerSeq, context->events);
111     } else {
112         HILOG_ERROR(LOG_CORE, "failed to call onReceive function");
113     }
114     napi_close_handle_scope(context->env, scope);
115     SafeDeleteWork(work);
116 }
117 }
~OnTriggerContext()118 OnTriggerContext::~OnTriggerContext()
119 {
120     if (onTrigger != nullptr) {
121         napi_delete_reference(env, onTrigger);
122     }
123     if (holder != nullptr) {
124         napi_delete_reference(env, holder);
125     }
126 }
127 
~OnReceiveContext()128 OnReceiveContext::~OnReceiveContext()
129 {
130     if (onReceive != nullptr) {
131         napi_delete_reference(env, onReceive);
132     }
133 }
134 
~WatcherContext()135 WatcherContext::~WatcherContext()
136 {
137     if (triggerContext != nullptr) {
138         delete triggerContext;
139     }
140     if (receiveContext != nullptr) {
141         delete receiveContext;
142     }
143 }
144 
NapiAppEventWatcher(const std::string & name,const std::vector<AppEventFilter> & filters,TriggerCondition cond)145 NapiAppEventWatcher::NapiAppEventWatcher(
146     const std::string& name,
147     const std::vector<AppEventFilter>& filters,
148     TriggerCondition cond)
149     : AppEventWatcher(name, filters, cond), context_(nullptr)
150 {}
151 
~NapiAppEventWatcher()152 NapiAppEventWatcher::~NapiAppEventWatcher()
153 {
154     HILOG_DEBUG(LOG_CORE, "start to destroy NapiAppEventWatcher object");
155     if (context_ == nullptr) {
156         return;
157     }
158     napi_env env = nullptr;
159     if (context_->receiveContext != nullptr) {
160         env = context_->receiveContext->env;
161     } else if (context_->triggerContext != nullptr) {
162         env = context_->triggerContext->env;
163     } else {
164         delete context_;
165         return;
166     }
167     uv_loop_t* loop = nullptr;
168     if (napi_get_uv_event_loop(env, &loop) != napi_ok || loop == nullptr) {
169         HILOG_ERROR(LOG_CORE, "failed to get loop.");
170         delete context_;
171         return;
172     }
173     uv_work_t* work = new(std::nothrow) uv_work_t();
174     if (work == nullptr) {
175         HILOG_ERROR(LOG_CORE, "failed to get work.");
176         delete context_;
177         return;
178     }
179     work->data = static_cast<void*>(context_);
180     uv_queue_work_with_qos(
181         loop,
182         work,
183         [] (uv_work_t* work) {
184             HILOG_DEBUG(LOG_CORE, "enter uv work callback.");
185         },
186         [] (uv_work_t* work, int status) {
187             WatcherContext* context = static_cast<WatcherContext*>(work->data);
188             HILOG_DEBUG(LOG_CORE, "start to destroy WatcherContext object");
189             delete context;
190             SafeDeleteWork(work);
191         },
192         uv_qos_default);
193 }
194 
InitHolder(const napi_env env,const napi_value holder)195 void NapiAppEventWatcher::InitHolder(const napi_env env, const napi_value holder)
196 {
197     if (context_ == nullptr) {
198         context_ = new(std::nothrow) WatcherContext();
199         if (context_ == nullptr) {
200             return;
201         }
202     }
203     if (context_->triggerContext == nullptr) {
204         context_->triggerContext = new(std::nothrow) OnTriggerContext();
205         if (context_->triggerContext == nullptr) {
206             return;
207         }
208     }
209     context_->triggerContext->env = env;
210     context_->triggerContext->holder = NapiUtil::CreateReference(env, holder);
211 }
212 
OnTrigger(const TriggerCondition & triggerCond)213 void NapiAppEventWatcher::OnTrigger(const TriggerCondition& triggerCond)
214 {
215     HILOG_DEBUG(LOG_CORE, "onTrigger start");
216     if (context_ == nullptr || context_->triggerContext == nullptr) {
217         HILOG_ERROR(LOG_CORE, "onTrigger context is null");
218         return;
219     }
220     context_->triggerContext->row = triggerCond.row;
221     context_->triggerContext->size = triggerCond.size;
222 
223     uv_loop_t* loop = nullptr;
224     if (napi_get_uv_event_loop(context_->triggerContext->env, &loop) != napi_ok || loop == nullptr) {
225         HILOG_ERROR(LOG_CORE, "failed to get loop.");
226         return;
227     }
228     uv_work_t* work = new(std::nothrow) uv_work_t();
229     if (work == nullptr) {
230         return;
231     }
232     work->data = static_cast<void*>(context_->triggerContext);
233     uv_queue_work_with_qos(loop, work, [] (uv_work_t* work) { HILOG_DEBUG(LOG_CORE, "enter uv work callback."); },
234         OnTriggerWork, uv_qos_default);
235 }
236 
InitTrigger(const napi_env env,const napi_value triggerFunc)237 void NapiAppEventWatcher::InitTrigger(const napi_env env, const napi_value triggerFunc)
238 {
239     HILOG_DEBUG(LOG_CORE, "start to init OnTrigger");
240     if (context_ == nullptr) {
241         context_ = new(std::nothrow) WatcherContext();
242         if (context_ == nullptr) {
243             return;
244         }
245     }
246     if (context_->triggerContext == nullptr) {
247         context_->triggerContext = new(std::nothrow) OnTriggerContext();
248         if (context_->triggerContext == nullptr) {
249             return;
250         }
251     }
252     context_->triggerContext->env = env;
253     context_->triggerContext->onTrigger = NapiUtil::CreateReference(env, triggerFunc);
254 }
255 
InitReceiver(const napi_env env,const napi_value receiveFunc)256 void NapiAppEventWatcher::InitReceiver(const napi_env env, const napi_value receiveFunc)
257 {
258     HILOG_DEBUG(LOG_CORE, "start to init onReceive");
259     if (context_ == nullptr) {
260         context_ = new(std::nothrow) WatcherContext();
261         if (context_ == nullptr) {
262             return;
263         }
264     }
265     if (context_->receiveContext == nullptr) {
266         context_->receiveContext = new(std::nothrow) OnReceiveContext();
267         if (context_->receiveContext == nullptr) {
268             return;
269         }
270     }
271     context_->receiveContext->env = env;
272     context_->receiveContext->onReceive = NapiUtil::CreateReference(env, receiveFunc);
273 }
274 
OnEvents(const std::vector<std::shared_ptr<AppEventPack>> & events)275 void NapiAppEventWatcher::OnEvents(const std::vector<std::shared_ptr<AppEventPack>>& events)
276 {
277     HILOG_DEBUG(LOG_CORE, "onEvents start, seq=%{public}" PRId64 ", event num=%{public}zu", GetSeq(), events.size());
278     if (context_ == nullptr || context_->receiveContext == nullptr || events.empty()) {
279         HILOG_ERROR(LOG_CORE, "onReceive context is null or events is empty");
280         return;
281     }
282     context_->receiveContext->domain = events[0]->GetDomain();
283     context_->receiveContext->events = events;
284     context_->receiveContext->observerSeq = GetSeq();
285 
286     uv_loop_t* loop = nullptr;
287     if (napi_get_uv_event_loop(context_->receiveContext->env, &loop) != napi_ok || loop == nullptr) {
288         HILOG_ERROR(LOG_CORE, "failed to get loop.");
289         return;
290     }
291     uv_work_t* work = new(std::nothrow) uv_work_t();
292     if (work == nullptr) {
293         return;
294     }
295     work->data = static_cast<void*>(context_->receiveContext);
296     uv_queue_work_with_qos(loop, work, [] (uv_work_t* work) { HILOG_DEBUG(LOG_CORE, "enter uv work callback."); },
297         OnReceiveWork, uv_qos_default);
298 }
299 
IsRealTimeEvent(std::shared_ptr<AppEventPack> event)300 bool NapiAppEventWatcher::IsRealTimeEvent(std::shared_ptr<AppEventPack> event)
301 {
302     return (context_ != nullptr && context_->receiveContext != nullptr);
303 }
304 } // namespace HiviewDFX
305 } // namespace OHOS
306