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 "NapiAudioRendererDeviceChangeCallback"
17 #endif
18
19 #include "js_native_api.h"
20 #include "napi_audio_renderer_device_change_callback.h"
21
22 namespace OHOS {
23 namespace AudioStandard {
NapiAudioRendererDeviceChangeCallback(napi_env env)24 NapiAudioRendererDeviceChangeCallback::NapiAudioRendererDeviceChangeCallback(napi_env env)
25 : env_(env)
26 {
27 AUDIO_INFO_LOG("instance create");
28 }
29
~NapiAudioRendererDeviceChangeCallback()30 NapiAudioRendererDeviceChangeCallback::~NapiAudioRendererDeviceChangeCallback()
31 {
32 if (regArDevInfoTsfn_) {
33 napi_release_threadsafe_function(arDevInfoTsfn_, napi_tsfn_abort);
34 }
35 AUDIO_INFO_LOG("instance destroy");
36 }
37
AddCallbackReference(napi_value args)38 void NapiAudioRendererDeviceChangeCallback::AddCallbackReference(napi_value args)
39 {
40 std::lock_guard<std::mutex> lock(mutex_);
41 napi_ref callback;
42 const int32_t refCount = 1;
43 bool isEquals = false;
44 napi_value copyValue = nullptr;
45
46 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
47 napi_get_reference_value(env_, (*autoRef)->cb_, ©Value);
48 CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
49 "get napi_strict_equals failed");
50 CHECK_AND_RETURN_LOG(!isEquals, "js callback already exits");
51 }
52
53 napi_status status = napi_create_reference(env_, args, refCount, &callback);
54 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
55 "NapiAudioRendererDeviceChangeCallback: create reference for callback fail");
56
57 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
58 callbacks_.push_back(cb);
59 AUDIO_DEBUG_LOG("AddAudioRendererDeviceChangeCallback sucessful");
60 }
61
CreateRendererDeviceChangeTsfn(napi_env env)62 void NapiAudioRendererDeviceChangeCallback::CreateRendererDeviceChangeTsfn(napi_env env)
63 {
64 regArDevInfoTsfn_ = true;
65 napi_value cbName;
66 std::string callbackName = "AudioRendererDeviceChange";
67 napi_create_string_utf8(env_, callbackName.c_str(), callbackName.length(), &cbName);
68 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr, RendererDeviceInfoTsfnFinalize,
69 nullptr, SafeJsCallbackRendererDeviceInfoWork, &arDevInfoTsfn_);
70 }
71
GetRendererDeviceChangeTsfnFlag()72 bool NapiAudioRendererDeviceChangeCallback::GetRendererDeviceChangeTsfnFlag()
73 {
74 return regArDevInfoTsfn_;
75 }
76
RemoveCallbackReference(napi_env env,napi_value args)77 void NapiAudioRendererDeviceChangeCallback::RemoveCallbackReference(napi_env env, napi_value args)
78 {
79 std::lock_guard<std::mutex> lock(mutex_);
80 bool isEquals = false;
81 napi_value copyValue = nullptr;
82
83 if (args == nullptr) {
84 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
85 napi_status ret = napi_delete_reference(env, (*autoRef)->cb_);
86 CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
87 (*autoRef)->cb_ = nullptr;
88 }
89 callbacks_.clear();
90 AUDIO_INFO_LOG("Remove all JS Callback");
91 return;
92 }
93
94 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
95 napi_get_reference_value(env, (*autoRef)->cb_, ©Value);
96 CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
97 CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
98 "get napi_strict_equals failed");
99
100 if (isEquals == true) {
101 AUDIO_INFO_LOG("found JS Callback, delete it!");
102 callbacks_.remove(*autoRef);
103 napi_status status = napi_delete_reference(env, (*autoRef)->cb_);
104 CHECK_AND_RETURN_LOG(status == napi_ok, "deleting callback reference failed");
105 (*autoRef)->cb_ = nullptr;
106 return;
107 }
108 }
109 AUDIO_INFO_LOG("RemoveCallbackReference success");
110 }
111
RemoveAllCallbacks()112 void NapiAudioRendererDeviceChangeCallback::RemoveAllCallbacks()
113 {
114 std::lock_guard<std::mutex> lock(mutex_);
115 callbacks_.clear();
116 AUDIO_INFO_LOG("RemoveAllCallbacks successful");
117 }
118
GetCallbackListSize() const119 int32_t NapiAudioRendererDeviceChangeCallback::GetCallbackListSize() const
120 {
121 return callbacks_.size();
122 }
123
OnOutputDeviceChange(const AudioDeviceDescriptor & deviceInfo,const AudioStreamDeviceChangeReason reason)124 void NapiAudioRendererDeviceChangeCallback::OnOutputDeviceChange(const AudioDeviceDescriptor &deviceInfo,
125 const AudioStreamDeviceChangeReason reason)
126 {
127 std::lock_guard<std::mutex> lock(mutex_);
128 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
129 OnJsCallbackRendererDeviceInfo((*autoRef)->cb_, deviceInfo);
130 }
131 }
132
SafeJsCallbackRendererDeviceInfoWork(napi_env env,napi_value js_cb,void * context,void * data)133 void NapiAudioRendererDeviceChangeCallback::SafeJsCallbackRendererDeviceInfoWork(
134 napi_env env, napi_value js_cb, void *context, void *data)
135 {
136 AudioRendererDeviceChangeJsCallback *event = reinterpret_cast<AudioRendererDeviceChangeJsCallback *>(data);
137 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
138 "SafeJsCallbackRendererDeviceInfoWork: No memory");
139 std::shared_ptr<AudioRendererDeviceChangeJsCallback> safeContext(
140 static_cast<AudioRendererDeviceChangeJsCallback*>(data),
141 [](AudioRendererDeviceChangeJsCallback *ptr) {
142 delete ptr;
143 });
144 napi_ref callback = event->callback_;
145 napi_handle_scope scope = nullptr;
146 napi_open_handle_scope(env, &scope);
147 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
148 AUDIO_INFO_LOG("SafeJsCallbackRendererDeviceInfoWork: safe js callback working.");
149
150 do {
151 napi_value jsCallback = nullptr;
152 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
153 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
154 // Call back function
155 napi_value args[ARGS_ONE] = { nullptr };
156 nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, args[0]);
157 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
158 " fail to convert to jsobj");
159 const size_t argCount = 1;
160 napi_value result = nullptr;
161 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
162 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
163 } while (0);
164 napi_close_handle_scope(env, scope);
165 }
166
RendererDeviceInfoTsfnFinalize(napi_env env,void * data,void * hint)167 void NapiAudioRendererDeviceChangeCallback::RendererDeviceInfoTsfnFinalize(napi_env env, void *data, void *hint)
168 {
169 AUDIO_INFO_LOG("RendererDeviceInfoTsfnFinalize: safe thread resource release.");
170 }
171
OnJsCallbackRendererDeviceInfo(napi_ref method,const AudioDeviceDescriptor & deviceInfo)172 void NapiAudioRendererDeviceChangeCallback::OnJsCallbackRendererDeviceInfo(napi_ref method,
173 const AudioDeviceDescriptor &deviceInfo)
174 {
175 CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackRendererDeviceInfo method is nullptr");
176 AudioRendererDeviceChangeJsCallback *event =
177 new AudioRendererDeviceChangeJsCallback {method, env_, deviceInfo};
178 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr.");
179
180 napi_acquire_threadsafe_function(arDevInfoTsfn_);
181 napi_call_threadsafe_function(arDevInfoTsfn_, event, napi_tsfn_blocking);
182 }
183
NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)184 NapiAudioRendererOutputDeviceChangeWithInfoCallback::NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)
185 : env_(env)
186 {
187 AUDIO_INFO_LOG("instance create");
188 }
189
~NapiAudioRendererOutputDeviceChangeWithInfoCallback()190 NapiAudioRendererOutputDeviceChangeWithInfoCallback::~NapiAudioRendererOutputDeviceChangeWithInfoCallback()
191 {
192 if (regArOutputDevChg_) {
193 napi_release_threadsafe_function(arOutputDevChgTsfn_, napi_tsfn_abort);
194 }
195 AUDIO_INFO_LOG("instance destroy");
196 }
197
AddCallbackReference(napi_value args)198 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::AddCallbackReference(napi_value args)
199 {
200 std::lock_guard<std::mutex> lock(mutex_);
201 napi_ref callback = nullptr;
202 const int32_t refCount = 1;
203 bool isEquals = false;
204 napi_value copyValue = nullptr;
205
206 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
207 napi_get_reference_value(env_, (*autoRef)->cb_, ©Value);
208 CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
209 "get napi_strict_equals failed");
210 CHECK_AND_RETURN_LOG(!isEquals, "js Callback already exist");
211 }
212
213 napi_status status = napi_create_reference(env_, args, refCount, &callback);
214 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
215 "creating reference for callback fail");
216
217 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
218 callbacks_.push_back(cb);
219 AUDIO_INFO_LOG("successful");
220 }
221
CreateOutputDeviceChangeTsfn(napi_env env)222 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::CreateOutputDeviceChangeTsfn(napi_env env)
223 {
224 regArOutputDevChg_ = true;
225 napi_value cbName;
226 std::string callbackName = "AROutputDeviceChangeWithInfo";
227 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
228 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr, OutputDeviceInfoTsfnFinalize,
229 nullptr, SafeJsCallbackOutputDeviceInfoWork, &arOutputDevChgTsfn_);
230 }
231
GetOutputDeviceChangeTsfnFlag()232 bool NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetOutputDeviceChangeTsfnFlag()
233 {
234 return regArOutputDevChg_;
235 }
236
RemoveCallbackReference(napi_env env,napi_value args)237 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveCallbackReference(napi_env env, napi_value args)
238 {
239 std::lock_guard<std::mutex> lock(mutex_);
240 bool isEquals = false;
241 napi_value copyValue = nullptr;
242
243 if (args == nullptr) {
244 callbacks_.clear();
245 AUDIO_INFO_LOG("Remove all JS Callback");
246 return;
247 }
248
249 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
250 napi_get_reference_value(env, (*autoRef)->cb_, ©Value);
251 CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
252 CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
253 "get napi_strict_equals failed");
254
255 if (isEquals == true) {
256 AUDIO_INFO_LOG("found JS Callback, delete it!");
257 callbacks_.remove(*autoRef);
258 return;
259 }
260 }
261
262 AUDIO_INFO_LOG("RemoveCallbackReference success");
263 }
264
RemoveAllCallbacks()265 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveAllCallbacks()
266 {
267 std::lock_guard<std::mutex> lock(mutex_);
268 callbacks_.clear();
269 AUDIO_INFO_LOG("RemoveAllCallbacks successful");
270 }
271
GetCallbackListSize() const272 int32_t NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetCallbackListSize() const
273 {
274 return callbacks_.size();
275 }
276
OnOutputDeviceChange(const AudioDeviceDescriptor & deviceInfo,const AudioStreamDeviceChangeReason reason)277 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnOutputDeviceChange(const AudioDeviceDescriptor &deviceInfo,
278 const AudioStreamDeviceChangeReason reason)
279 {
280 std::lock_guard<std::mutex> lock(mutex_);
281 for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
282 OnJsCallbackOutputDeviceInfo((*autoRef)->cb_, deviceInfo, reason);
283 }
284 }
285
SafeJsCallbackOutputDeviceInfoWork(napi_env env,napi_value js_cb,void * context,void * data)286 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::SafeJsCallbackOutputDeviceInfoWork(
287 napi_env env, napi_value js_cb, void *context, void *data)
288 {
289 AudioRendererOutputDeviceChangeWithInfoJsCallback *event =
290 reinterpret_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback *>(data);
291 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
292 "OnJsCallbackOutputDeviceInfo: No memory");
293 std::shared_ptr<AudioRendererOutputDeviceChangeWithInfoJsCallback> safeContext(
294 static_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback*>(data),
295 [](AudioRendererOutputDeviceChangeWithInfoJsCallback *ptr) {
296 delete ptr;
297 });
298 napi_ref callback = event->callback_;
299 napi_handle_scope scope = nullptr;
300 napi_open_handle_scope(env, &scope);
301 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
302 AUDIO_INFO_LOG("SafeJsCallbackOutputDeviceInfoWork: safe js callback working.");
303
304 do {
305 napi_value jsCallback = nullptr;
306 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
307 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
308 // Call back function
309 constexpr size_t argCount = ARGS_ONE;
310 napi_value args[argCount] = {};
311 napi_create_object(env, &args[PARAM0]);
312 napi_value deviceObj = nullptr;
313 nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, deviceObj);
314 CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
315 " fail to convert to jsobj");
316 napi_set_named_property(env, args[PARAM0], "devices", deviceObj);
317 nstatus = NapiParamUtils::SetValueInt32(env, "changeReason", static_cast<const int32_t> (event->reason_),
318 args[PARAM0]);
319 CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
320 " fail to convert to jsobj");
321
322 napi_value result = nullptr;
323 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
324 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
325 } while (0);
326 napi_close_handle_scope(env, scope);
327 }
328
OutputDeviceInfoTsfnFinalize(napi_env env,void * data,void * hint)329 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OutputDeviceInfoTsfnFinalize(
330 napi_env env, void *data, void *hint)
331 {
332 AUDIO_INFO_LOG("OutputDeviceInfoTsfnFinalize: safe thread resource release.");
333 }
334
OnJsCallbackOutputDeviceInfo(napi_ref method,const AudioDeviceDescriptor & deviceInfo,AudioStreamDeviceChangeReason reason)335 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnJsCallbackOutputDeviceInfo(napi_ref method,
336 const AudioDeviceDescriptor &deviceInfo, AudioStreamDeviceChangeReason reason)
337 {
338 CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackOutputDeviceInfo method is nullptr");
339 AudioRendererOutputDeviceChangeWithInfoJsCallback *event =
340 new AudioRendererOutputDeviceChangeWithInfoJsCallback {method, env_, deviceInfo, reason};
341 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr.");
342
343 napi_acquire_threadsafe_function(arOutputDevChgTsfn_);
344 napi_call_threadsafe_function(arOutputDevChgTsfn_, event, napi_tsfn_blocking);
345 }
346 } // namespace AudioStandard
347 } // namespace OHOS