• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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