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