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, &paramType);
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