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