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 "NapiAudioManagerCallback"
17 #endif
18
19 #include "napi_audio_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
26 namespace OHOS {
27 namespace AudioStandard {
28
IsSameCallback(napi_env env,napi_value callback,napi_ref refCallback)29 bool NapiAudioManagerCallback::IsSameCallback(napi_env env, napi_value callback, napi_ref refCallback)
30 {
31 bool isEquals = false;
32 napi_value copyValue = nullptr;
33
34 napi_get_reference_value(env, refCallback, ©Value);
35 if (napi_strict_equals(env, copyValue, callback, &isEquals) != napi_ok) {
36 AUDIO_ERR_LOG("IsSameCallback: get napi_strict_equals failed");
37 return false;
38 }
39
40 return isEquals;
41 }
42
NapiAudioManagerCallback(napi_env env)43 NapiAudioManagerCallback::NapiAudioManagerCallback(napi_env env)
44 : env_(env)
45 {
46 AUDIO_DEBUG_LOG("NapiAudioManagerCallback: instance create");
47 }
48
~NapiAudioManagerCallback()49 NapiAudioManagerCallback::~NapiAudioManagerCallback()
50 {
51 AUDIO_DEBUG_LOG("NapiAudioManagerCallback: instance destroy");
52 }
53
SaveCallbackReference(const std::string & callbackName,napi_value args)54 void NapiAudioManagerCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
55 {
56 std::lock_guard<std::mutex> lock(mutex_);
57 napi_ref callback = nullptr;
58 const int32_t refCount = ARGS_ONE;
59 napi_status status = napi_create_reference(env_, args, refCount, &callback);
60 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
61 "NapiAudioManagerCallback: creating reference for callback fail");
62
63 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
64 if (callbackName == DEVICE_CHANGE_CALLBACK_NAME) {
65 deviceChangeCallback_ = cb;
66 } else if (callbackName == MICROPHONE_BLOCKED_CALLBACK_NAME) {
67 onMicroPhoneBlockedCallback_ = cb;
68 } else {
69 AUDIO_ERR_LOG("NapiAudioManagerCallback: Unknown callback type: %{public}s", callbackName.c_str());
70 }
71 }
72
GetAudioManagerDeviceChangeCbListSize()73 int32_t NapiAudioManagerCallback::GetAudioManagerDeviceChangeCbListSize()
74 {
75 std::lock_guard<std::mutex> lock(mutex_);
76 return audioManagerDeviceChangeCbList_.size();
77 }
78
SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)79 void NapiAudioManagerCallback::SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
80 {
81 std::lock_guard<std::mutex> lock(mutex_);
82 napi_ref callbackRef = nullptr;
83 const int32_t refCount = ARGS_ONE;
84
85 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
86 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
87 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
88 }
89
90 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
91 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
92 "SaveCallbackReference: creating reference for callback fail");
93 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
94
95 routingManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
96 AUDIO_INFO_LOG("Save routing device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
97 deviceFlag, routingManagerDeviceChangeCbList_.size());
98 }
99
RemoveRoutingManagerDeviceChangeCbRef(napi_env env,napi_value callback)100 void NapiAudioManagerCallback::RemoveRoutingManagerDeviceChangeCbRef(napi_env env, napi_value callback)
101 {
102 std::lock_guard<std::mutex> lock(mutex_);
103
104 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
105 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
106 if (isSameCallback) {
107 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: find js callback, delete it");
108 napi_delete_reference(env, (*it).first->cb_);
109 (*it).first->cb_ = nullptr;
110 routingManagerDeviceChangeCbList_.erase(it);
111 return;
112 }
113 }
114 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: js callback no find");
115 }
116
RemoveAllRoutingManagerDeviceChangeCb()117 void NapiAudioManagerCallback::RemoveAllRoutingManagerDeviceChangeCb()
118 {
119 std::lock_guard<std::mutex> lock(mutex_);
120 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
121 napi_delete_reference(env_, (*it).first->cb_);
122 (*it).first->cb_ = nullptr;
123 }
124 routingManagerDeviceChangeCbList_.clear();
125 AUDIO_INFO_LOG("RemoveAllRoutingManagerDeviceChangeCb: remove all js callbacks success");
126 }
127
GetRoutingManagerDeviceChangeCbListSize()128 int32_t NapiAudioManagerCallback::GetRoutingManagerDeviceChangeCbListSize()
129 {
130 std::lock_guard<std::mutex> lock(mutex_);
131 return routingManagerDeviceChangeCbList_.size();
132 }
133
OnDeviceChange(const DeviceChangeAction & deviceChangeAction)134 void NapiAudioManagerCallback::OnDeviceChange(const DeviceChangeAction &deviceChangeAction)
135 {
136 std::lock_guard<std::mutex> lock(mutex_);
137 AUDIO_DEBUG_LOG("OnDeviceChange: type[%{public}d], flag [%{public}d]",
138 deviceChangeAction.type, deviceChangeAction.flag);
139
140 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); it++) {
141 if (deviceChangeAction.flag == (*it).second) {
142 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
143 cb->callback = (*it).first;
144 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
145 cb->deviceChangeAction = deviceChangeAction;
146 OnJsCallbackDeviceChange(cb);
147 }
148 }
149
150 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); it++) {
151 if (deviceChangeAction.flag == (*it).second) {
152 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
153 cb->callback = (*it).first;
154 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
155 cb->deviceChangeAction = deviceChangeAction;
156 OnJsCallbackDeviceChange(cb);
157 }
158 }
159 return;
160 }
161
OnMicrophoneBlocked(const MicrophoneBlockedInfo & microphoneBlockedInfo)162 void NapiAudioManagerCallback::OnMicrophoneBlocked(const MicrophoneBlockedInfo µphoneBlockedInfo)
163 {
164 std::lock_guard<std::mutex> lock(mutex_);
165 AUDIO_INFO_LOG("status [%{public}d]", microphoneBlockedInfo.blockStatus);
166
167 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); it++) {
168 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
169 cb->callback = *it;
170 cb->callbackName = MICROPHONE_BLOCKED_CALLBACK_NAME;
171 cb->microphoneBlockedInfo = microphoneBlockedInfo;
172 OnJsCallbackMicrophoneBlocked(cb);
173 }
174 return;
175 }
176
SaveMicrophoneBlockedCallbackReference(napi_value callback)177 void NapiAudioManagerCallback::SaveMicrophoneBlockedCallbackReference(napi_value callback)
178 {
179 std::lock_guard<std::mutex> lock(mutex_);
180 napi_ref callbackRef = nullptr;
181 const int32_t refCount = ARGS_ONE;
182
183 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
184 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it)->cb_);
185 CHECK_AND_RETURN_LOG(!isSameCallback, "audio manager has same callback, nothing to do");
186 }
187
188 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
189 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback fail");
190 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
191 microphoneBlockedCbList_.push_back({cb});
192 AUDIO_INFO_LOG("SaveMicrophoneBlocked callback ref success, list size [%{public}zu]",
193 microphoneBlockedCbList_.size());
194 }
195
RemoveMicrophoneBlockedCallbackReference(napi_env env,napi_value callback)196 void NapiAudioManagerCallback::RemoveMicrophoneBlockedCallbackReference(napi_env env, napi_value callback)
197 {
198 std::lock_guard<std::mutex> lock(mutex_);
199 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
200 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it)->cb_);
201 if (isSameCallback) {
202 AUDIO_INFO_LOG("find microphoneBlocked callback, remove it");
203 napi_delete_reference(env_, (*it)->cb_);
204 (*it)->cb_ = nullptr;
205 microphoneBlockedCbList_.erase(it);
206 return;
207 }
208 }
209 AUDIO_INFO_LOG("remove microphoneBlocked callback no find");
210 }
211
RemoveAllMicrophoneBlockedCallback()212 void NapiAudioManagerCallback::RemoveAllMicrophoneBlockedCallback()
213 {
214 std::lock_guard<std::mutex> lock(mutex_);
215 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
216 napi_delete_reference(env_, (*it)->cb_);
217 (*it)->cb_ = nullptr;
218 }
219 microphoneBlockedCbList_.clear();
220 AUDIO_INFO_LOG("remove all js callback success");
221 }
222
SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)223 void NapiAudioManagerCallback::SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
224 {
225 std::lock_guard<std::mutex> lock(mutex_);
226 napi_ref callbackRef = nullptr;
227 const int32_t refCount = 1;
228
229 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
230 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
231 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: audio manager has same callback, nothing to do");
232 }
233
234 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
235 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
236 "SaveCallbackReference: creating reference for callback fail");
237 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
238 audioManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
239 AUDIO_INFO_LOG("Save manager device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
240 deviceFlag, audioManagerDeviceChangeCbList_.size());
241 }
242
RemoveAudioManagerDeviceChangeCbRef(napi_env env,napi_value callback)243 void NapiAudioManagerCallback::RemoveAudioManagerDeviceChangeCbRef(napi_env env, napi_value callback)
244 {
245 std::lock_guard<std::mutex> lock(mutex_);
246
247 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
248 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
249 if (isSameCallback) {
250 AUDIO_INFO_LOG("RemoveAudioManagerDeviceChangeCbRef: find js callback, delete it");
251 napi_delete_reference(env, (*it).first->cb_);
252 (*it).first->cb_ = nullptr;
253 audioManagerDeviceChangeCbList_.erase(it);
254 return;
255 }
256 }
257 AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
258 }
259
RemoveAllAudioManagerDeviceChangeCb()260 void NapiAudioManagerCallback::RemoveAllAudioManagerDeviceChangeCb()
261 {
262 std::lock_guard<std::mutex> lock(mutex_);
263 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
264 napi_delete_reference(env_, (*it).first->cb_);
265 (*it).first->cb_ = nullptr;
266 }
267 audioManagerDeviceChangeCbList_.clear();
268 AUDIO_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
269 }
270
WorkCallbackInterruptDone(uv_work_t * work,int status)271 void NapiAudioManagerCallback::WorkCallbackInterruptDone(uv_work_t *work, int status)
272 {
273 std::shared_ptr<AudioManagerJsCallback> context(
274 static_cast<AudioManagerJsCallback*>(work->data),
275 [work](AudioManagerJsCallback* ptr) {
276 delete ptr;
277 delete work;
278 });
279 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
280 AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(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 napi_value jsCallback = nullptr;
293 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
294 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
295 request.c_str());
296 napi_value args[ARGS_ONE] = { nullptr };
297 NapiParamUtils::SetValueDeviceChangeAction(env, event->deviceChangeAction, args[PARAM0]);
298 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
299 "%{public}s fail to create DeviceChange callback", request.c_str());
300 const size_t argCount = ARGS_ONE;
301 napi_value result = nullptr;
302 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
303 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call DeviceChange callback",
304 request.c_str());
305 } while (0);
306 napi_close_handle_scope(env, scope);
307 }
308
OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> & jsCb)309 void NapiAudioManagerCallback::OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> &jsCb)
310 {
311 uv_loop_s *loop = nullptr;
312 napi_get_uv_event_loop(env_, &loop);
313 CHECK_AND_RETURN_LOG(loop != nullptr, "loop is nullptr");
314
315 uv_work_t *work = new(std::nothrow) uv_work_t;
316 CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackDeviceChange: No memory");
317
318 if (jsCb.get() == nullptr) {
319 AUDIO_ERR_LOG("NapiAudioManagerCallback: OnJsCallbackDeviceChange: jsCb.get() is null");
320 delete work;
321 return;
322 }
323
324 work->data = reinterpret_cast<void *>(jsCb.get());
325 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackInterruptDone,
326 uv_qos_default);
327 if (ret != 0) {
328 AUDIO_ERR_LOG("Failed to execute libuv work queue");
329 delete work;
330 } else {
331 jsCb.release();
332 }
333 }
334
OnJsCallbackMicrophoneBlocked(std::unique_ptr<AudioManagerJsCallback> & jsCb)335 void NapiAudioManagerCallback::OnJsCallbackMicrophoneBlocked(std::unique_ptr<AudioManagerJsCallback> &jsCb)
336 {
337 if (jsCb.get() == nullptr) {
338 AUDIO_ERR_LOG("jsCb.get() is null");
339 return;
340 }
341 AudioManagerJsCallback *event = jsCb.get();
342 auto task = [event]() {
343 std::shared_ptr<AudioManagerJsCallback> context(
344 static_cast<AudioManagerJsCallback*>(event),
345 [](AudioManagerJsCallback* ptr) {
346 delete ptr;
347 });
348 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr");
349 std::string request = event->callbackName;
350 CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
351 napi_env env = event->callback->env_;
352 napi_ref callback = event->callback->cb_;
353 napi_handle_scope scope = nullptr;
354 napi_open_handle_scope(env, &scope);
355 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
356 do {
357 napi_value jsCallback = nullptr;
358 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
359 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
360 request.c_str());
361 napi_value args[ARGS_ONE] = { nullptr };
362 NapiParamUtils::SetValueBlockedDeviceAction(env, event->microphoneBlockedInfo, args[PARAM0]);
363 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
364 "%{public}s fail to create microphoneBlocked callback", request.c_str());
365 const size_t argCount = ARGS_ONE;
366 napi_value result = nullptr;
367 AUDIO_INFO_LOG("Send microphoneBlocked callback to app");
368 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
369 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call microphoneBlocked callback",
370 request.c_str());
371 } while (0);
372 napi_close_handle_scope(env, scope);
373 };
374 if (napi_status::napi_ok != napi_send_event(env_, task, napi_eprio_immediate)) {
375 AUDIO_ERR_LOG("Failed to sendEvent");
376 } else {
377 jsCb.release();
378 }
379 }
380
381 } // namespace AudioStandard
382 } // namespace OHOS