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 "NapiAudioRoutingMgrCallbacks"
17 #endif
18
19 #include "napi_audio_routing_manager_callbacks.h"
20 #include "napi_audio_enum.h"
21 #include "napi_audio_error.h"
22 #include "napi_param_utils.h"
23 #include "audio_errors.h"
24 #include "audio_manager_log.h"
25 #include "napi_audio_manager_callbacks.h"
26
27 namespace OHOS {
28 namespace AudioStandard {
NapiAudioPreferredOutputDeviceChangeCallback(napi_env env)29 NapiAudioPreferredOutputDeviceChangeCallback::NapiAudioPreferredOutputDeviceChangeCallback(napi_env env)
30 : env_(env)
31 {
32 AUDIO_DEBUG_LOG("NapiAudioPreferredOutputDeviceChangeCallback: instance create");
33 }
34
~NapiAudioPreferredOutputDeviceChangeCallback()35 NapiAudioPreferredOutputDeviceChangeCallback::~NapiAudioPreferredOutputDeviceChangeCallback()
36 {
37 AUDIO_DEBUG_LOG("NapiAudioPreferredOutputDeviceChangeCallback: instance destroy");
38 }
39
SaveCallbackReference(AudioStreamType streamType,napi_value callback)40 void NapiAudioPreferredOutputDeviceChangeCallback::SaveCallbackReference(AudioStreamType streamType,
41 napi_value callback)
42 {
43 std::lock_guard<std::mutex> lock(mutex_);
44 napi_ref callbackRef = nullptr;
45 const int32_t refCount = ARGS_ONE;
46
47 bool isSameCallback = true;
48 for (auto it = preferredOutputDeviceCbList_.begin(); it != preferredOutputDeviceCbList_.end(); ++it) {
49 isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it).first->cb_);
50 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
51 }
52
53 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
54 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
55 "SaveCallbackReference: creating reference for callback fail");
56 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
57 preferredOutputDeviceCbList_.push_back({cb, streamType});
58 AUDIO_INFO_LOG("Save callback reference success, prefer ouput device callback list size [%{public}zu]",
59 preferredOutputDeviceCbList_.size());
60 }
61
RemoveCallbackReference(napi_env env,napi_value callback)62 void NapiAudioPreferredOutputDeviceChangeCallback::RemoveCallbackReference(napi_env env, napi_value callback)
63 {
64 std::lock_guard<std::mutex> lock(mutex_);
65
66 if (callback == nullptr) {
67 AUDIO_INFO_LOG("RemoveCallbackReference: js callback is nullptr, remove all callback reference");
68 RemoveAllCallbacks();
69 return;
70 }
71 for (auto it = preferredOutputDeviceCbList_.begin(); it != preferredOutputDeviceCbList_.end(); ++it) {
72 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it).first->cb_);
73 if (isSameCallback) {
74 AUDIO_INFO_LOG("RemoveCallbackReference: find js callback, delete it");
75 napi_status status = napi_delete_reference(env, (*it).first->cb_);
76 (*it).first->cb_ = nullptr;
77 CHECK_AND_RETURN_LOG(status == napi_ok, "RemoveCallbackReference: delete reference for callback fail");
78 preferredOutputDeviceCbList_.erase(it);
79 return;
80 }
81 }
82 AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
83 }
84
RemoveAllCallbacks()85 void NapiAudioPreferredOutputDeviceChangeCallback::RemoveAllCallbacks()
86 {
87 for (auto it = preferredOutputDeviceCbList_.begin(); it != preferredOutputDeviceCbList_.end(); ++it) {
88 napi_delete_reference(env_, (*it).first->cb_);
89 (*it).first->cb_ = nullptr;
90 }
91 preferredOutputDeviceCbList_.clear();
92 AUDIO_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
93 }
94
OnPreferredOutputDeviceUpdated(const std::vector<sptr<AudioDeviceDescriptor>> & desc)95 void NapiAudioPreferredOutputDeviceChangeCallback::OnPreferredOutputDeviceUpdated(
96 const std::vector<sptr<AudioDeviceDescriptor>> &desc)
97 {
98 std::lock_guard<std::mutex> lock(mutex_);
99 CHECK_AND_RETURN_LOG(preferredOutputDeviceCbList_.size() > 0,
100 "Cannot find the reference of prefer device callback");
101 AUDIO_DEBUG_LOG("OnPreferredOutputDeviceUpdated: Cb list size [%{public}zu]",
102 preferredOutputDeviceCbList_.size());
103
104 for (auto it = preferredOutputDeviceCbList_.begin(); it != preferredOutputDeviceCbList_.end(); it++) {
105 std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> cb =
106 std::make_unique<AudioActiveOutputDeviceChangeJsCallback>();
107 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
108
109 cb->callback = (*it).first;
110 cb->callbackName = PREFERRED_OUTPUT_DEVICE_CALLBACK_NAME;
111 cb->desc = desc;
112 OnJsCallbackActiveOutputDeviceChange(cb);
113 }
114 return;
115 }
116
WorkCallbackInterruptDone(uv_work_t * work,int status)117 void NapiAudioPreferredOutputDeviceChangeCallback::WorkCallbackInterruptDone(uv_work_t *work, int status)
118 {
119 std::shared_ptr<AudioActiveOutputDeviceChangeJsCallback> context(
120 static_cast<AudioActiveOutputDeviceChangeJsCallback*>(work->data),
121 [work](AudioActiveOutputDeviceChangeJsCallback* ptr) {
122 delete ptr;
123 delete work;
124 });
125 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
126 AudioActiveOutputDeviceChangeJsCallback *event =
127 reinterpret_cast<AudioActiveOutputDeviceChangeJsCallback *>(work->data);
128 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr");
129 std::string request = event->callbackName;
130 CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
131 napi_env env = event->callback->env_;
132 napi_ref callback = event->callback->cb_;
133 napi_handle_scope scope = nullptr;
134 napi_open_handle_scope(env, &scope);
135 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
136 AUDIO_DEBUG_LOG("JsCallBack %{public}s, uv_queue_work_with_qos start", request.c_str());
137 do {
138 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
139
140 napi_value jsCallback = nullptr;
141 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
142 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
143 request.c_str());
144
145 // Call back function
146 napi_value args[ARGS_ONE] = { nullptr };
147 NapiParamUtils::SetDeviceDescriptors(env, event->desc, args[PARAM0]);
148 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
149 "%{public}s fail to create ringer mode callback", request.c_str());
150
151 const size_t argCount = ARGS_ONE;
152 napi_value result = nullptr;
153 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
154 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call ringer mode callback", request.c_str());
155 } while (0);
156 napi_close_handle_scope(env, scope);
157 }
158
OnJsCallbackActiveOutputDeviceChange(std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> & jsCb)159 void NapiAudioPreferredOutputDeviceChangeCallback::OnJsCallbackActiveOutputDeviceChange(
160 std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> &jsCb)
161 {
162 uv_loop_s *loop = nullptr;
163 napi_get_uv_event_loop(env_, &loop);
164 CHECK_AND_RETURN_LOG(loop != nullptr, "loop is nullptr");
165
166 uv_work_t *work = new(std::nothrow) uv_work_t;
167 CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackDeviceChange: No memory");
168
169 if (jsCb.get() == nullptr) {
170 AUDIO_ERR_LOG("OnJsCallbackDeviceChange: jsCb.get() is null");
171 delete work;
172 return;
173 }
174
175 work->data = reinterpret_cast<void *>(jsCb.get());
176 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackInterruptDone,
177 uv_qos_default);
178 if (ret != 0) {
179 AUDIO_ERR_LOG("Failed to execute libuv work queue");
180 delete work;
181 } else {
182 jsCb.release();
183 }
184 }
185
NapiAudioPreferredInputDeviceChangeCallback(napi_env env)186 NapiAudioPreferredInputDeviceChangeCallback::NapiAudioPreferredInputDeviceChangeCallback(napi_env env)
187 : env_(env)
188 {
189 AUDIO_DEBUG_LOG("NapiAudioPreferredInputDeviceChangeCallback: instance create");
190 }
191
~NapiAudioPreferredInputDeviceChangeCallback()192 NapiAudioPreferredInputDeviceChangeCallback::~NapiAudioPreferredInputDeviceChangeCallback()
193 {
194 AUDIO_DEBUG_LOG("NapiAudioPreferredInputDeviceChangeCallback: instance destroy");
195 }
196
SaveCallbackReference(SourceType sourceType,napi_value callback)197 void NapiAudioPreferredInputDeviceChangeCallback::SaveCallbackReference(SourceType sourceType, napi_value callback)
198 {
199 std::lock_guard<std::mutex> lock(preferredInputListMutex_);
200 napi_ref callbackRef = nullptr;
201 const int32_t refCount = ARGS_ONE;
202
203 bool isSameCallback = true;
204 for (auto it = preferredInputDeviceCbList_.begin(); it != preferredInputDeviceCbList_.end(); ++it) {
205 isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it).first->cb_);
206 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
207 }
208
209 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
210 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
211 "SaveCallbackReference: creating reference for callback fail");
212 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
213 preferredInputDeviceCbList_.push_back({cb, sourceType});
214 AUDIO_INFO_LOG("Save callback reference success, prefer input device callback list size [%{public}zu]",
215 preferredInputDeviceCbList_.size());
216 }
217
RemoveCallbackReference(napi_env env,napi_value callback)218 void NapiAudioPreferredInputDeviceChangeCallback::RemoveCallbackReference(napi_env env, napi_value callback)
219 {
220 std::lock_guard<std::mutex> lock(preferredInputListMutex_);
221
222 if (callback == nullptr) {
223 AUDIO_INFO_LOG("RemoveCallbackReference: js callback is nullptr, remove all callback reference");
224 RemoveAllCallbacks();
225 return;
226 }
227 for (auto it = preferredInputDeviceCbList_.begin(); it != preferredInputDeviceCbList_.end(); ++it) {
228 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it).first->cb_);
229 if (isSameCallback) {
230 AUDIO_INFO_LOG("RemoveCallbackReference: find js callback, delete it");
231 napi_status status = napi_delete_reference(env, (*it).first->cb_);
232 (*it).first->cb_ = nullptr;
233 CHECK_AND_RETURN_LOG(status == napi_ok, "RemoveCallbackReference: delete reference for callback fail");
234 preferredInputDeviceCbList_.erase(it);
235 return;
236 }
237 }
238 AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
239 }
240
RemoveAllCallbacks()241 void NapiAudioPreferredInputDeviceChangeCallback::RemoveAllCallbacks()
242 {
243 for (auto it = preferredInputDeviceCbList_.begin(); it != preferredInputDeviceCbList_.end(); ++it) {
244 napi_delete_reference(env_, (*it).first->cb_);
245 (*it).first->cb_ = nullptr;
246 }
247 preferredInputDeviceCbList_.clear();
248 }
249
OnPreferredInputDeviceUpdated(const std::vector<sptr<AudioDeviceDescriptor>> & desc)250 void NapiAudioPreferredInputDeviceChangeCallback::OnPreferredInputDeviceUpdated(
251 const std::vector<sptr<AudioDeviceDescriptor>> &desc)
252 {
253 std::lock_guard<std::mutex> lock(preferredInputListMutex_);
254 CHECK_AND_RETURN_LOG(preferredInputDeviceCbList_.size() > 0, "Cannot find the reference of prefer device callback");
255 AUDIO_DEBUG_LOG("OnPreferredInputDeviceUpdated: Cb list size [%{public}zu]", preferredInputDeviceCbList_.size());
256
257 for (auto it = preferredInputDeviceCbList_.begin(); it != preferredInputDeviceCbList_.end(); it++) {
258 std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> cb =
259 std::make_unique<AudioActiveInputDeviceChangeJsCallback>();
260 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
261
262 cb->callback = (*it).first;
263 cb->callbackName = PREFERRED_INPUT_DEVICE_CALLBACK_NAME;
264 cb->desc = desc;
265 OnJsCallbackActiveInputDeviceChange(cb);
266 }
267 return;
268 }
269
WorkCallbackInterruptDone(uv_work_t * work,int status)270 void NapiAudioPreferredInputDeviceChangeCallback::WorkCallbackInterruptDone(uv_work_t *work, int status)
271 {
272 std::shared_ptr<AudioActiveInputDeviceChangeJsCallback> context(
273 static_cast<AudioActiveInputDeviceChangeJsCallback*>(work->data),
274 [work](AudioActiveInputDeviceChangeJsCallback* ptr) {
275 delete ptr;
276 delete work;
277 });
278 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
279 AudioActiveInputDeviceChangeJsCallback *event =
280 reinterpret_cast<AudioActiveInputDeviceChangeJsCallback *>(work->data);
281 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr");
282 std::string request = event->callbackName;
283 CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
284 napi_env env = event->callback->env_;
285 napi_ref callback = event->callback->cb_;
286 napi_handle_scope scope = nullptr;
287 napi_open_handle_scope(env, &scope);
288 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
289 AUDIO_DEBUG_LOG("JsCallBack %{public}s, uv_queue_work_with_qos start", request.c_str());
290 do {
291 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
292
293 napi_value jsCallback = nullptr;
294 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
295 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
296 request.c_str());
297
298 // Call back function
299 napi_value args[ARGS_ONE] = { nullptr };
300 NapiParamUtils::SetDeviceDescriptors(env, event->desc, args[PARAM0]);
301 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
302 "%{public}s fail to create ringer mode callback", request.c_str());
303
304 const size_t argCount = ARGS_ONE;
305 napi_value result = nullptr;
306 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
307 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call ringer mode callback", request.c_str());
308 } while (0);
309 napi_close_handle_scope(env, scope);
310 }
311
OnJsCallbackActiveInputDeviceChange(std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> & jsCb)312 void NapiAudioPreferredInputDeviceChangeCallback::OnJsCallbackActiveInputDeviceChange(
313 std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> &jsCb)
314 {
315 uv_loop_s *loop = nullptr;
316 napi_get_uv_event_loop(env_, &loop);
317 CHECK_AND_RETURN_LOG(loop != nullptr, "loop is nullptr");
318
319 uv_work_t *work = new(std::nothrow) uv_work_t;
320 CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackDeviceChange: No memory");
321
322 if (jsCb.get() == nullptr) {
323 AUDIO_ERR_LOG("OnJsCallbackDeviceChange: jsCb.get() is null");
324 delete work;
325 return;
326 }
327
328 work->data = reinterpret_cast<void *>(jsCb.get());
329 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackInterruptDone,
330 uv_qos_default);
331 if (ret != 0) {
332 AUDIO_ERR_LOG("Failed to execute libuv work queue");
333 delete work;
334 } else {
335 jsCb.release();
336 }
337 }
338 } // namespace AudioStandard
339 } // namespace OHOS
340