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 #ifndef LOG_TAG
16 #define LOG_TAG "NapiAudioCapturerCallback"
17 #endif
18
19 #include "js_native_api.h"
20 #include "napi_audio_capturer_callbacks.h"
21 #include "napi_param_utils.h"
22 #include "audio_errors.h"
23 #include "audio_capturer_log.h"
24
25 namespace OHOS {
26 namespace AudioStandard {
NapiAudioCapturerCallback(napi_env env)27 NapiAudioCapturerCallback::NapiAudioCapturerCallback(napi_env env)
28 : env_(env)
29 {
30 AUDIO_DEBUG_LOG("NapiAudioCapturerCallback: instance create");
31 }
32
~NapiAudioCapturerCallback()33 NapiAudioCapturerCallback::~NapiAudioCapturerCallback()
34 {
35 if (regAcStateChgTsfn_) {
36 napi_release_threadsafe_function(acStateChgTsfn_, napi_tsfn_abort);
37 } else if (regAcInterruptTsfn_) {
38 napi_release_threadsafe_function(acInterruptTsfn_, napi_tsfn_abort);
39 }
40 AUDIO_DEBUG_LOG("NapiAudioCapturerCallback: instance destroy");
41 }
42
SaveCallbackReference(const std::string & callbackName,napi_value args)43 void NapiAudioCapturerCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
44 {
45 std::lock_guard<std::mutex> lock(mutex_);
46 // create function that will operate while save callback reference success.
47 std::function<void(std::shared_ptr<AutoRef> generatedCallback)> successed =
48 [this, callbackName](std::shared_ptr<AutoRef> generatedCallback) {
49 if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
50 interruptCallback_ = generatedCallback;
51 return;
52 }
53 if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
54 stateChangeCallback_ = generatedCallback;
55 return;
56 }
57 };
58 NapiAudioCapturerCallbackInner::SaveCallbackReferenceInner(callbackName, args, successed);
59 }
60
CreateStateChangeTsfn(napi_env env)61 void NapiAudioCapturerCallback::CreateStateChangeTsfn(napi_env env)
62 {
63 regAcStateChgTsfn_ = true;
64 napi_value cbName;
65 std::string callbackName = "StateChange";
66 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
67 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
68 StateChangeTsfnFinalize, nullptr, SafeJsCallbackStateChangeWork, &acStateChgTsfn_);
69 }
70
CreateInterruptTsfn(napi_env env)71 void NapiAudioCapturerCallback::CreateInterruptTsfn(napi_env env)
72 {
73 regAcInterruptTsfn_ = true;
74 napi_value cbName;
75 std::string callbackName = "captureInterrupt";
76 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
77 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
78 InterruptTsfnFinalize, nullptr, SafeJsCallbackInterruptWork, &acInterruptTsfn_);
79 }
80
GetStateChangeTsfnFlag()81 bool NapiAudioCapturerCallback::GetStateChangeTsfnFlag()
82 {
83 return regAcStateChgTsfn_;
84 }
85
GetInterruptTsfnFlag()86 bool NapiAudioCapturerCallback::GetInterruptTsfnFlag()
87 {
88 return regAcInterruptTsfn_;
89 }
90
GetCallback(const std::string & callbackName)91 std::shared_ptr<AutoRef> NapiAudioCapturerCallback::GetCallback(const std::string &callbackName)
92 {
93 std::shared_ptr<AutoRef> cb = nullptr;
94
95 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
96 return interruptCallback_;
97 }
98 if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
99 return stateChangeCallback_;
100 }
101 AUDIO_ERR_LOG("NapiAudioCapturerCallback->GetCallback Unknown callback type: %{public}s", callbackName.c_str());
102 return cb;
103 }
104
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback)105 void NapiAudioCapturerCallback::RemoveCallbackReference(const std::string &callbackName, napi_env env,
106 napi_value callback)
107 {
108 std::lock_guard<std::mutex> lock(mutex_);
109 // create function that will operate while save callback reference success.
110 std::function<void()> successed =
111 [this, callbackName]() {
112 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
113 interruptCallback_ = nullptr;
114 return;
115 }
116 if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
117 stateChangeCallback_ = nullptr;
118 return;
119 }
120 };
121 RemoveCallbackReferenceInner(callbackName, env, callback, successed);
122 }
123
OnInterrupt(const InterruptEvent & interruptEvent)124 void NapiAudioCapturerCallback::OnInterrupt(const InterruptEvent &interruptEvent)
125 {
126 std::lock_guard<std::mutex> lock(mutex_);
127 AUDIO_DEBUG_LOG("NapiAudioCapturerCallback: OnInterrupt is called, hintType: %{public}d", interruptEvent.hintType);
128 CHECK_AND_RETURN_LOG(interruptCallback_ != nullptr, "Cannot find the reference of interrupt callback");
129
130 std::unique_ptr<AudioCapturerJsCallback> cb = std::make_unique<AudioCapturerJsCallback>();
131 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
132 cb->callback = interruptCallback_;
133 cb->callbackName = INTERRUPT_CALLBACK_NAME;
134 cb->interruptEvent = interruptEvent;
135 return OnJsCallbackInterrupt(cb);
136 }
137
SafeJsCallbackInterruptWork(napi_env env,napi_value js_cb,void * context,void * data)138 void NapiAudioCapturerCallback::SafeJsCallbackInterruptWork(napi_env env, napi_value js_cb, void *context, void *data)
139 {
140 AudioCapturerJsCallback *event = reinterpret_cast<AudioCapturerJsCallback *>(data);
141 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
142 "OnJsCallbackInterrupt: no memory");
143 std::shared_ptr<AudioCapturerJsCallback> safeContext(
144 static_cast<AudioCapturerJsCallback*>(data),
145 [](AudioCapturerJsCallback *ptr) {
146 delete ptr;
147 });
148 std::string request = event->callbackName;
149 napi_ref callback = event->callback->cb_;
150 napi_handle_scope scope = nullptr;
151 napi_open_handle_scope(env, &scope);
152 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
153 AUDIO_INFO_LOG("SafeJsCallbackInterruptWork: safe capture interrupt callback working.");
154 do {
155 napi_value jsCallback = nullptr;
156 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
157 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
158 request.c_str());
159 napi_value args[ARGS_ONE] = { nullptr };
160 NapiParamUtils::SetInterruptEvent(env, event->interruptEvent, args[PARAM0]);
161 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
162 "%{public}s fail to create Interrupt callback", request.c_str());
163 const size_t argCount = ARGS_ONE;
164 napi_value result = nullptr;
165 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
166 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
167 } while (0);
168 napi_close_handle_scope(env, scope);
169 }
170
InterruptTsfnFinalize(napi_env env,void * data,void * hint)171 void NapiAudioCapturerCallback::InterruptTsfnFinalize(napi_env env, void *data, void *hint)
172 {
173 AUDIO_INFO_LOG("InterruptTsfnFinalize: safe thread resource release.");
174 }
175
OnJsCallbackInterrupt(std::unique_ptr<AudioCapturerJsCallback> & jsCb)176 void NapiAudioCapturerCallback::OnJsCallbackInterrupt(std::unique_ptr<AudioCapturerJsCallback> &jsCb)
177 {
178 if (jsCb.get() == nullptr) {
179 AUDIO_ERR_LOG("OnJsCallBackInterrupt: jsCb.get() is null");
180 return;
181 }
182
183 AudioCapturerJsCallback *event = jsCb.release();
184 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
185 "OnJsCallbackInterrupt: event is nullptr.");
186
187 napi_acquire_threadsafe_function(acInterruptTsfn_);
188 napi_call_threadsafe_function(acInterruptTsfn_, event, napi_tsfn_blocking);
189 }
190
OnStateChange(const CapturerState state)191 void NapiAudioCapturerCallback::OnStateChange(const CapturerState state)
192 {
193 std::lock_guard<std::mutex> lock(mutex_);
194 AUDIO_DEBUG_LOG("NapiAudioCapturOnStateChange is called,Callback: state: %{public}d", state);
195 CHECK_AND_RETURN_LOG(stateChangeCallback_ != nullptr, "Cannot find the reference of stateChange callback");
196
197 std::unique_ptr<AudioCapturerJsCallback> cb = std::make_unique<AudioCapturerJsCallback>();
198 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
199 cb->callback = stateChangeCallback_;
200 cb->callbackName = STATE_CHANGE_CALLBACK_NAME;
201 cb->state = state;
202 return OnJsCallbackStateChange(cb);
203 }
204
SafeJsCallbackStateChangeWork(napi_env env,napi_value js_cb,void * context,void * data)205 void NapiAudioCapturerCallback::SafeJsCallbackStateChangeWork(napi_env env, napi_value js_cb, void *context, void *data)
206 {
207 AudioCapturerJsCallback *event = reinterpret_cast<AudioCapturerJsCallback *>(data);
208 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
209 "OnJsCallbackStateChange: no memory");
210 std::shared_ptr<AudioCapturerJsCallback> safeContext(
211 static_cast<AudioCapturerJsCallback*>(data),
212 [](AudioCapturerJsCallback* ptr) {
213 delete ptr;
214 });
215 std::string request = event->callbackName;
216 napi_ref callback = event->callback->cb_;
217 napi_handle_scope scope = nullptr;
218 napi_open_handle_scope(env, &scope);
219 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
220 AUDIO_INFO_LOG("SafeJsCallbackStateChangeWork: safe js callback working.");
221 do {
222 napi_value jsCallback = nullptr;
223 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
224 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
225 request.c_str());
226 napi_value args[1] = { nullptr };
227 nstatus = NapiParamUtils::SetValueInt32(env, event->state, args[PARAM0]);
228 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
229 "%{public}s fail to create Interrupt callback", request.c_str());
230 const size_t argCount = 1;
231 napi_value result = nullptr;
232 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
233 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
234 } while (0);
235 napi_close_handle_scope(env, scope);
236 }
237
StateChangeTsfnFinalize(napi_env env,void * data,void * hint)238 void NapiAudioCapturerCallback::StateChangeTsfnFinalize(napi_env env, void *data, void *hint)
239 {
240 AUDIO_INFO_LOG("StateChangeTsfnFinalize: safe thread resource release.");
241 }
242
OnJsCallbackStateChange(std::unique_ptr<AudioCapturerJsCallback> & jsCb)243 void NapiAudioCapturerCallback::OnJsCallbackStateChange(std::unique_ptr<AudioCapturerJsCallback> &jsCb)
244 {
245 if (jsCb.get() == nullptr) {
246 AUDIO_ERR_LOG("OnJsCallbackStateChange: OnJsCallbackRingerMode: jsCb.get() is null");
247 return;
248 }
249
250 AudioCapturerJsCallback *event = jsCb.release();
251 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
252 "OnJsCallbackStateChange: event is nullptr.");
253
254 napi_acquire_threadsafe_function(acStateChgTsfn_);
255 napi_call_threadsafe_function(acStateChgTsfn_, event, napi_tsfn_blocking);
256 }
257
GetEnv()258 napi_env &NapiAudioCapturerCallback::GetEnv()
259 {
260 return env_;
261 }
262
CheckIfTargetCallbackName(const std::string & callbackName)263 bool NapiAudioCapturerCallback::CheckIfTargetCallbackName(const std::string &callbackName)
264 {
265 if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME ||
266 callbackName == STATE_CHANGE_CALLBACK_NAME) {
267 return true;
268 }
269 return false;
270 }
271 } // namespace AudioStandard
272 } // namespace OHOS