• 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 #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, &copyValue);
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 &microphoneBlockedInfo)
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