1 /*
2 * Copyright (c) 2021-2022 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_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 {
AudioManagerCallbackNapi(napi_env env)25 AudioManagerCallbackNapi::AudioManagerCallbackNapi(napi_env env)
26 : env_(env)
27 {
28 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: instance create");
29 }
30
~AudioManagerCallbackNapi()31 AudioManagerCallbackNapi::~AudioManagerCallbackNapi()
32 {
33 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: instance destroy");
34 }
35
SaveCallbackReference(const std::string & callbackName,napi_value args)36 void AudioManagerCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
37 {
38 std::lock_guard<std::mutex> lock(mutex_);
39 napi_ref callback = nullptr;
40 const int32_t refCount = 1;
41 napi_status status = napi_create_reference(env_, args, refCount, &callback);
42 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
43 "AudioManagerCallbackNapi: creating reference for callback fail");
44
45 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
46 if (callbackName == DEVICE_CHANGE_CALLBACK_NAME) {
47 deviceChangeCallback_ = cb;
48 } else {
49 AUDIO_ERR_LOG("AudioManagerCallbackNapi: Unknown callback type: %{public}s", callbackName.c_str());
50 }
51 }
52
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)53 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
54 {
55 napi_value value = nullptr;
56 napi_create_int32(env, intValue, &value);
57 napi_set_named_property(env, result, fieldStr.c_str(), value);
58 }
59
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string & stringValue,napi_value & result)60 static void SetValueString(const napi_env &env, const std::string &fieldStr, const std::string &stringValue,
61 napi_value &result)
62 {
63 napi_value value = nullptr;
64 napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
65 napi_set_named_property(env, result, fieldStr.c_str(), value);
66 }
67
NativeDeviceChangeActionToJsObj(const napi_env & env,napi_value & jsObj,const DeviceChangeAction & action)68 static void NativeDeviceChangeActionToJsObj(const napi_env& env, napi_value& jsObj, const DeviceChangeAction &action)
69 {
70 napi_create_object(env, &jsObj);
71 SetValueInt32(env, "type", static_cast<int32_t>(action.type), jsObj);
72
73 napi_value value = nullptr;
74 napi_value subValue = nullptr;
75 napi_value jsArray;
76 size_t size = action.deviceDescriptors.size();
77 napi_create_array_with_length(env, size, &jsArray);
78
79 for (size_t i = 0; i < size; i++) {
80 if (action.deviceDescriptors[i] != nullptr) {
81 (void)napi_create_object(env, &value);
82 SetValueInt32(env, "deviceRole", static_cast<int32_t>(action.deviceDescriptors[i]->deviceRole_), value);
83 SetValueInt32(env, "deviceType", static_cast<int32_t>(action.deviceDescriptors[i]->deviceType_), value);
84 SetValueInt32(env, "id", static_cast<int32_t>(action.deviceDescriptors[i]->deviceId_), value);
85 SetValueString(env, "name", action.deviceDescriptors[i]->deviceName_, value);
86 SetValueString(env, "address", action.deviceDescriptors[i]->macAddress_, value);
87 SetValueString(env, "displayName", action.deviceDescriptors[i]->displayName_, value);
88 SetValueString(env, "networkId", action.deviceDescriptors[i]->networkId_, value);
89 SetValueInt32(env, "interruptGroupId",
90 static_cast<int32_t>(action.deviceDescriptors[i]->interruptGroupId_), value);
91 SetValueInt32(env, "volumeGroupId",
92 static_cast<int32_t>(action.deviceDescriptors[i]->volumeGroupId_), value);
93
94 napi_value sampleRates;
95 napi_create_array_with_length(env, 1, &sampleRates);
96 napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.samplingRate, &subValue);
97 napi_set_element(env, sampleRates, 0, subValue);
98 napi_set_named_property(env, value, "sampleRates", sampleRates);
99
100 napi_value channelCounts;
101 napi_create_array_with_length(env, 1, &channelCounts);
102 napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.channels, &subValue);
103 napi_set_element(env, channelCounts, 0, subValue);
104 napi_set_named_property(env, value, "channelCounts", channelCounts);
105
106 napi_value channelMasks;
107 napi_create_array_with_length(env, 1, &channelMasks);
108 napi_create_int32(env, action.deviceDescriptors[i]->channelMasks_, &subValue);
109 napi_set_element(env, channelMasks, 0, subValue);
110 napi_set_named_property(env, value, "channelMasks", channelMasks);
111
112 napi_set_element(env, jsArray, i, value);
113 }
114 }
115
116 napi_set_named_property(env, jsObj, "deviceDescriptors", jsArray);
117 }
118
SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)119 void AudioManagerCallbackNapi::SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
120 {
121 std::lock_guard<std::mutex> lock(mutex_);
122 napi_ref callbackRef = nullptr;
123 const int32_t refCount = 1;
124
125 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
126 bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
127 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: audio manager has same callback, nothing to do");
128 }
129
130 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
131 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
132 "SaveCallbackReference: creating reference for callback fail");
133 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
134 audioManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
135 AUDIO_INFO_LOG("Save manager device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
136 deviceFlag, audioManagerDeviceChangeCbList_.size());
137 }
138
RemoveAudioManagerDeviceChangeCbRef(napi_env env,napi_value callback)139 void AudioManagerCallbackNapi::RemoveAudioManagerDeviceChangeCbRef(napi_env env, napi_value callback)
140 {
141 std::lock_guard<std::mutex> lock(mutex_);
142
143 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
144 bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
145 if (isSameCallback) {
146 AUDIO_INFO_LOG("RemoveAudioManagerDeviceChangeCbRef: find js callback, delete it");
147 napi_delete_reference(env, (*it).first->cb_);
148 (*it).first->cb_ = nullptr;
149 audioManagerDeviceChangeCbList_.erase(it);
150 return;
151 }
152 }
153 AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
154 }
155
RemoveAllAudioManagerDeviceChangeCb()156 void AudioManagerCallbackNapi::RemoveAllAudioManagerDeviceChangeCb()
157 {
158 std::lock_guard<std::mutex> lock(mutex_);
159 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
160 napi_delete_reference(env_, (*it).first->cb_);
161 (*it).first->cb_ = nullptr;
162 }
163 audioManagerDeviceChangeCbList_.clear();
164 AUDIO_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
165 }
166
GetAudioManagerDeviceChangeCbListSize()167 int32_t AudioManagerCallbackNapi::GetAudioManagerDeviceChangeCbListSize()
168 {
169 std::lock_guard<std::mutex> lock(mutex_);
170 return audioManagerDeviceChangeCbList_.size();
171 }
172
SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)173 void AudioManagerCallbackNapi::SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
174 {
175 std::lock_guard<std::mutex> lock(mutex_);
176 napi_ref callbackRef = nullptr;
177 const int32_t refCount = 1;
178
179 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
180 bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
181 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
182 }
183
184 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
185 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
186 "SaveCallbackReference: creating reference for callback fail");
187 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
188
189 routingManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
190 AUDIO_INFO_LOG("Save routing device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
191 deviceFlag, routingManagerDeviceChangeCbList_.size());
192 }
193
RemoveRoutingManagerDeviceChangeCbRef(napi_env env,napi_value callback)194 void AudioManagerCallbackNapi::RemoveRoutingManagerDeviceChangeCbRef(napi_env env, napi_value callback)
195 {
196 std::lock_guard<std::mutex> lock(mutex_);
197
198 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
199 bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
200 if (isSameCallback) {
201 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: find js callback, delete it");
202 napi_delete_reference(env, (*it).first->cb_);
203 (*it).first->cb_ = nullptr;
204 routingManagerDeviceChangeCbList_.erase(it);
205 return;
206 }
207 }
208 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: js callback no find");
209 }
210
RemoveAllRoutingManagerDeviceChangeCb()211 void AudioManagerCallbackNapi::RemoveAllRoutingManagerDeviceChangeCb()
212 {
213 std::lock_guard<std::mutex> lock(mutex_);
214 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
215 napi_delete_reference(env_, (*it).first->cb_);
216 (*it).first->cb_ = nullptr;
217 }
218 routingManagerDeviceChangeCbList_.clear();
219 AUDIO_INFO_LOG("RemoveAllRoutingManagerDeviceChangeCb: remove all js callbacks success");
220 }
221
GetRoutingManagerDeviceChangeCbListSize()222 int32_t AudioManagerCallbackNapi::GetRoutingManagerDeviceChangeCbListSize()
223 {
224 std::lock_guard<std::mutex> lock(mutex_);
225 return routingManagerDeviceChangeCbList_.size();
226 }
227
OnDeviceChange(const DeviceChangeAction & deviceChangeAction)228 void AudioManagerCallbackNapi::OnDeviceChange(const DeviceChangeAction &deviceChangeAction)
229 {
230 std::lock_guard<std::mutex> lock(mutex_);
231 AUDIO_DEBUG_LOG("OnDeviceChange: type[%{public}d], flag [%{public}d]",
232 deviceChangeAction.type, deviceChangeAction.flag);
233
234 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); it++) {
235 if (deviceChangeAction.flag == (*it).second) {
236 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
237 cb->callback = (*it).first;
238 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
239 cb->deviceChangeAction = deviceChangeAction;
240 OnJsCallbackDeviceChange(cb);
241 }
242 }
243
244 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); it++) {
245 if (deviceChangeAction.flag == (*it).second) {
246 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
247 cb->callback = (*it).first;
248 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
249 cb->deviceChangeAction = deviceChangeAction;
250 OnJsCallbackDeviceChange(cb);
251 }
252 }
253 return;
254 }
255
OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> & jsCb)256 void AudioManagerCallbackNapi::OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> &jsCb)
257 {
258 uv_loop_s *loop = nullptr;
259 napi_get_uv_event_loop(env_, &loop);
260 if (loop == nullptr) {
261 return;
262 }
263
264 uv_work_t *work = new(std::nothrow) uv_work_t;
265 if (work == nullptr) {
266 AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: No memory");
267 return;
268 }
269
270 if (jsCb.get() == nullptr) {
271 AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: jsCb.get() is null");
272 delete work;
273 return;
274 }
275
276 work->data = reinterpret_cast<void *>(jsCb.get());
277
278 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
279 // Js Thread
280 AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(work->data);
281 std::string request = event->callbackName;
282 napi_env env = event->callback->env_;
283 napi_ref callback = event->callback->cb_;
284 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: JsCallBack %{public}s, uv_queue_work start", request.c_str());
285 do {
286 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
287
288 napi_value jsCallback = nullptr;
289 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
290 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
291 request.c_str());
292
293 // Call back function
294 napi_value args[1] = { nullptr };
295 NativeDeviceChangeActionToJsObj(env, args[0], event->deviceChangeAction);
296 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
297 "%{public}s fail to create DeviceChange callback", request.c_str());
298
299 const size_t argCount = 1;
300 napi_value result = nullptr;
301 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
302 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call DeviceChange callback", request.c_str());
303 } while (0);
304 delete event;
305 delete work;
306 });
307 if (ret != 0) {
308 AUDIO_ERR_LOG("Failed to execute libuv work queue");
309 delete work;
310 } else {
311 jsCb.release();
312 }
313 }
314 } // namespace AudioStandard
315 } // namespace OHOS
316