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 "audio_haptic_player_napi.h"
17
18 #include "audio_haptic_log.h"
19
20 namespace {
21 /* Constants for array index */
22 const int32_t PARAM0 = 0;
23 const int32_t PARAM1 = 1;
24
25 /* Constants for array size */
26 const int32_t ARGS_ONE = 1;
27 const int32_t ARGS_TWO = 2;
28
29 const std::string AUDIO_INTERRUPT_CALLBACK_NAME = "audioInterrupt";
30 const std::string END_OF_STREAM_CALLBACK_NAME = "endOfStream";
31
32 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "AudioHapticPlayerNapi"};
33 }
34
35 namespace OHOS {
36 namespace Media {
37 thread_local napi_ref AudioHapticPlayerNapi::sConstructor_ = nullptr;
38 std::shared_ptr<AudioHapticPlayer> AudioHapticPlayerNapi::sAudioHapticPlayer_ = nullptr;
39
AudioHapticPlayerNapi()40 AudioHapticPlayerNapi::AudioHapticPlayerNapi() : env_(nullptr) {}
41
42 AudioHapticPlayerNapi::~AudioHapticPlayerNapi() = default;
43
Init(napi_env env,napi_value exports)44 napi_value AudioHapticPlayerNapi::Init(napi_env env, napi_value exports)
45 {
46 napi_status status;
47 napi_value ctorObj;
48 int32_t refCount = 1;
49
50 napi_property_descriptor audioHapticPlayerProp[] = {
51 DECLARE_NAPI_FUNCTION("isMuted", IsMuted),
52 DECLARE_NAPI_FUNCTION("start", Start),
53 DECLARE_NAPI_FUNCTION("stop", Stop),
54 DECLARE_NAPI_FUNCTION("release", Release),
55 DECLARE_NAPI_FUNCTION("on", On),
56 DECLARE_NAPI_FUNCTION("off", Off),
57 };
58
59 status = napi_define_class(env, AUDIO_HAPTIC_PLAYER_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor,
60 nullptr, sizeof(audioHapticPlayerProp) / sizeof(audioHapticPlayerProp[0]), audioHapticPlayerProp, &ctorObj);
61 if (status == napi_ok) {
62 if (napi_create_reference(env, ctorObj, refCount, &sConstructor_) == napi_ok) {
63 status = napi_set_named_property(env, exports, AUDIO_HAPTIC_PLAYER_NAPI_CLASS_NAME.c_str(), ctorObj);
64 if (status == napi_ok) {
65 return exports;
66 }
67 }
68 }
69
70 return nullptr;
71 }
72
Constructor(napi_env env,napi_callback_info info)73 napi_value AudioHapticPlayerNapi::Constructor(napi_env env, napi_callback_info info)
74 {
75 napi_status status;
76 napi_value result = nullptr;
77 napi_value thisVar = nullptr;
78
79 napi_get_undefined(env, &result);
80 status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
81 if (status == napi_ok && thisVar != nullptr) {
82 std::unique_ptr<AudioHapticPlayerNapi> obj = std::make_unique<AudioHapticPlayerNapi>();
83 if (obj != nullptr) {
84 obj->env_ = env;
85 if (obj->sAudioHapticPlayer_ != nullptr) {
86 obj->audioHapticPlayer_ = move(obj->sAudioHapticPlayer_);
87 } else {
88 MEDIA_LOGE("Failed to create sAudioHapticPlayer_ instance.");
89 return result;
90 }
91
92 if (obj->audioHapticPlayer_ != nullptr && obj->callbackNapi_ == nullptr) {
93 obj->callbackNapi_ = std::make_shared<AudioHapticPlayerCallbackNapi>(env);
94 CHECK_AND_RETURN_RET_LOG(obj->callbackNapi_ != nullptr, result, "No memory");
95 int32_t ret = obj->audioHapticPlayer_->SetAudioHapticPlayerCallback(obj->callbackNapi_);
96 MEDIA_LOGI("Constructor: SetAudioHapticPlayerCallback %{public}s",
97 ret == 0 ? "succeess" : "failed");
98 }
99
100 status = napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()),
101 AudioHapticPlayerNapi::Destructor, nullptr, nullptr);
102 if (status == napi_ok) {
103 obj.release();
104 return thisVar;
105 } else {
106 MEDIA_LOGE("Failed to wrap the native audioHapticPlayer object with JS.");
107 }
108 }
109 }
110
111 return result;
112 }
113
Destructor(napi_env env,void * nativeObject,void * finalize_hint)114 void AudioHapticPlayerNapi::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
115 {
116 AudioHapticPlayerNapi *audioHapticPlayerHelper = reinterpret_cast<AudioHapticPlayerNapi*>(nativeObject);
117 if (audioHapticPlayerHelper != nullptr) {
118 audioHapticPlayerHelper->~AudioHapticPlayerNapi();
119 }
120 }
121
CreatePlayerInstance(napi_env env,std::shared_ptr<AudioHapticPlayer> & audioHapticPlayer)122 napi_value AudioHapticPlayerNapi::CreatePlayerInstance(napi_env env,
123 std::shared_ptr<AudioHapticPlayer> &audioHapticPlayer)
124 {
125 napi_status status;
126 napi_value result = nullptr;
127 napi_value ctor;
128
129 status = napi_get_reference_value(env, sConstructor_, &ctor);
130 if (status == napi_ok) {
131 sAudioHapticPlayer_ = audioHapticPlayer;
132 status = napi_new_instance(env, ctor, 0, nullptr, &result);
133 if (status == napi_ok) {
134 return result;
135 } else {
136 MEDIA_LOGE("CreatePlayerInstance: New instance could not be obtained.");
137 }
138 }
139
140 napi_get_undefined(env, &result);
141 return result;
142 }
143
IsMuted(napi_env env,napi_callback_info info)144 napi_value AudioHapticPlayerNapi::IsMuted(napi_env env, napi_callback_info info)
145 {
146 napi_value result = nullptr;
147 napi_get_boolean(env, false, &result);
148
149 size_t argc = ARGS_ONE;
150 napi_value argv[ARGS_ONE] = {0};
151 napi_value thisVar = nullptr;
152
153 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
154 CHECK_AND_RETURN_RET_LOG(status == napi_ok && thisVar != nullptr, result, "IsMuted: napi_get_cb_info fail");
155 if (argc != ARGS_ONE) {
156 MEDIA_LOGE("IsMuted: requires 1 parameters");
157 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID, "mandatory parameters are left unspecified");
158 return result;
159 }
160
161 void *native = nullptr;
162 status = napi_unwrap(env, thisVar, &native);
163 auto *audioHapticPlayerNapi = reinterpret_cast<AudioHapticPlayerNapi *>(native);
164 if (status != napi_ok || audioHapticPlayerNapi == nullptr) {
165 MEDIA_LOGE("IsMuted: unwrap failure!");
166 return result;
167 }
168
169 napi_valuetype valueType = napi_undefined;
170 napi_typeof(env, argv[PARAM0], &valueType);
171 int32_t jsAudioHapticType = -1;
172 if (valueType == napi_number) {
173 napi_get_value_int32(env, argv[PARAM0], &jsAudioHapticType);
174 }
175 if (!IsLegalAudioHapticType(jsAudioHapticType)) {
176 MEDIA_LOGE("IsMuted: the param type mismatch");
177 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID,
178 "parameter verification failed: The param of type must be enum AudioHapticType");
179 return result;
180 }
181
182 AudioHapticType audioHapticType = static_cast<AudioHapticType>(jsAudioHapticType);
183 bool isMuted = audioHapticPlayerNapi->audioHapticPlayer_->IsMuted(audioHapticType);
184 napi_get_boolean(env, isMuted, &result);
185
186 return result;
187 }
188
IsLegalAudioHapticType(int32_t audioHapticType)189 bool AudioHapticPlayerNapi::IsLegalAudioHapticType(int32_t audioHapticType)
190 {
191 switch (audioHapticType) {
192 case AUDIO_HAPTIC_TYPE_AUDIO:
193 case AUDIO_HAPTIC_TYPE_HAPTIC:
194 return true;
195 default:
196 break;
197 }
198 MEDIA_LOGE("IsLegalAudioHapticType: audioHapticType %{public}d is invalid", audioHapticType);
199 return false;
200 }
201
CommonAsyncCallbackComp(napi_env env,napi_status status,void * data)202 void AudioHapticPlayerNapi::CommonAsyncCallbackComp(napi_env env, napi_status status, void *data)
203 {
204 auto context = static_cast<AudioHapticPlayerAsyncContext *>(data);
205 napi_value result[2] = {};
206
207 napi_get_undefined(env, &result[PARAM1]);
208 if (!context->status) {
209 napi_get_undefined(env, &result[PARAM0]);
210 } else {
211 napi_value message = nullptr;
212 napi_create_string_utf8(env, "Error: Operation is not supported or failed", NAPI_AUTO_LENGTH, &message);
213 napi_create_error(env, nullptr, message, &result[PARAM0]);
214 }
215
216 if (context->deferred) {
217 if (!context->status) {
218 napi_resolve_deferred(env, context->deferred, result[PARAM1]);
219 } else {
220 napi_reject_deferred(env, context->deferred, result[PARAM0]);
221 }
222 }
223 napi_delete_async_work(env, context->work);
224
225 delete context;
226 context = nullptr;
227 }
228
Start(napi_env env,napi_callback_info info)229 napi_value AudioHapticPlayerNapi::Start(napi_env env, napi_callback_info info)
230 {
231 napi_value result = nullptr;
232 napi_value resource = nullptr;
233 size_t argc = ARGS_ONE;
234 napi_value argv[ARGS_ONE] = {0};
235 napi_value thisVar = nullptr;
236
237 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
238 napi_get_undefined(env, &result);
239 CHECK_AND_RETURN_RET_LOG(status == napi_ok && thisVar != nullptr, result, "Start: napi_get_cb_info fail");
240 if (argc != 0) {
241 MEDIA_LOGE("Start: requires 0 parameters");
242 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
243 return result;
244 }
245
246 std::unique_ptr<AudioHapticPlayerAsyncContext> asyncContext = std::make_unique<AudioHapticPlayerAsyncContext>();
247 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
248 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
249 napi_create_promise(env, &asyncContext->deferred, &result);
250 napi_create_string_utf8(env, "Start", NAPI_AUTO_LENGTH, &resource);
251 status = napi_create_async_work(env, nullptr, resource,
252 [](napi_env env, void *data) {
253 AudioHapticPlayerAsyncContext *context = static_cast<AudioHapticPlayerAsyncContext*>(data);
254 context->status = context->objectInfo->audioHapticPlayer_->Start();
255 },
256 CommonAsyncCallbackComp, static_cast<void*>(asyncContext.get()), &asyncContext->work);
257 if (status != napi_ok) {
258 MEDIA_LOGE("Start: Failed to get create async work");
259 napi_get_undefined(env, &result);
260 } else {
261 napi_queue_async_work(env, asyncContext->work);
262 asyncContext.release();
263 }
264 }
265
266 return result;
267 }
268
Stop(napi_env env,napi_callback_info info)269 napi_value AudioHapticPlayerNapi::Stop(napi_env env, napi_callback_info info)
270 {
271 napi_value result = nullptr;
272 napi_value resource = nullptr;
273 size_t argc = ARGS_ONE;
274 napi_value argv[ARGS_ONE] = {0};
275 napi_value thisVar = nullptr;
276
277 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
278 napi_get_undefined(env, &result);
279 CHECK_AND_RETURN_RET_LOG(status == napi_ok && thisVar != nullptr, result, "Stop: napi_get_cb_info fail");
280 if (argc != 0) {
281 MEDIA_LOGE("Stop: requires 0 parameters");
282 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
283 return result;
284 }
285
286 std::unique_ptr<AudioHapticPlayerAsyncContext> asyncContext = std::make_unique<AudioHapticPlayerAsyncContext>();
287 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
288 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
289 napi_create_promise(env, &asyncContext->deferred, &result);
290 napi_create_string_utf8(env, "Stop", NAPI_AUTO_LENGTH, &resource);
291 status = napi_create_async_work(env, nullptr, resource,
292 [](napi_env env, void *data) {
293 AudioHapticPlayerAsyncContext *context = static_cast<AudioHapticPlayerAsyncContext*>(data);
294 context->status = context->objectInfo->audioHapticPlayer_->Stop();
295 },
296 CommonAsyncCallbackComp, static_cast<void*>(asyncContext.get()), &asyncContext->work);
297 if (status != napi_ok) {
298 MEDIA_LOGE("Stop: Failed to get create async work");
299 napi_get_undefined(env, &result);
300 } else {
301 napi_queue_async_work(env, asyncContext->work);
302 asyncContext.release();
303 }
304 }
305
306 return result;
307 }
308
Release(napi_env env,napi_callback_info info)309 napi_value AudioHapticPlayerNapi::Release(napi_env env, napi_callback_info info)
310 {
311 napi_value result = nullptr;
312 napi_value resource = nullptr;
313 size_t argc = ARGS_ONE;
314 napi_value argv[ARGS_ONE] = {0};
315 napi_value thisVar = nullptr;
316
317 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
318 napi_get_undefined(env, &result);
319 CHECK_AND_RETURN_RET_LOG(status == napi_ok && thisVar != nullptr, result, "Release: napi_get_cb_info fail");
320 if (argc != 0) {
321 MEDIA_LOGE("Release: requires 0 parameters");
322 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
323 return result;
324 }
325
326 std::unique_ptr<AudioHapticPlayerAsyncContext> asyncContext = std::make_unique<AudioHapticPlayerAsyncContext>();
327 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
328 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
329 napi_create_promise(env, &asyncContext->deferred, &result);
330 napi_create_string_utf8(env, "Release", NAPI_AUTO_LENGTH, &resource);
331 status = napi_create_async_work(env, nullptr, resource,
332 [](napi_env env, void *data) {
333 AudioHapticPlayerAsyncContext *context = static_cast<AudioHapticPlayerAsyncContext*>(data);
334 context->status = context->objectInfo->audioHapticPlayer_->Release();
335 },
336 CommonAsyncCallbackComp, static_cast<void*>(asyncContext.get()), &asyncContext->work);
337 if (status != napi_ok) {
338 MEDIA_LOGE("Release: Failed to get create async work");
339 napi_get_undefined(env, &result);
340 } else {
341 napi_queue_async_work(env, asyncContext->work);
342 asyncContext.release();
343 }
344 }
345
346 return result;
347 }
348
On(napi_env env,napi_callback_info info)349 napi_value AudioHapticPlayerNapi::On(napi_env env, napi_callback_info info)
350 {
351 napi_value result = nullptr;
352 size_t argc = ARGS_TWO;
353 napi_value argv[ARGS_TWO] = {nullptr};
354 napi_value jsThis = nullptr;
355 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
356 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, result, "On: napi_get_cb_info fail");
357 if (argc != ARGS_TWO) {
358 MEDIA_LOGE("On: requires 2 parameters");
359 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
360 return result;
361 }
362
363 napi_valuetype argvType = napi_undefined;
364 napi_typeof(env, argv[PARAM0], &argvType);
365 if (argvType != napi_string) {
366 MEDIA_LOGE("On: the first parameter must be napi_string");
367 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
368 return result;
369 }
370 std::string callbackName = AudioHapticCommonNapi::GetStringArgument(env, argv[0]);
371 MEDIA_LOGI("On: callbackName: %{public}s", callbackName.c_str());
372
373 napi_typeof(env, argv[PARAM1], &argvType);
374 if (argvType != napi_function) {
375 MEDIA_LOGE("On: the second parameter must be napi_function");
376 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
377 return result;
378 }
379
380 return RegisterCallback(env, jsThis, argv, callbackName);
381 }
382
RegisterCallback(napi_env env,napi_value jsThis,napi_value * argv,const std::string & cbName)383 napi_value AudioHapticPlayerNapi::RegisterCallback(napi_env env, napi_value jsThis, napi_value* argv,
384 const std::string& cbName)
385 {
386 napi_value result = nullptr;
387 napi_get_undefined(env, &result);
388
389 AudioHapticPlayerNapi *audioHapticPlayerNapi = nullptr;
390 napi_status status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&audioHapticPlayerNapi));
391 if (status != napi_ok || audioHapticPlayerNapi == nullptr || audioHapticPlayerNapi->audioHapticPlayer_ == nullptr) {
392 MEDIA_LOGE("RegisterCallback: Failed to get audioHapticPlayer_");
393 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_OPERATE_NOT_ALLOWED);
394 return result;
395 }
396
397 if (!cbName.compare(AUDIO_INTERRUPT_CALLBACK_NAME) ||
398 !cbName.compare(END_OF_STREAM_CALLBACK_NAME)) {
399 result = RegisterAudioHapticPlayerCallback(env, argv, cbName, audioHapticPlayerNapi);
400 } else {
401 MEDIA_LOGE("RegisterCallback: the callback name: %{public}s is not supported", cbName.c_str());
402 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
403 return result;
404 }
405
406 return result;
407 }
408
RegisterAudioHapticPlayerCallback(napi_env env,napi_value * argv,const std::string & cbName,AudioHapticPlayerNapi * audioHapticPlayerNapi)409 napi_value AudioHapticPlayerNapi::RegisterAudioHapticPlayerCallback(napi_env env, napi_value* argv,
410 const std::string& cbName, AudioHapticPlayerNapi *audioHapticPlayerNapi)
411 {
412 napi_value result = nullptr;
413 napi_get_undefined(env, &result);
414
415 if (audioHapticPlayerNapi->callbackNapi_ == nullptr) {
416 MEDIA_LOGE("RegisterAudioHapticPlayerCallback: callbackNapi_ is null");
417 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_OPERATE_NOT_ALLOWED);
418 return result;
419 }
420 audioHapticPlayerNapi->callbackNapi_->SaveCallbackReference(cbName, argv[PARAM1]);
421
422 return result;
423 }
424
Off(napi_env env,napi_callback_info info)425 napi_value AudioHapticPlayerNapi::Off(napi_env env, napi_callback_info info)
426 {
427 napi_value result = nullptr;
428 size_t argc = ARGS_TWO;
429 napi_value argv[ARGS_TWO] = {nullptr};
430 napi_value jsThis = nullptr;
431 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
432 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, result, "Off: napi_get_cb_info fail");
433 if (argc != ARGS_ONE && argc != ARGS_TWO) {
434 MEDIA_LOGE("Off: requires 1 or 2 parameters");
435 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
436 return result;
437 }
438
439 napi_valuetype argvType = napi_undefined;
440 napi_typeof(env, argv[PARAM0], &argvType);
441 if (argvType != napi_string) {
442 MEDIA_LOGE("Off: the parameter must be napi_string");
443 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
444 return result;
445 }
446 std::string callbackName = AudioHapticCommonNapi::GetStringArgument(env, argv[0]);
447 MEDIA_LOGI("Off: callbackName: %{public}s", callbackName.c_str());
448
449 return UnregisterCallback(env, jsThis, callbackName);
450 }
451
UnregisterCallback(napi_env env,napi_value jsThis,const std::string & cbName)452 napi_value AudioHapticPlayerNapi::UnregisterCallback(napi_env env, napi_value jsThis, const std::string &cbName)
453 {
454 napi_value result = nullptr;
455 napi_get_undefined(env, &result);
456
457 AudioHapticPlayerNapi *audioHapticPlayerNapi = nullptr;
458 napi_status status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&audioHapticPlayerNapi));
459 if (status != napi_ok || audioHapticPlayerNapi == nullptr || audioHapticPlayerNapi->audioHapticPlayer_ == nullptr) {
460 MEDIA_LOGE("UnregisterCallback: Failed to get audioHapticPlayer_");
461 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_OPERATE_NOT_ALLOWED);
462 return result;
463 }
464
465 if (!cbName.compare(AUDIO_INTERRUPT_CALLBACK_NAME) ||
466 !cbName.compare(END_OF_STREAM_CALLBACK_NAME)) {
467 result = UnregisterAudioHapticPlayerCallback(env, cbName, audioHapticPlayerNapi);
468 } else {
469 MEDIA_LOGE("UnregisterCallback: the callback name: %{public}s is not supported", cbName.c_str());
470 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_INPUT_INVALID);
471 return result;
472 }
473
474 return result;
475 }
476
UnregisterAudioHapticPlayerCallback(napi_env env,const std::string & cbName,AudioHapticPlayerNapi * audioHapticPlayerNapi)477 napi_value AudioHapticPlayerNapi::UnregisterAudioHapticPlayerCallback(napi_env env, const std::string& cbName,
478 AudioHapticPlayerNapi *audioHapticPlayerNapi)
479 {
480 napi_value result = nullptr;
481 napi_get_undefined(env, &result);
482
483 if (audioHapticPlayerNapi->callbackNapi_ == nullptr) {
484 MEDIA_LOGE("RegisterAudioHapticPlayerCallback: callbackNapi_ is null");
485 AudioHapticCommonNapi::ThrowError(env, NAPI_ERR_OPERATE_NOT_ALLOWED);
486 return result;
487 }
488 audioHapticPlayerNapi->callbackNapi_->RemoveCallbackReference(cbName);
489
490 return result;
491 }
492 } // namespace Media
493 } // namespace OHOS