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 #include "ringtone_player_callback_napi.h"
16
17 #include <uv.h>
18
19 #include "media_errors.h"
20 #include "media_log.h"
21
22 using OHOS::HiviewDFX::HiLog;
23 using OHOS::HiviewDFX::HiLogLabel;
24
25 namespace {
26 const std::string AUDIO_INTERRUPT_CALLBACK_NAME = "audioInterrupt";
27
28 constexpr HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "RingtonePlayerCallbackNapi"};
29 }
30
31 namespace OHOS {
32 namespace Media {
RingtonePlayerCallbackNapi(napi_env env)33 RingtonePlayerCallbackNapi::RingtonePlayerCallbackNapi(napi_env env)
34 : env_(env)
35 {
36 MEDIA_LOGI("RingtonePlayerCallbackNapi: instance create");
37 }
38
~RingtonePlayerCallbackNapi()39 RingtonePlayerCallbackNapi::~RingtonePlayerCallbackNapi()
40 {
41 MEDIA_LOGI("RingtonePlayerCallbackNapi: instance destroy");
42 }
43
SaveCallbackReference(const std::string & callbackName,napi_value args)44 void RingtonePlayerCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
45 {
46 std::lock_guard<std::mutex> lock(mutex_);
47 napi_ref callback = nullptr;
48 const int32_t refCount = 1;
49 napi_status status = napi_create_reference(env_, args, refCount, &callback);
50 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
51 "RingtonePlayerCallbackNapi: creating reference for callback fail");
52
53 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
54 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
55 interruptCallback_ = cb;
56 } else {
57 MEDIA_LOGE("RingtonePlayerCallbackNapi: Unknown callback type: %{public}s", callbackName.c_str());
58 }
59 }
60
RemoveCallbackReference(const std::string & callbackName)61 void RingtonePlayerCallbackNapi::RemoveCallbackReference(const std::string &callbackName)
62 {
63 std::lock_guard<std::mutex> lock(mutex_);
64
65 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
66 interruptCallback_ = nullptr;
67 } else {
68 MEDIA_LOGE("Unknown callback type: %{public}s", callbackName.c_str());
69 }
70 }
71
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)72 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
73 {
74 napi_handle_scope scope = nullptr;
75 napi_open_handle_scope(env, &scope);
76
77 napi_value value = nullptr;
78 napi_create_int32(env, intValue, &value);
79 napi_set_named_property(env, result, fieldStr.c_str(), value);
80
81 napi_close_handle_scope(env, scope);
82 }
83
NativeInterruptEventToJsObj(const napi_env & env,napi_value & jsObj,const AudioStandard::InterruptEvent & interruptEvent)84 static void NativeInterruptEventToJsObj(const napi_env& env, napi_value& jsObj,
85 const AudioStandard::InterruptEvent& interruptEvent)
86 {
87 napi_create_object(env, &jsObj);
88 SetValueInt32(env, "eventType", static_cast<int32_t>(interruptEvent.eventType), jsObj);
89 SetValueInt32(env, "forceType", static_cast<int32_t>(interruptEvent.forceType), jsObj);
90 SetValueInt32(env, "hintType", static_cast<int32_t>(interruptEvent.hintType), jsObj);
91 }
92
OnInterrupt(const AudioStandard::InterruptEvent & interruptEvent)93 void RingtonePlayerCallbackNapi::OnInterrupt(const AudioStandard::InterruptEvent &interruptEvent)
94 {
95 std::lock_guard<std::mutex> lock(mutex_);
96 MEDIA_LOGI("RingtonePlayerCallbackNapi: OnInterrupt is called");
97 MEDIA_LOGI("RingtonePlayerCallbackNapi: hintType: %{public}d", interruptEvent.hintType);
98 CHECK_AND_RETURN_LOG(interruptCallback_ != nullptr, "Cannot find the reference of interrupt callback");
99
100 std::unique_ptr<RingtonePlayerJsCallback> cb = std::make_unique<RingtonePlayerJsCallback>();
101 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
102 cb->callback = interruptCallback_;
103 cb->callbackName = AUDIO_INTERRUPT_CALLBACK_NAME;
104 cb->interruptEvent = interruptEvent;
105 return OnJsCallbackInterrupt(cb);
106 }
107
OnJsCallbackInterrupt(std::unique_ptr<RingtonePlayerJsCallback> & jsCb)108 void RingtonePlayerCallbackNapi::OnJsCallbackInterrupt(std::unique_ptr<RingtonePlayerJsCallback> &jsCb)
109 {
110 uv_loop_s *loop = nullptr;
111 napi_get_uv_event_loop(env_, &loop);
112 CHECK_AND_RETURN(loop != nullptr);
113
114 uv_work_t *work = new(std::nothrow) uv_work_t;
115 CHECK_AND_RETURN_LOG(work != nullptr, "RingtonePlayerCallbackNapi: OnJsCallBackInterrupt: No memory");
116 if (jsCb.get() == nullptr) {
117 MEDIA_LOGE("RingtonePlayerCallbackNapi: OnJsCallBackInterrupt: jsCb.get() is null");
118 delete work;
119 return;
120 }
121 work->data = reinterpret_cast<void *>(jsCb.get());
122
123 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
124 // Js Thread
125 RingtonePlayerJsCallback *event = reinterpret_cast<RingtonePlayerJsCallback *>(work->data);
126 std::string request = event->callbackName;
127 napi_env env = event->callback->env_;
128 napi_ref callback = event->callback->cb_;
129 napi_handle_scope scope = nullptr;
130 napi_open_handle_scope(env, &scope);
131 MEDIA_LOGI("RingtonePlayerCallbackNapi: JsCallBack %{public}s, uv_queue_work start", request.c_str());
132 do {
133 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s cancelled", request.c_str());
134
135 napi_value jsCallback = nullptr;
136 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
137 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
138 request.c_str());
139
140 // Call back function
141 napi_value args[1] = { nullptr };
142 NativeInterruptEventToJsObj(env, args[0], event->interruptEvent);
143 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
144 "%{public}s fail to create Interrupt callback", request.c_str());
145
146 const size_t argCount = 1;
147 napi_value result = nullptr;
148 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
149 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
150 } while (0);
151 napi_close_handle_scope(env, scope);
152 delete event;
153 delete work;
154 });
155 if (ret != 0) {
156 MEDIA_LOGE("Failed to execute libuv work queue");
157 delete work;
158 } else {
159 jsCb.release();
160 }
161 }
162 } // namespace Media
163 } // namespace OHOS
164