1 /*
2 * Copyright (c) 2021-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 "vibrator_napi_utils.h"
17
18 #include <string>
19 #include "hilog/log.h"
20 #include "securec.h"
21
22 #include "miscdevice_log.h"
23 #include "vibrator_napi_error.h"
24
25 #undef LOG_TAG
26 #define LOG_TAG "VibratorNapiUtils"
27
28 namespace OHOS {
29 namespace Sensors {
30 namespace {
31 constexpr int32_t RESULT_LENGTH = 2;
32 } // namespace
~AsyncCallbackInfo()33 AsyncCallbackInfo::~AsyncCallbackInfo()
34 {
35 CALL_LOG_ENTER;
36 if (asyncWork != nullptr) {
37 MISC_HILOGD("Delete work");
38 napi_delete_async_work(env, asyncWork);
39 }
40 for (int32_t i = 0; i < CALLBACK_NUM; ++i) {
41 if (callback[i] != nullptr) {
42 MISC_HILOGD("Delete reference, i:%{public}d", i);
43 napi_delete_reference(env, callback[i]);
44 }
45 }
46 }
47
IsMatchType(const napi_env & env,const napi_value & value,const napi_valuetype & type)48 bool IsMatchType(const napi_env &env, const napi_value &value, const napi_valuetype &type)
49 {
50 napi_valuetype paramType = napi_undefined;
51 napi_status ret = napi_typeof(env, value, ¶mType);
52 if ((ret != napi_ok) || (paramType != type)) {
53 MISC_HILOGE("Type mismatch");
54 return false;
55 }
56 return true;
57 }
58
GetNapiInt32(const napi_env & env,const int32_t value,napi_value & result)59 bool GetNapiInt32(const napi_env &env, const int32_t value, napi_value &result)
60 {
61 CALL_LOG_ENTER;
62 napi_status ret = napi_create_int32(env, value, &result);
63 if (ret != napi_ok) {
64 MISC_HILOGE("GetNapiInt32 failed");
65 return false;
66 }
67 return true;
68 }
69
GetInt32Value(const napi_env & env,const napi_value & value,int32_t & result)70 bool GetInt32Value(const napi_env &env, const napi_value &value, int32_t &result)
71 {
72 CALL_LOG_ENTER;
73 napi_valuetype valuetype = napi_undefined;
74 CHKCF(napi_typeof(env, value, &valuetype) == napi_ok, "napi_typeof failed");
75 CHKCF((valuetype == napi_number), "Wrong argument type. Number expected");
76 CHKCF(napi_get_value_int32(env, value, &result) == napi_ok, "napi_get_value_int32 failed");
77 return true;
78 }
79
GetInt64Value(const napi_env & env,const napi_value & value,int64_t & result)80 bool GetInt64Value(const napi_env &env, const napi_value &value, int64_t &result)
81 {
82 CALL_LOG_ENTER;
83 napi_valuetype valuetype = napi_undefined;
84 CHKCF(napi_typeof(env, value, &valuetype) == napi_ok, "napi_typeof failed");
85 CHKCF((valuetype == napi_number), "Wrong argument type. Number expected");
86 CHKCF(napi_get_value_int64(env, value, &result) == napi_ok, "napi_get_value_int64 failed");
87 return true;
88 }
89
GetBoolValue(const napi_env & env,const napi_value & value,bool & result)90 bool GetBoolValue(const napi_env &env, const napi_value &value, bool &result)
91 {
92 CALL_LOG_ENTER;
93 napi_valuetype valuetype = napi_undefined;
94 CHKCF(napi_typeof(env, value, &valuetype) == napi_ok, "napi_typeof failed");
95 CHKCF((valuetype == napi_boolean), "Wrong argument type. bool expected");
96 CHKCF(napi_get_value_bool(env, value, &result) == napi_ok, "napi_get_value_bool failed");
97 return true;
98 }
99
GetStringValue(const napi_env & env,const napi_value & value,string & result)100 bool GetStringValue(const napi_env &env, const napi_value &value, string &result)
101 {
102 CALL_LOG_ENTER;
103 napi_valuetype valuetype = napi_undefined;
104 napi_status ret = napi_typeof(env, value, &valuetype);
105 if (ret != napi_ok) {
106 MISC_HILOGE("napi_typeof failed");
107 return false;
108 }
109 CHKCF((valuetype == napi_string), "Wrong argument type. String or function expected");
110 size_t bufLength = 0;
111 ret = napi_get_value_string_utf8(env, value, nullptr, 0, &bufLength);
112 if (ret != napi_ok) {
113 MISC_HILOGE("napi_get_value_string_utf8 failed");
114 return false;
115 }
116 bufLength = bufLength > STRING_LENGTH_MAX ? STRING_LENGTH_MAX : bufLength;
117 char str[STRING_LENGTH_MAX] = {0};
118 size_t strLen = 0;
119 ret = napi_get_value_string_utf8(env, value, str, bufLength + 1, &strLen);
120 if (ret != napi_ok) {
121 MISC_HILOGE("napi_get_value_string_utf8 failed");
122 return false;
123 }
124 result = str;
125 return true;
126 }
127
GetPropertyItem(const napi_env & env,const napi_value & value,const std::string & type,napi_value & item)128 bool GetPropertyItem(const napi_env &env, const napi_value &value, const std::string &type, napi_value &item)
129 {
130 bool exist = false;
131 napi_status status = napi_has_named_property(env, value, type.c_str(), &exist);
132 if ((status != napi_ok) || (!exist)) {
133 MISC_HILOGE("Can not find %{public}s property", type.c_str());
134 return false;
135 }
136 CHKCF((napi_get_named_property(env, value, type.c_str(), &item) == napi_ok), "napi get property fail");
137 return true;
138 }
139
GetPropertyString(const napi_env & env,const napi_value & value,const std::string & type,std::string & result)140 bool GetPropertyString(const napi_env &env, const napi_value &value, const std::string &type, std::string &result)
141 {
142 bool exist = false;
143 napi_status status = napi_has_named_property(env, value, type.c_str(), &exist);
144 if ((status != napi_ok) || (!exist)) {
145 MISC_HILOGE("Can not find %{public}s property", type.c_str());
146 return false;
147 }
148
149 napi_value item = nullptr;
150 CHKCF((napi_get_named_property(env, value, type.c_str(), &item) == napi_ok), "napi get property fail");
151 if (!GetStringValue(env, item, result)) {
152 return false;
153 }
154 return true;
155 }
156
GetPropertyInt32(const napi_env & env,const napi_value & value,const std::string & type,int32_t & result)157 bool GetPropertyInt32(const napi_env &env, const napi_value &value, const std::string &type, int32_t &result)
158 {
159 napi_value item = nullptr;
160 bool exist = false;
161 napi_status status = napi_has_named_property(env, value, type.c_str(), &exist);
162 if (status != napi_ok || !exist) {
163 MISC_HILOGD("Can not find %{public}s property", type.c_str());
164 return false;
165 }
166 CHKCF((napi_get_named_property(env, value, type.c_str(), &item) == napi_ok), "napi get property fail");
167 if (!GetInt32Value(env, item, result)) {
168 MISC_HILOGE("Get int value fail");
169 return false;
170 }
171 return true;
172 }
173
GetPropertyInt64(const napi_env & env,const napi_value & value,const std::string & type,int64_t & result)174 bool GetPropertyInt64(const napi_env &env, const napi_value &value, const std::string &type, int64_t &result)
175 {
176 napi_value item = nullptr;
177 bool exist = false;
178 napi_status status = napi_has_named_property(env, value, type.c_str(), &exist);
179 if (status != napi_ok || !exist) {
180 MISC_HILOGE("Can not find %{public}s property", type.c_str());
181 return false;
182 }
183 CHKCF((napi_get_named_property(env, value, type.c_str(), &item) == napi_ok), "napi get property fail");
184 if (!GetInt64Value(env, item, result)) {
185 MISC_HILOGE("Get int value fail");
186 return false;
187 }
188 return true;
189 }
190
GetPropertyBool(const napi_env & env,const napi_value & value,const std::string & type,bool & result)191 bool GetPropertyBool(const napi_env &env, const napi_value &value, const std::string &type, bool &result)
192 {
193 bool exist = false;
194 napi_status status = napi_has_named_property(env, value, type.c_str(), &exist);
195 if ((status != napi_ok) || (!exist)) {
196 MISC_HILOGD("Can not find %{public}s property", type.c_str());
197 return false;
198 }
199 napi_value item = nullptr;
200 CHKCF((napi_get_named_property(env, value, type.c_str(), &item) == napi_ok), "napi get property fail");
201 if (!GetBoolValue(env, item, result)) {
202 MISC_HILOGE("Get bool value fail");
203 return false;
204 }
205 return true;
206 }
207
208 std::map<int32_t, ConstructResultFunc> g_convertFuncList = {
209 {COMMON_CALLBACK, ConstructCommonResult},
210 {IS_SUPPORT_EFFECT_CALLBACK, ConstructIsSupportEffectResult},
211 };
212
ConvertErrorToResult(const napi_env & env,sptr<AsyncCallbackInfo> asyncCallbackInfo,napi_value & result)213 bool ConvertErrorToResult(const napi_env &env, sptr<AsyncCallbackInfo> asyncCallbackInfo, napi_value &result)
214 {
215 CHKPF(asyncCallbackInfo);
216 int32_t code = asyncCallbackInfo->error.code;
217 auto msg = GetNapiError(code);
218 if (!msg) {
219 MISC_HILOGE("ErrCode:%{public}d is invalid", code);
220 return false;
221 }
222 result = CreateBusinessError(env, code, msg.value());
223 return (result != nullptr);
224 }
225
ConstructCommonResult(const napi_env & env,sptr<AsyncCallbackInfo> asyncCallbackInfo,napi_value result[],int32_t length)226 bool ConstructCommonResult(const napi_env &env, sptr<AsyncCallbackInfo> asyncCallbackInfo, napi_value result[],
227 int32_t length)
228 {
229 CHKPF(asyncCallbackInfo);
230 CHKCF(length == RESULT_LENGTH, "Array length is different");
231 if (asyncCallbackInfo->error.code != SUCCESS) {
232 CHKCF(ConvertErrorToResult(env, asyncCallbackInfo, result[0]), "Create napi err fail in async work");
233 CHKCF((napi_get_undefined(env, &result[1]) == napi_ok), "napi_get_undefined fail");
234 } else {
235 CHKCF((napi_get_undefined(env, &result[0]) == napi_ok), "napi_get_undefined fail");
236 CHKCF((napi_get_undefined(env, &result[1]) == napi_ok), "napi_get_undefined fail");
237 }
238 return true;
239 }
240
ConstructIsSupportEffectResult(const napi_env & env,sptr<AsyncCallbackInfo> asyncCallbackInfo,napi_value result[],int32_t length)241 bool ConstructIsSupportEffectResult(const napi_env &env, sptr<AsyncCallbackInfo> asyncCallbackInfo,
242 napi_value result[], int32_t length)
243 {
244 CHKPF(asyncCallbackInfo);
245 CHKCF(length == RESULT_LENGTH, "Array length is different");
246 if (asyncCallbackInfo->error.code != SUCCESS) {
247 CHKCF(ConvertErrorToResult(env, asyncCallbackInfo, result[0]), "Create napi err fail in async work");
248 CHKCF((napi_get_undefined(env, &result[1]) == napi_ok), "napi_get_undefined fail");
249 } else {
250 CHKCF((napi_get_undefined(env, &result[0]) == napi_ok), "napi_get_undefined fail");
251 CHKCF((napi_get_boolean(env, asyncCallbackInfo->isSupportEffect, &result[1]) == napi_ok),
252 "napi_get_boolean fail");
253 }
254 return true;
255 }
256
EmitSystemCallback(const napi_env & env,sptr<AsyncCallbackInfo> asyncCallbackInfo)257 void EmitSystemCallback(const napi_env &env, sptr<AsyncCallbackInfo> asyncCallbackInfo)
258 {
259 CHKPV(asyncCallbackInfo);
260 if (asyncCallbackInfo->error.code == SUCCESS) {
261 CHKPV(asyncCallbackInfo->callback[0]);
262 napi_value callback = nullptr;
263 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, asyncCallbackInfo->callback[0], &callback));
264 napi_value result = nullptr;
265 NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &result));
266 napi_value callResult = nullptr;
267 NAPI_CALL_RETURN_VOID(env, napi_call_function(env, nullptr, callback, 1, &result, &callResult));
268 return;
269 }
270 CHKPV(asyncCallbackInfo->callback[1]);
271 napi_value callback = nullptr;
272 NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, asyncCallbackInfo->callback[1], &callback));
273 napi_value result[2] = {0};
274 NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, asyncCallbackInfo->error.message.data(),
275 NAPI_AUTO_LENGTH, &result[0]));
276 NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, asyncCallbackInfo->error.code, &result[1]));
277 napi_value callResult = nullptr;
278 NAPI_CALL_RETURN_VOID(env, napi_call_function(env, nullptr, callback, 1, result, &callResult));
279 }
280
EmitAsyncCallbackWork(sptr<AsyncCallbackInfo> asyncCallbackInfo)281 void EmitAsyncCallbackWork(sptr<AsyncCallbackInfo> asyncCallbackInfo)
282 {
283 CALL_LOG_ENTER;
284 CHKPV(asyncCallbackInfo);
285 CHKPV(asyncCallbackInfo->env);
286 napi_env env = asyncCallbackInfo->env;
287 napi_value resourceName = nullptr;
288 napi_status ret = napi_create_string_latin1(env, "AsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
289 CHKCV((ret == napi_ok), "napi_create_string_latin1 fail");
290 asyncCallbackInfo->IncStrongRef(nullptr);
291 napi_status status = napi_create_async_work(
292 env, nullptr, resourceName, [](napi_env env, void *data) {},
293 [](napi_env env, napi_status status, void *data) {
294 CALL_LOG_ENTER;
295 sptr<AsyncCallbackInfo> asyncCallbackInfo(static_cast<AsyncCallbackInfo *>(data));
296 /**
297 * After the asynchronous task is created, the asyncCallbackInfo reference count is reduced
298 * to 0 destruction, so you need to add 1 to the asyncCallbackInfo reference count when the
299 * asynchronous task is created, and subtract 1 from the reference count after the naked
300 * pointer is converted to a pointer when the asynchronous task is executed, the reference
301 * count of the smart pointer is guaranteed to be 1.
302 */
303 asyncCallbackInfo->DecStrongRef(nullptr);
304 if (asyncCallbackInfo->callbackType == SYSTEM_VIBRATE_CALLBACK) {
305 EmitSystemCallback(env, asyncCallbackInfo);
306 return;
307 }
308 CHKPV(asyncCallbackInfo->callback[0]);
309 napi_value callback = nullptr;
310 napi_status ret = napi_get_reference_value(env, asyncCallbackInfo->callback[0], &callback);
311 CHKCV((ret == napi_ok), "napi_get_reference_value fail");
312 napi_value result[RESULT_LENGTH] = { 0 };
313 CHKCV((g_convertFuncList.find(asyncCallbackInfo->callbackType) != g_convertFuncList.end()),
314 "Callback type invalid in async work");
315 bool state = g_convertFuncList[asyncCallbackInfo->callbackType](env, asyncCallbackInfo, result,
316 sizeof(result) / sizeof(napi_value));
317 CHKCV(state, "Create napi data fail in async work");
318 napi_value callResult = nullptr;
319 CHKCV((napi_call_function(env, nullptr, callback, 2, result, &callResult) == napi_ok),
320 "napi_call_function fail");
321 },
322 asyncCallbackInfo.GetRefPtr(), &asyncCallbackInfo->asyncWork);
323 if (status != napi_ok
324 || napi_queue_async_work_with_qos(
325 asyncCallbackInfo->env, asyncCallbackInfo->asyncWork, napi_qos_default) != napi_ok) {
326 MISC_HILOGE("Create async work fail");
327 asyncCallbackInfo->DecStrongRef(nullptr);
328 }
329 }
330
EmitPromiseWork(sptr<AsyncCallbackInfo> asyncCallbackInfo)331 void EmitPromiseWork(sptr<AsyncCallbackInfo> asyncCallbackInfo)
332 {
333 CALL_LOG_ENTER;
334 CHKPV(asyncCallbackInfo);
335 CHKPV(asyncCallbackInfo->env);
336 napi_value resourceName = nullptr;
337 napi_env env = asyncCallbackInfo->env;
338 napi_status ret = napi_create_string_latin1(env, "Promise", NAPI_AUTO_LENGTH, &resourceName);
339 CHKCV((ret == napi_ok), "napi_create_string_latin1 fail");
340 // Make the reference count of asyncCallbackInfo add 1, and the function exits the non-destructor
341 asyncCallbackInfo->IncStrongRef(nullptr);
342 napi_status status = napi_create_async_work(
343 env, nullptr, resourceName, [](napi_env env, void *data) {},
344 [](napi_env env, napi_status status, void *data) {
345 CALL_LOG_ENTER;
346 sptr<AsyncCallbackInfo> asyncCallbackInfo(static_cast<AsyncCallbackInfo *>(data));
347 /**
348 * After the asynchronous task is created, the asyncCallbackInfo reference count is reduced
349 * to 0 destruction, so you need to add 1 to the asyncCallbackInfo reference count when the
350 * asynchronous task is created, and subtract 1 from the reference count after the naked
351 * pointer is converted to a pointer when the asynchronous task is executed, the reference
352 * count of the smart pointer is guaranteed to be 1.
353 */
354 asyncCallbackInfo->DecStrongRef(nullptr);
355 CHKPV(asyncCallbackInfo->deferred);
356 if (asyncCallbackInfo->callbackType == SYSTEM_VIBRATE_CALLBACK) {
357 EmitSystemCallback(env, asyncCallbackInfo);
358 return;
359 }
360 napi_value result[RESULT_LENGTH] = { 0 };
361 CHKCV((g_convertFuncList.find(asyncCallbackInfo->callbackType) != g_convertFuncList.end()),
362 "Callback type invalid in promise");
363 bool ret = g_convertFuncList[asyncCallbackInfo->callbackType](env, asyncCallbackInfo, result,
364 sizeof(result) / sizeof(napi_value));
365 CHKCV(ret, "Callback type invalid in promise");
366 if (asyncCallbackInfo->error.code != SUCCESS) {
367 CHKCV((napi_reject_deferred(env, asyncCallbackInfo->deferred, result[0]) == napi_ok),
368 "napi_reject_deferred fail");
369 } else {
370 CHKCV((napi_resolve_deferred(env, asyncCallbackInfo->deferred, result[1]) == napi_ok),
371 "napi_resolve_deferred fail");
372 }
373 }, asyncCallbackInfo.GetRefPtr(), &asyncCallbackInfo->asyncWork);
374 if (status != napi_ok
375 || napi_queue_async_work_with_qos(
376 asyncCallbackInfo->env, asyncCallbackInfo->asyncWork, napi_qos_default) != napi_ok) {
377 MISC_HILOGE("Create async work fail");
378 asyncCallbackInfo->DecStrongRef(nullptr);
379 }
380 }
381 } // namespace Sensors
382 } // namespace OHOS
383