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