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 <uv.h>
17 #include <functional>
18 #include "scan_callback.h"
19 #include "napi_scan_utils.h"
20 #include "scan_log.h"
21 
22 namespace OHOS::Scan {
ScanCallback(napi_env env,napi_ref ref)23 ScanCallback::ScanCallback(napi_env env, napi_ref ref) : env_(env), ref_(ref), callbackFunction_(nullptr)
24 {
25 }
26 
ScanCallback(std::function<void (std::vector<ScanDeviceInfo> & infos)> callbackFunction)27 ScanCallback::ScanCallback(std::function<void(std::vector<ScanDeviceInfo> &infos)>
28     callbackFunction) : env_(nullptr), ref_(nullptr), callbackFunction_(callbackFunction)
29 {
30 }
31 
~ScanCallback()32 ScanCallback::~ScanCallback()
33 {
34     std::lock_guard<std::mutex> autoLock(mutex_);
35     if (env_ == nullptr || ref_ == nullptr) {
36         return;
37     }
38     SCAN_HILOGI("callback has been destroyed");
39     uv_loop_s *loop = nullptr;
40     napi_get_uv_event_loop(env_, &loop);
41     if (loop == nullptr) {
42         return;
43     }
44     Param *param = new (std::nothrow) Param;
45     if (param == nullptr) {
46         return;
47     }
48     param->env = env_;
49     param->callbackRef = ref_;
50     uv_work_t *work = new (std::nothrow) uv_work_t;
51     if (work == nullptr) {
52         delete param;
53         return;
54     }
55     work->data = reinterpret_cast<void*>(param);
56     int retVal = UvQueueWork(loop, work);
57     if (retVal != 0) {
58         SCAN_HILOGE("Failed to get uv_queue_work.");
59         delete param;
60         delete work;
61         return;
62     }
63 }
64 
InitialCallbackParam(napi_env & env_,napi_ref & ref_,std::mutex & mutex_)65 void CallbackParam::InitialCallbackParam(napi_env &env_, napi_ref &ref_, std::mutex &mutex_)
66 {
67     std::lock_guard<std::mutex> lock(mutex_);
68     this->env = env_;
69     this->ref = ref_;
70     this->mutexPtr = &mutex_;
71 }
72 
SetCallbackParam(uint32_t & state,const ScanDeviceInfoTCP & deviceInfoTCP)73 void CallbackParam::SetCallbackParam(uint32_t &state, const ScanDeviceInfoTCP &deviceInfoTCP)
74 {
75     std::lock_guard<std::mutex> lock(*mutexPtr);
76     this->state = state;
77     this->deviceInfoTCP = deviceInfoTCP;
78 }
79 
SetCallbackParam(uint32_t & state,const ScanDeviceInfo & deviceInfo)80 void CallbackParam::SetCallbackParam(uint32_t &state, const ScanDeviceInfo &deviceInfo)
81 {
82     std::lock_guard<std::mutex> lock(*mutexPtr);
83     this->state = state;
84     this->deviceInfo = deviceInfo;
85 }
86 
SetCallbackSyncParam(uint32_t & state,const ScanDeviceInfoSync & deviceInfoSync)87 void CallbackParam::SetCallbackSyncParam(uint32_t &state, const ScanDeviceInfoSync &deviceInfoSync)
88 {
89     std::lock_guard<std::mutex> lock(*mutexPtr);
90     this->state = state;
91     this->deviceInfoSync = deviceInfoSync;
92 }
93 
SetCallbackParam(bool & isGetSucc,int32_t & sizeRead)94 void CallbackParam::SetCallbackParam(bool &isGetSucc, int32_t &sizeRead)
95 {
96     std::lock_guard<std::mutex> lock(*mutexPtr);
97     this->isGetSucc = isGetSucc;
98     this->sizeRead = sizeRead;
99 }
100 
SetCallbackParam(int32_t & scanVersion)101 void CallbackParam::SetCallbackParam(int32_t &scanVersion)
102 {
103     std::lock_guard<std::mutex> lock(*mutexPtr);
104     this->scanVersion = scanVersion;
105 }
106 
SetCallbackParam(std::string & message)107 void CallbackParam::SetCallbackParam(std::string &message)
108 {
109     std::lock_guard<std::mutex> lock(*mutexPtr);
110     this->message = message;
111 }
112 
SetCallbackContext(CallbackParam * & callBackParam,uv_work_function & uvWorkLambda,std::mutex & mutex_)113 void CallbackContext::SetCallbackContext(CallbackParam* &callBackParam,
114     uv_work_function &uvWorkLambda, std::mutex &mutex_)
115 {
116     std::lock_guard<std::mutex> lock(mutex_);
117     this->callBackParam = callBackParam;
118     this->uvWorkLambda = uvWorkLambda;
119 }
120 
CreateCallbackParam(uv_work_t * & work,CallbackParam * & param,CallbackContext * & context,bool & flag)121 void ScanCallback::CreateCallbackParam(uv_work_t *&work, CallbackParam *&param, CallbackContext *&context, bool &flag)
122 {
123     work = new (std::nothrow) uv_work_t;
124     CHECK_AND_CREATE(work, "Failed to create uv_work_t work", flag);
125     param = new (std::nothrow) CallbackParam;
126     CHECK_AND_CREATE(param, "Failed to create CallbackParam param", flag);
127     context = new (std::nothrow) CallbackContext;
128     CHECK_AND_CREATE(context, "Failed to create CallbackContext context", flag);
129     if (!flag) {
130         DELETE_AND_NULLIFY(work);
131         DELETE_AND_NULLIFY(param);
132         DELETE_AND_NULLIFY(context);
133     }
134 }
135 
ExecuteUvQueueWork(CallbackContext * & context,uv_work_t * & work,uv_loop_s * & loop)136 bool ScanCallback::ExecuteUvQueueWork(CallbackContext* &context, uv_work_t* &work, uv_loop_s *&loop)
137 {
138     work->data = context;
139     int32_t retVal = uv_queue_work(
140         loop, work, [](uv_work_t *work) {},
141         [](uv_work_t *work, int statusInt) {
142             CallbackContext *context = static_cast<CallbackContext*>(work->data);
143             CallbackParam *cbParam = context->callBackParam;
144             napi_handle_scope scope = nullptr;
145             napi_open_handle_scope(cbParam->env, &scope);
146             if (scope != nullptr) {
147                 auto uvWorkLambda = context->uvWorkLambda;
148                 std::lock_guard<std::mutex> autoLock(*cbParam->mutexPtr);
149                 napi_value callbackFunc = NapiScanUtils::GetReference(cbParam->env, cbParam->ref);
150                 napi_value callbackResult = nullptr;
151                 uvWorkLambda(cbParam, callbackFunc, callbackResult);
152                 SCAN_HILOGD("run napi call deviceInfo callback fun success");
153                 napi_close_handle_scope(cbParam->env, scope);
154             }
155             DELETE_AND_NULLIFY(work);
156             DELETE_AND_NULLIFY(cbParam);
157             DELETE_AND_NULLIFY(context);
158         });
159     if (retVal != 0) {
160         SCAN_HILOGE("failed to get uv_queue_work.");
161         DELETE_AND_NULLIFY(work);
162         DELETE_AND_NULLIFY(context->callBackParam);
163         DELETE_AND_NULLIFY(context);
164         return false;
165     }
166     return true;
167 }
168 
OnCallback(uint32_t state,const ScanDeviceInfoTCP & info)169 bool ScanCallback::OnCallback(uint32_t state, const ScanDeviceInfoTCP &info)
170 {
171     SCAN_HILOGD("Enter OnCallback::ScanDeviceInfoTCP");
172 
173     INIT_CALLBACK_PARAMS;
174 
175     if (!flag) {
176         SCAN_HILOGE("ScanCallback::OnCallback ScanDeviceInfoTCP error exit");
177         return false;
178     }
179 
180     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
181         napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
182         callbackValues[0] = ScannerInfoHelperTCP::MakeJsObject(cbParam->env, cbParam->deviceInfoTCP);
183         napi_call_function(cbParam->env, nullptr, callbackFunc,
184             NapiScanUtils::ARGC_ONE, callbackValues, &callbackResult);
185     };
186     param->InitialCallbackParam(env_, ref_, mutex_);
187     param->SetCallbackParam(state, info);
188     context->SetCallbackContext(param, uvWorkLambda, mutex_);
189 
190     return ExecuteUvQueueWork(context, work, loop);
191 }
192 
OnCallback(uint32_t state,const ScanDeviceInfo & info)193 bool ScanCallback::OnCallback(uint32_t state, const ScanDeviceInfo &info)
194 {
195     SCAN_HILOGD("Enter OnCallback::ScanDeviceInfo");
196 
197     INIT_CALLBACK_PARAMS;
198 
199     if (!flag) {
200         SCAN_HILOGE("ScanCallback::OnCallback ScanDeviceInfo error exit");
201         return false;
202     }
203 
204     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
205         napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
206         callbackValues[0] = ScannerInfoHelper::MakeJsObject(cbParam->env, cbParam->deviceInfo);
207         napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
208             callbackValues, &callbackResult);
209     };
210     param->InitialCallbackParam(env_, ref_, mutex_);
211     param->SetCallbackParam(state, info);
212     context->SetCallbackContext(param, uvWorkLambda, mutex_);
213 
214     return ExecuteUvQueueWork(context, work, loop);
215 }
216 
OnCallbackSync(uint32_t state,const ScanDeviceInfoSync & info)217 bool ScanCallback::OnCallbackSync(uint32_t state, const ScanDeviceInfoSync &info)
218 {
219     SCAN_HILOGD("Enter OnCallback::ScanDeviceInfo");
220 
221     INIT_CALLBACK_PARAMS;
222 
223     if (!flag) {
224         SCAN_HILOGE("ScanCallback::OnCallback ScanDeviceInfo error exit");
225         return false;
226     }
227 
228     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
229         napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
230         callbackValues[0] = ScannerInfoSyncHelper::MakeJsObject(cbParam->env, cbParam->deviceInfoSync);
231         napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
232             callbackValues, &callbackResult);
233     };
234     param->InitialCallbackParam(env_, ref_, mutex_);
235     param->SetCallbackSyncParam(state, info);
236     context->SetCallbackContext(param, uvWorkLambda, mutex_);
237 
238     return ExecuteUvQueueWork(context, work, loop);
239 }
240 
OnGetFrameResCallback(bool isGetSucc,int32_t sizeRead)241 bool ScanCallback::OnGetFrameResCallback(bool isGetSucc, int32_t sizeRead)
242 {
243     SCAN_HILOGD("Enter OnCallback::OnGetFrameResCallback");
244 
245     INIT_CALLBACK_PARAMS;
246 
247     if (!flag) {
248         SCAN_HILOGE("ScanCallback::OnCallback OnGetFrameResCallback error exit");
249         return false;
250     }
251 
252     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
253         napi_value callbackValues[NapiScanUtils::ARGC_TWO] = { 0 };
254         callbackValues[0] = NapiScanUtils::CreateBoolean(cbParam->env, cbParam->isGetSucc);
255         callbackValues[1] = NapiScanUtils::CreateInt32(cbParam->env, cbParam->sizeRead);
256         napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_TWO,
257             callbackValues, &callbackResult);
258     };
259     param->InitialCallbackParam(env_, ref_, mutex_);
260     param->SetCallbackParam(isGetSucc, sizeRead);
261     context->SetCallbackContext(param, uvWorkLambda, mutex_);
262 
263     return ExecuteUvQueueWork(context, work, loop);
264 }
265 
OnScanInitCallback(int32_t & scanVersion)266 bool ScanCallback::OnScanInitCallback(int32_t &scanVersion)
267 {
268     SCAN_HILOGD("Enter OnCallback::OnScanInitCallback");
269 
270     INIT_CALLBACK_PARAMS;
271 
272     if (!flag) {
273         SCAN_HILOGE("ScanCallback::OnCallback OnScanInitCallback error exit");
274         return false;
275     }
276 
277     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
278         napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
279         callbackValues[0] = NapiScanUtils::CreateInt32(cbParam->env, cbParam->scanVersion);
280         napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
281             callbackValues, &callbackResult);
282     };
283     param->InitialCallbackParam(env_, ref_, mutex_);
284     param->SetCallbackParam(scanVersion);
285     context->SetCallbackContext(param, uvWorkLambda, mutex_);
286 
287     return ExecuteUvQueueWork(context, work, loop);
288 }
289 
OnSendSearchMessage(std::string & message)290 bool ScanCallback::OnSendSearchMessage(std::string &message)
291 {
292     SCAN_HILOGD("Enter OnCallback::OnSendSearchMessage");
293 
294     INIT_CALLBACK_PARAMS;
295 
296     if (!flag) {
297         SCAN_HILOGE("ScanCallback::OnCallback OnSendSearchMessage error exit");
298         return false;
299     }
300 
301     uv_work_function uvWorkLambda = [](CallbackParam* &cbParam, napi_value &callbackFunc, napi_value &callbackResult) {
302         napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
303         callbackValues[0] = NapiScanUtils::CreateStringUtf8(cbParam->env, cbParam->message);
304         napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
305             callbackValues, &callbackResult);
306     };
307     param->InitialCallbackParam(env_, ref_, mutex_);
308     param->SetCallbackParam(message);
309     context->SetCallbackContext(param, uvWorkLambda, mutex_);
310 
311     return ExecuteUvQueueWork(context, work, loop);
312 }
313 
OnGetDevicesList(std::vector<ScanDeviceInfo> & infos)314 bool ScanCallback::OnGetDevicesList(std::vector<ScanDeviceInfo> &infos)
315 {
316     SCAN_HILOGI("Enter OnGetDevicesList");
317     if (callbackFunction_ == nullptr) {
318         SCAN_HILOGE("callbackFunction_ is a nullptr");
319         return false;
320     }
321     callbackFunction_(infos);
322     return true;
323 }
324 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)325 int ScanCallback::UvQueueWork(uv_loop_s *loop, uv_work_t *work)
326 {
327     if (loop == nullptr || work == nullptr) {
328         return -1; // parameter error
329     }
330     int retVal = uv_queue_work(loop, work, [](uv_work_t *work) {}, [](uv_work_t *work, int status) {
331         SCAN_HILOGI("uv_queue_work ScanCallback DeleteReference");
332         if (work == nullptr) {
333             return;
334         }
335         Param *param = reinterpret_cast<Param*>(work->data);
336         if (param == nullptr) {
337             delete work;
338             return;
339         }
340         napi_handle_scope scope = nullptr;
341         napi_open_handle_scope(param->env, &scope);
342         if (scope == nullptr) {
343             delete param;
344             delete work;
345             return;
346         }
347         napi_ref callbackRef = param->callbackRef;
348         NapiScanUtils::DeleteReference(param->env, callbackRef);
349         napi_close_handle_scope(param->env, scope);
350         delete param;
351         delete work;
352     });
353     return retVal;
354 }
355 } // namespace OHOS::Scan
356