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 "js_native_api.h"
20 #include "napi_audio_manager_callbacks.h"
21 #include "napi_audio_enum.h"
22 #include "napi_audio_error.h"
23 #include "napi_param_utils.h"
24 #include "audio_errors.h"
25 #include "audio_manager_log.h"
26
27 namespace OHOS {
28 namespace AudioStandard {
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 if (regAmMicBlockedTsfn_) {
52 napi_release_threadsafe_function(amMicBlockedTsfn_, napi_tsfn_abort);
53 } else if (regAmDevChgTsfn_) {
54 napi_release_threadsafe_function(amDevChgTsfn_, napi_tsfn_abort);
55 }
56 AUDIO_DEBUG_LOG("NapiAudioManagerCallback: instance destroy");
57 }
58
SaveCallbackReference(const std::string & callbackName,napi_value args)59 void NapiAudioManagerCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
60 {
61 std::lock_guard<std::mutex> lock(mutex_);
62 napi_ref callback = nullptr;
63 const int32_t refCount = ARGS_ONE;
64 napi_status status = napi_create_reference(env_, args, refCount, &callback);
65 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
66 "NapiAudioManagerCallback: creating reference for callback fail");
67
68 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
69 if (callbackName == DEVICE_CHANGE_CALLBACK_NAME) {
70 deviceChangeCallback_ = cb;
71 } else if (callbackName == MICROPHONE_BLOCKED_CALLBACK_NAME) {
72 onMicroPhoneBlockedCallback_ = cb;
73 } else {
74 AUDIO_ERR_LOG("NapiAudioManagerCallback: Unknown callback type: %{public}s", callbackName.c_str());
75 }
76 }
77
CreateMicBlockedTsfn(napi_env env)78 void NapiAudioManagerCallback::CreateMicBlockedTsfn(napi_env env)
79 {
80 regAmMicBlockedTsfn_ = true;
81 napi_value cbName;
82 std::string callbackName = "MicBlocked";
83 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
84 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
85 MicrophoneBlockedTsfnFinalize, nullptr, SafeJsCallbackMicrophoneBlockedWork, &amMicBlockedTsfn_);
86 }
87
GetMicBlockedTsfnFlag()88 bool NapiAudioManagerCallback::GetMicBlockedTsfnFlag()
89 {
90 return regAmMicBlockedTsfn_;
91 }
92
CreateDevChgTsfn(napi_env env)93 void NapiAudioManagerCallback::CreateDevChgTsfn(napi_env env)
94 {
95 regAmDevChgTsfn_ = true;
96 napi_value cbName;
97 std::string callbackName = "ManagerDeviceChange";
98 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
99 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
100 DeviceChangeTsfnFinalize, nullptr, SafeJsCallbackDeviceChangeWork, &amDevChgTsfn_);
101 }
102
GetDevChgTsfnFlag()103 bool NapiAudioManagerCallback::GetDevChgTsfnFlag()
104 {
105 return regAmDevChgTsfn_;
106 }
107
GetAudioManagerDeviceChangeCbListSize()108 int32_t NapiAudioManagerCallback::GetAudioManagerDeviceChangeCbListSize()
109 {
110 std::lock_guard<std::mutex> lock(mutex_);
111 return audioManagerDeviceChangeCbList_.size();
112 }
113
SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)114 void NapiAudioManagerCallback::SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
115 {
116 std::lock_guard<std::mutex> lock(mutex_);
117 napi_ref callbackRef = nullptr;
118 const int32_t refCount = ARGS_ONE;
119
120 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
121 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
122 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
123 }
124
125 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
126 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
127 "SaveCallbackReference: creating reference for callback fail");
128 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
129
130 routingManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
131 AUDIO_INFO_LOG("Save routing device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
132 deviceFlag, routingManagerDeviceChangeCbList_.size());
133 }
134
RemoveRoutingManagerDeviceChangeCbRef(napi_env env,napi_value callback)135 void NapiAudioManagerCallback::RemoveRoutingManagerDeviceChangeCbRef(napi_env env, napi_value callback)
136 {
137 std::lock_guard<std::mutex> lock(mutex_);
138
139 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
140 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
141 if (isSameCallback) {
142 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: find js callback, erase it");
143 routingManagerDeviceChangeCbList_.erase(it);
144 return;
145 }
146 }
147 AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: js callback no find");
148 }
149
RemoveAllRoutingManagerDeviceChangeCb()150 void NapiAudioManagerCallback::RemoveAllRoutingManagerDeviceChangeCb()
151 {
152 std::lock_guard<std::mutex> lock(mutex_);
153 routingManagerDeviceChangeCbList_.clear();
154 AUDIO_INFO_LOG("RemoveAllRoutingManagerDeviceChangeCb: remove all js callbacks success");
155 }
156
GetRoutingManagerDeviceChangeCbListSize()157 int32_t NapiAudioManagerCallback::GetRoutingManagerDeviceChangeCbListSize()
158 {
159 std::lock_guard<std::mutex> lock(mutex_);
160 return routingManagerDeviceChangeCbList_.size();
161 }
162
OnDeviceChange(const DeviceChangeAction & deviceChangeAction)163 void NapiAudioManagerCallback::OnDeviceChange(const DeviceChangeAction &deviceChangeAction)
164 {
165 std::lock_guard<std::mutex> lock(mutex_);
166 AUDIO_DEBUG_LOG("OnDeviceChange: type[%{public}d], flag [%{public}d]",
167 deviceChangeAction.type, deviceChangeAction.flag);
168
169 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); it++) {
170 if (deviceChangeAction.flag == (*it).second) {
171 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
172 cb->callback = (*it).first;
173 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
174 cb->deviceChangeAction = deviceChangeAction;
175 OnJsCallbackDeviceChange(cb);
176 }
177 }
178
179 for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); it++) {
180 if (deviceChangeAction.flag == (*it).second) {
181 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
182 cb->callback = (*it).first;
183 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
184 cb->deviceChangeAction = deviceChangeAction;
185 OnJsCallbackDeviceChange(cb);
186 }
187 }
188 return;
189 }
190
OnMicrophoneBlocked(const MicrophoneBlockedInfo & microphoneBlockedInfo)191 void NapiAudioManagerCallback::OnMicrophoneBlocked(const MicrophoneBlockedInfo µphoneBlockedInfo)
192 {
193 std::lock_guard<std::mutex> lock(mutex_);
194 AUDIO_INFO_LOG("status [%{public}d]", microphoneBlockedInfo.blockStatus);
195
196 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); it++) {
197 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
198 cb->callback = *it;
199 cb->callbackName = MICROPHONE_BLOCKED_CALLBACK_NAME;
200 cb->microphoneBlockedInfo = microphoneBlockedInfo;
201 OnJsCallbackMicrophoneBlocked(cb);
202 }
203 return;
204 }
205
SaveMicrophoneBlockedCallbackReference(napi_value callback)206 void NapiAudioManagerCallback::SaveMicrophoneBlockedCallbackReference(napi_value callback)
207 {
208 std::lock_guard<std::mutex> lock(mutex_);
209 napi_ref callbackRef = nullptr;
210 const int32_t refCount = ARGS_ONE;
211
212 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
213 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it)->cb_);
214 CHECK_AND_RETURN_LOG(!isSameCallback, "audio manager has same callback, nothing to do");
215 }
216
217 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
218 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback fail");
219 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
220 microphoneBlockedCbList_.push_back({cb});
221 AUDIO_INFO_LOG("SaveMicrophoneBlocked callback ref success, list size [%{public}zu]",
222 microphoneBlockedCbList_.size());
223 }
224
RemoveMicrophoneBlockedCallbackReference(napi_env env,napi_value callback)225 void NapiAudioManagerCallback::RemoveMicrophoneBlockedCallbackReference(napi_env env, napi_value callback)
226 {
227 std::lock_guard<std::mutex> lock(mutex_);
228 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
229 bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, callback, (*it)->cb_);
230 if (isSameCallback) {
231 AUDIO_INFO_LOG("find microphoneBlocked callback, remove it");
232 napi_delete_reference(env_, (*it)->cb_);
233 (*it)->cb_ = nullptr;
234 microphoneBlockedCbList_.erase(it);
235 return;
236 }
237 }
238 AUDIO_INFO_LOG("remove microphoneBlocked callback no find");
239 }
240
RemoveAllMicrophoneBlockedCallback()241 void NapiAudioManagerCallback::RemoveAllMicrophoneBlockedCallback()
242 {
243 std::lock_guard<std::mutex> lock(mutex_);
244 for (auto it = microphoneBlockedCbList_.begin(); it != microphoneBlockedCbList_.end(); ++it) {
245 napi_delete_reference(env_, (*it)->cb_);
246 (*it)->cb_ = nullptr;
247 }
248 microphoneBlockedCbList_.clear();
249 AUDIO_INFO_LOG("remove all js callback success");
250 }
251
SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)252 void NapiAudioManagerCallback::SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
253 {
254 std::lock_guard<std::mutex> lock(mutex_);
255 napi_ref callbackRef = nullptr;
256 const int32_t refCount = 1;
257
258 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
259 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
260 CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: audio manager has same callback, nothing to do");
261 }
262
263 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
264 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
265 "SaveCallbackReference: creating reference for callback fail");
266 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
267 audioManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
268 AUDIO_INFO_LOG("Save manager device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
269 deviceFlag, audioManagerDeviceChangeCbList_.size());
270 }
271
RemoveAudioManagerDeviceChangeCbRef(napi_env env,napi_value callback)272 void NapiAudioManagerCallback::RemoveAudioManagerDeviceChangeCbRef(napi_env env, napi_value callback)
273 {
274 std::lock_guard<std::mutex> lock(mutex_);
275
276 for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
277 bool isSameCallback = IsSameCallback(env_, callback, (*it).first->cb_);
278 if (isSameCallback) {
279 AUDIO_INFO_LOG("RemoveAudioManagerDeviceChangeCbRef: find js callback, erase it");
280 audioManagerDeviceChangeCbList_.erase(it);
281 return;
282 }
283 }
284 AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
285 }
286
RemoveAllAudioManagerDeviceChangeCb()287 void NapiAudioManagerCallback::RemoveAllAudioManagerDeviceChangeCb()
288 {
289 std::lock_guard<std::mutex> lock(mutex_);
290 audioManagerDeviceChangeCbList_.clear();
291 AUDIO_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
292 }
293
SafeJsCallbackDeviceChangeWork(napi_env env,napi_value js_cb,void * context,void * data)294 void NapiAudioManagerCallback::SafeJsCallbackDeviceChangeWork(napi_env env, napi_value js_cb, void *context, void *data)
295 {
296 AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(data);
297 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
298 "OnJsCallbackDeviceChange: no memory");
299 std::shared_ptr<AudioManagerJsCallback> safeContext(
300 static_cast<AudioManagerJsCallback*>(data),
301 [](AudioManagerJsCallback *ptr) {
302 delete ptr;
303 });
304 std::string request = event->callbackName;
305 napi_ref callback = event->callback->cb_;
306 napi_handle_scope scope = nullptr;
307 napi_open_handle_scope(env, &scope);
308 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
309 AUDIO_INFO_LOG("SafeJsCallbackDeviceChangeWork: safe js callback working.");
310
311 do {
312 napi_value jsCallback = nullptr;
313 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
314 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
315 request.c_str());
316 napi_value args[ARGS_ONE] = { nullptr };
317 NapiParamUtils::SetValueDeviceChangeAction(env, event->deviceChangeAction, args[PARAM0]);
318 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
319 "%{public}s fail to create DeviceChange callback", request.c_str());
320 const size_t argCount = ARGS_ONE;
321 napi_value result = nullptr;
322 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
323 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call DeviceChange callback",
324 request.c_str());
325 } while (0);
326 napi_close_handle_scope(env, scope);
327 }
328
DeviceChangeTsfnFinalize(napi_env env,void * data,void * hint)329 void NapiAudioManagerCallback::DeviceChangeTsfnFinalize(napi_env env, void *data, void *hint)
330 {
331 AUDIO_INFO_LOG("DeviceChangeTsfnFinalize: safe thread resource release.");
332 }
333
OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> & jsCb)334 void NapiAudioManagerCallback::OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> &jsCb)
335 {
336 if (jsCb.get() == nullptr) {
337 AUDIO_ERR_LOG("NapiAudioManagerCallback: OnJsCallbackDeviceChange: jsCb.get() is null");
338 return;
339 }
340 AudioManagerJsCallback *event = jsCb.release();
341 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
342
343 napi_acquire_threadsafe_function(amDevChgTsfn_);
344 napi_call_threadsafe_function(amDevChgTsfn_, event, napi_tsfn_blocking);
345 }
346
SafeJsCallbackMicrophoneBlockedWork(napi_env env,napi_value js_cb,void * context,void * data)347 void NapiAudioManagerCallback::SafeJsCallbackMicrophoneBlockedWork(
348 napi_env env, napi_value js_cb, void *context, void *data)
349 {
350 AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(data);
351 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
352 "OnJsCallbackMicrophoneBlocked: no memory");
353 std::shared_ptr<AudioManagerJsCallback> safeContext(
354 static_cast<AudioManagerJsCallback*>(data),
355 [](AudioManagerJsCallback *ptr) {
356 delete ptr;
357 });
358 std::string request = event->callbackName;
359 napi_ref callback = event->callback->cb_;
360 napi_handle_scope scope = nullptr;
361 napi_open_handle_scope(env, &scope);
362 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
363 AUDIO_INFO_LOG("SafeJsCallbackMicrophoneBlockedWork: safe capture state callback working.");
364 do {
365 napi_value jsCallback = nullptr;
366 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
367 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
368 request.c_str());
369 napi_value args[ARGS_ONE] = { nullptr };
370 NapiParamUtils::SetValueBlockedDeviceAction(env, event->microphoneBlockedInfo, args[PARAM0]);
371 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
372 "%{public}s fail to create microphoneBlocked callback", request.c_str());
373 const size_t argCount = ARGS_ONE;
374 napi_value result = nullptr;
375 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
376 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call microphoneBlocked callback",
377 request.c_str());
378 } while (0);
379 napi_close_handle_scope(env, scope);
380 }
381
MicrophoneBlockedTsfnFinalize(napi_env env,void * data,void * hint)382 void NapiAudioManagerCallback::MicrophoneBlockedTsfnFinalize(napi_env env, void *data, void *hint)
383 {
384 AUDIO_INFO_LOG("RingModeTsfnFinalize: safe thread resource release.");
385 }
386
OnJsCallbackMicrophoneBlocked(std::unique_ptr<AudioManagerJsCallback> & jsCb)387 void NapiAudioManagerCallback::OnJsCallbackMicrophoneBlocked(std::unique_ptr<AudioManagerJsCallback> &jsCb)
388 {
389 if (jsCb.get() == nullptr) {
390 AUDIO_ERR_LOG("jsCb.get() is null");
391 return;
392 }
393
394 AudioManagerJsCallback *event = jsCb.release();
395 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
396
397 napi_acquire_threadsafe_function(amMicBlockedTsfn_);
398 napi_call_threadsafe_function(amMicBlockedTsfn_, event, napi_tsfn_blocking);
399 }
400 } // namespace AudioStandard
401 } // namespace OHOS