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_renderer_device_change_callback_napi.h"
17 #include <uv.h>
18 #include "audio_errors.h"
19 #include "audio_log.h"
20
21 using namespace std;
22
23 namespace OHOS {
24 namespace AudioStandard {
AudioRendererDeviceChangeCallbackNapi(napi_env env)25 AudioRendererDeviceChangeCallbackNapi::AudioRendererDeviceChangeCallbackNapi(napi_env env)
26 : env_(env)
27 {
28 AUDIO_INFO_LOG("instance create");
29 }
30
~AudioRendererDeviceChangeCallbackNapi()31 AudioRendererDeviceChangeCallbackNapi::~AudioRendererDeviceChangeCallbackNapi()
32 {
33 AUDIO_INFO_LOG("instance destroy");
34 }
35
AddCallbackReference(napi_value args)36 void AudioRendererDeviceChangeCallbackNapi::AddCallbackReference(napi_value args)
37 {
38 std::lock_guard<std::mutex> lock(mutex_);
39 napi_ref callback = nullptr;
40 const int32_t refCount = 1;
41 bool isEquals = false;
42 napi_value copyValue = nullptr;
43
44 for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
45 napi_get_reference_value(env_, *ref, ©Value);
46 if (napi_ok != napi_strict_equals(env_, copyValue, args, &isEquals)) {
47 AUDIO_ERR_LOG("get napi_strict_equals failed");
48 return;
49 }
50 if (isEquals == true) {
51 AUDIO_INFO_LOG("js Callback already exist");
52 return;
53 }
54 }
55
56 napi_status status = napi_create_reference(env_, args, refCount, &callback);
57 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
58 "AudioRendererDeviceChangeCallbackNapi: creating reference for callback fail");
59
60 callbacks_.push_back(callback);
61 AUDIO_INFO_LOG("AddCallbackReference successful");
62 }
63
RemoveCallbackReference(napi_env env,napi_value args)64 void AudioRendererDeviceChangeCallbackNapi::RemoveCallbackReference(napi_env env, napi_value args)
65 {
66 std::lock_guard<std::mutex> lock(mutex_);
67 bool isEquals = false;
68 napi_value copyValue = nullptr;
69
70 if (args == nullptr) {
71 for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
72 napi_status ret = napi_delete_reference(env, *ref);
73 CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
74 }
75 callbacks_.clear();
76 AUDIO_INFO_LOG("Remove all JS Callback");
77 return;
78 }
79
80 for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
81 napi_get_reference_value(env, *ref, ©Value);
82 if (copyValue == nullptr) {
83 AUDIO_ERR_LOG("copyValue is nullptr");
84 return;
85 }
86 if (napi_ok != napi_strict_equals(env, args, copyValue, &isEquals)) {
87 AUDIO_ERR_LOG("get napi_strict_equals failed");
88 return;
89 }
90
91 if (isEquals == true) {
92 AUDIO_INFO_LOG("found JS Callback, delete it!");
93 callbacks_.remove(*ref);
94 napi_status status = napi_delete_reference(env, *ref);
95 CHECK_AND_RETURN_LOG(status == napi_ok,
96 "deleting reference for callback fail");
97 return;
98 }
99 }
100
101 AUDIO_INFO_LOG("RemoveCallbackReference success");
102 }
103
RemoveAllCallbacks()104 void AudioRendererDeviceChangeCallbackNapi::RemoveAllCallbacks()
105 {
106 std::lock_guard<std::mutex> lock(mutex_);
107 for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
108 napi_status ret = napi_delete_reference(env_, *ref);
109 CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
110 }
111 callbacks_.clear();
112 AUDIO_INFO_LOG("RemoveAllCallbacks successful");
113 }
114
OnStateChange(const DeviceInfo & deviceInfo)115 void AudioRendererDeviceChangeCallbackNapi::OnStateChange(const DeviceInfo &deviceInfo)
116 {
117 std::lock_guard<std::mutex> lock(mutex_);
118 for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
119 OnJsCallbackRendererDeviceInfo(*ref, deviceInfo);
120 }
121 }
122
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & obj)123 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& obj)
124 {
125 napi_value value = nullptr;
126 napi_create_int32(env, intValue, &value);
127 napi_set_named_property(env, obj, fieldStr.c_str(), value);
128 }
129
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string & stringValue,napi_value & result)130 static void SetValueString(const napi_env &env, const std::string &fieldStr, const std::string &stringValue,
131 napi_value &result)
132 {
133 napi_value value = nullptr;
134 napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
135 napi_set_named_property(env, result, fieldStr.c_str(), value);
136 }
137
SetDeviceDescriptors(const napi_env & env,napi_value & jsChangeInfoObj,const DeviceInfo & deviceInfo)138 static void SetDeviceDescriptors(const napi_env& env, napi_value &jsChangeInfoObj, const DeviceInfo &deviceInfo)
139 {
140 SetValueInt32(env, "deviceRole", static_cast<int32_t>(deviceInfo.deviceRole), jsChangeInfoObj);
141 SetValueInt32(env, "deviceType", static_cast<int32_t>(deviceInfo.deviceType), jsChangeInfoObj);
142 SetValueInt32(env, "id", static_cast<int32_t>(deviceInfo.deviceId), jsChangeInfoObj);
143 SetValueString(env, "name", deviceInfo.deviceName, jsChangeInfoObj);
144 SetValueString(env, "address", deviceInfo.macAddress, jsChangeInfoObj);
145
146 napi_value value = nullptr;
147 napi_value sampleRates;
148 napi_create_array_with_length(env, 1, &sampleRates);
149 napi_create_int32(env, deviceInfo.audioStreamInfo.samplingRate, &value);
150 napi_set_element(env, sampleRates, 0, value);
151 napi_set_named_property(env, jsChangeInfoObj, "sampleRates", sampleRates);
152
153 napi_value channelCounts;
154 napi_create_array_with_length(env, 1, &channelCounts);
155 napi_create_int32(env, deviceInfo.audioStreamInfo.channels, &value);
156 napi_set_element(env, channelCounts, 0, value);
157 napi_set_named_property(env, jsChangeInfoObj, "channelCounts", channelCounts);
158
159 napi_value channelMasks;
160 napi_create_array_with_length(env, 1, &channelMasks);
161 napi_create_int32(env, deviceInfo.channelMasks, &value);
162 napi_set_element(env, channelMasks, 0, value);
163 napi_set_named_property(env, jsChangeInfoObj, "channelMasks", channelMasks);
164 }
165
NativeRendererChangeInfoToJsObj(const napi_env & env,napi_value & jsChangeDeviceInfosObj,const DeviceInfo & devInfo)166 static void NativeRendererChangeInfoToJsObj(const napi_env &env, napi_value &jsChangeDeviceInfosObj,
167 const DeviceInfo &devInfo)
168 {
169 napi_value valueParam = nullptr;
170 napi_create_array_with_length(env, 1, &jsChangeDeviceInfosObj);
171 napi_create_object(env, &valueParam);
172 SetDeviceDescriptors(env, valueParam, devInfo);
173 napi_set_element(env, jsChangeDeviceInfosObj, 0, valueParam);
174 }
175
WorkCallbackCompleted(uv_work_t * work,int status)176 void AudioRendererDeviceChangeCallbackNapi::WorkCallbackCompleted(uv_work_t *work, int status)
177 {
178 // Js Thread
179 std::shared_ptr<AudioRendererDeviceChangeJsCallback> context(
180 static_cast<AudioRendererDeviceChangeJsCallback*>(work->data),
181 [work](AudioRendererDeviceChangeJsCallback* ptr) {
182 delete ptr;
183 delete work;
184 });
185
186 AudioRendererDeviceChangeJsCallback *event = reinterpret_cast<AudioRendererDeviceChangeJsCallback*>(work->data);
187 if (event == nullptr || event->callback_ == nullptr) {
188 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo: No memory");
189 return;
190 }
191 napi_env env = event->env_;
192 napi_ref callback = event->callback_;
193
194 do {
195 napi_value jsCallback = nullptr;
196 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
197 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
198 // Call back function
199 napi_value args[1] = { nullptr };
200 NativeRendererChangeInfoToJsObj(env, args[0], event->deviceInfo_);
201 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
202 " fail to convert to jsobj");
203
204 const size_t argCount = 1;
205 napi_value result = nullptr;
206 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
207 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
208 } while (0);
209 }
210
OnJsCallbackRendererDeviceInfo(napi_ref method,const DeviceInfo & deviceInfo)211 void AudioRendererDeviceChangeCallbackNapi::OnJsCallbackRendererDeviceInfo(napi_ref method,
212 const DeviceInfo &deviceInfo)
213 {
214 uv_loop_s *loop = nullptr;
215 napi_get_uv_event_loop(env_, &loop);
216 if (loop == nullptr) {
217 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo loop_ is nullptr");
218 return;
219 }
220
221 if (method == nullptr) {
222 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo method is nullptr");
223 return;
224 }
225
226 uv_work_t *work = new(std::nothrow) uv_work_t;
227 if (work == nullptr) {
228 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo: No memory");
229 }
230
231 work->data = new AudioRendererDeviceChangeJsCallback {method, env_, deviceInfo};
232 if (work->data == nullptr) {
233 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo failed: No memory");
234 }
235 AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo");
236 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, WorkCallbackCompleted);
237 if (ret != 0) {
238 AUDIO_ERR_LOG("Failed to execute libuv work queue");
239 delete work;
240 }
241 }
242
GetCallbackListSize() const243 int32_t AudioRendererDeviceChangeCallbackNapi::GetCallbackListSize() const
244 {
245 return callbacks_.size();
246 }
247 } // namespace AudioStandard
248 } // namespace OHOS
249