• 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 "NapiAudioRendererDeviceChangeCallback"
17 #endif
18 
19 #include "js_native_api.h"
20 #include "napi_audio_renderer_device_change_callback.h"
21 
22 namespace OHOS {
23 namespace AudioStandard {
NapiAudioRendererDeviceChangeCallback(napi_env env)24 NapiAudioRendererDeviceChangeCallback::NapiAudioRendererDeviceChangeCallback(napi_env env)
25     : env_(env)
26 {
27     AUDIO_INFO_LOG("instance create");
28 }
29 
~NapiAudioRendererDeviceChangeCallback()30 NapiAudioRendererDeviceChangeCallback::~NapiAudioRendererDeviceChangeCallback()
31 {
32     if (regArDevInfoTsfn_) {
33         napi_release_threadsafe_function(arDevInfoTsfn_, napi_tsfn_abort);
34     }
35     AUDIO_INFO_LOG("instance destroy");
36 }
37 
SaveCallbackReference(const std::string & callbackName,napi_value args)38 void NapiAudioRendererDeviceChangeCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
39 {
40     std::lock_guard<std::mutex> lock(mutex_);
41     bool isEquals = false;
42     napi_value copyValue = nullptr;
43 
44     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
45         napi_get_reference_value(env_, (*autoRef)->cb_, &copyValue);
46         CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
47             "get napi_strict_equals failed");
48         CHECK_AND_RETURN_LOG(!isEquals, "js callback already exits");
49     }
50     // create function that will operate while save callback reference success.
51     std::function<void(std::shared_ptr<AutoRef> generatedCallback)> successed =
52         [this](std::shared_ptr<AutoRef> generatedCallback) {
53         callbacks_.push_back(generatedCallback);
54     };
55     NapiAudioRendererCallbackInner::SaveCallbackReferenceInner(callbackName, args, successed);
56     AUDIO_DEBUG_LOG("SaveAudioRendererDeviceChangeCallback sucessful");
57 }
58 
CreateRendererDeviceChangeTsfn(napi_env env)59 void NapiAudioRendererDeviceChangeCallback::CreateRendererDeviceChangeTsfn(napi_env env)
60 {
61     regArDevInfoTsfn_ = true;
62     napi_value cbName;
63     std::string callbackName = "AudioRendererDeviceChange";
64     napi_create_string_utf8(env_, callbackName.c_str(), callbackName.length(), &cbName);
65     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr, RendererDeviceInfoTsfnFinalize,
66         nullptr, SafeJsCallbackRendererDeviceInfoWork, &arDevInfoTsfn_);
67 }
68 
GetRendererDeviceChangeTsfnFlag()69 bool NapiAudioRendererDeviceChangeCallback::GetRendererDeviceChangeTsfnFlag()
70 {
71     return regArDevInfoTsfn_;
72 }
73 
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback,napi_value args)74 void NapiAudioRendererDeviceChangeCallback::RemoveCallbackReference(const std::string &callbackName,
75     napi_env env, napi_value callback, napi_value args)
76 {
77     std::lock_guard<std::mutex> lock(mutex_);
78     bool isEquals = false;
79     napi_value copyValue = nullptr;
80 
81     if (args == nullptr) {
82         callbacks_.clear();
83         AUDIO_INFO_LOG("Remove all JS Callback");
84         return;
85     }
86 
87     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
88         napi_get_reference_value(env, (*autoRef)->cb_, &copyValue);
89         CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
90         CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
91             "get napi_strict_equals failed");
92 
93         if (isEquals == true) {
94             AUDIO_INFO_LOG("found JS Callback, delete it!");
95             callbacks_.remove(*autoRef);
96             return;
97         }
98     }
99     AUDIO_INFO_LOG("RemoveCallbackReference success");
100 }
101 
GetEnv()102 napi_env &NapiAudioRendererDeviceChangeCallback::GetEnv()
103 {
104     return env_;
105 }
106 
GetCallback(const std::string & callbackName)107 std::shared_ptr<AutoRef> NapiAudioRendererDeviceChangeCallback::GetCallback(const std::string &callbackName)
108 {
109     std::shared_ptr<AutoRef> callbackCur = std::make_shared<AutoRef>(env_, nullptr);
110     return callbackCur;
111 }
112 
CheckIfTargetCallbackName(const std::string & callbackName)113 bool NapiAudioRendererDeviceChangeCallback::CheckIfTargetCallbackName(const std::string &callbackName)
114 {
115     if (callbackName == DEVICECHANGE_CALLBACK_NAME) {
116         return true;
117     }
118     return false;
119 }
120 
RemoveAllCallbacks()121 void NapiAudioRendererDeviceChangeCallback::RemoveAllCallbacks()
122 {
123     std::lock_guard<std::mutex> lock(mutex_);
124     callbacks_.clear();
125     AUDIO_INFO_LOG("RemoveAllCallbacks successful");
126 }
127 
GetCallbackListSize() const128 int32_t NapiAudioRendererDeviceChangeCallback::GetCallbackListSize() const
129 {
130     return callbacks_.size();
131 }
132 
OnOutputDeviceChange(const AudioDeviceDescriptor & deviceInfo,const AudioStreamDeviceChangeReason reason)133 void NapiAudioRendererDeviceChangeCallback::OnOutputDeviceChange(const AudioDeviceDescriptor &deviceInfo,
134     const AudioStreamDeviceChangeReason reason)
135 {
136     std::lock_guard<std::mutex> lock(mutex_);
137     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
138         OnJsCallbackRendererDeviceInfo((*autoRef)->cb_, deviceInfo);
139     }
140 }
141 
SafeJsCallbackRendererDeviceInfoWork(napi_env env,napi_value js_cb,void * context,void * data)142 void NapiAudioRendererDeviceChangeCallback::SafeJsCallbackRendererDeviceInfoWork(
143     napi_env env, napi_value js_cb, void *context, void *data)
144 {
145     AudioRendererDeviceChangeJsCallback *event = reinterpret_cast<AudioRendererDeviceChangeJsCallback *>(data);
146     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
147         "SafeJsCallbackRendererDeviceInfoWork: No memory");
148     std::shared_ptr<AudioRendererDeviceChangeJsCallback> safeContext(
149         static_cast<AudioRendererDeviceChangeJsCallback*>(data),
150         [](AudioRendererDeviceChangeJsCallback *ptr) {
151             delete ptr;
152     });
153     napi_ref callback = event->callback_;
154     napi_handle_scope scope = nullptr;
155     napi_open_handle_scope(env, &scope);
156     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
157     AUDIO_INFO_LOG("SafeJsCallbackRendererDeviceInfoWork: safe js callback working.");
158 
159     do {
160         napi_value jsCallback = nullptr;
161         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
162         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
163         // Call back function
164         napi_value args[ARGS_ONE] = { nullptr };
165         nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, args[0]);
166         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
167             " fail to convert to jsobj");
168         const size_t argCount = 1;
169         napi_value result = nullptr;
170         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
171         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
172     } while (0);
173     napi_close_handle_scope(env, scope);
174 }
175 
RendererDeviceInfoTsfnFinalize(napi_env env,void * data,void * hint)176 void NapiAudioRendererDeviceChangeCallback::RendererDeviceInfoTsfnFinalize(napi_env env, void *data, void *hint)
177 {
178     AUDIO_INFO_LOG("RendererDeviceInfoTsfnFinalize: safe thread resource release.");
179 }
180 
OnJsCallbackRendererDeviceInfo(napi_ref method,const AudioDeviceDescriptor & deviceInfo)181 void NapiAudioRendererDeviceChangeCallback::OnJsCallbackRendererDeviceInfo(napi_ref method,
182     const AudioDeviceDescriptor &deviceInfo)
183 {
184     CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackRendererDeviceInfo method is nullptr");
185     AudioRendererDeviceChangeJsCallback *event =
186         new AudioRendererDeviceChangeJsCallback {method, env_, deviceInfo};
187     CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr.");
188 
189     napi_acquire_threadsafe_function(arDevInfoTsfn_);
190     napi_call_threadsafe_function(arDevInfoTsfn_, event, napi_tsfn_blocking);
191 }
192 
NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)193 NapiAudioRendererOutputDeviceChangeWithInfoCallback::NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)
194     : env_(env)
195 {
196     AUDIO_INFO_LOG("instance create");
197 }
198 
~NapiAudioRendererOutputDeviceChangeWithInfoCallback()199 NapiAudioRendererOutputDeviceChangeWithInfoCallback::~NapiAudioRendererOutputDeviceChangeWithInfoCallback()
200 {
201     if (regArOutputDevChg_) {
202         napi_release_threadsafe_function(arOutputDevChgTsfn_, napi_tsfn_abort);
203     }
204     AUDIO_INFO_LOG("instance destroy");
205 }
206 
SaveCallbackReference(const std::string & callbackName,napi_value args)207 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::SaveCallbackReference(const std::string &callbackName,
208     napi_value args)
209 {
210     std::lock_guard<std::mutex> lock(mutex_);
211     napi_ref callback = nullptr;
212     const int32_t refCount = 1;
213     bool isEquals = false;
214     napi_value copyValue = nullptr;
215 
216     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
217         napi_get_reference_value(env_, (*autoRef)->cb_, &copyValue);
218         CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
219             "get napi_strict_equals failed");
220         CHECK_AND_RETURN_LOG(!isEquals, "js Callback already exist");
221     }
222 
223     napi_status status = napi_create_reference(env_, args, refCount, &callback);
224     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
225         "creating reference for callback fail");
226 
227     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
228     callbacks_.push_back(cb);
229     AUDIO_INFO_LOG("successful");
230 }
231 
CreateOutputDeviceChangeTsfn(napi_env env)232 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::CreateOutputDeviceChangeTsfn(napi_env env)
233 {
234     regArOutputDevChg_ = true;
235     napi_value cbName;
236     std::string callbackName = "AROutputDeviceChangeWithInfo";
237     napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
238     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr, OutputDeviceInfoTsfnFinalize,
239         nullptr, SafeJsCallbackOutputDeviceInfoWork, &arOutputDevChgTsfn_);
240 }
241 
GetOutputDeviceChangeTsfnFlag()242 bool NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetOutputDeviceChangeTsfnFlag()
243 {
244     return regArOutputDevChg_;
245 }
246 
GetEnv()247 napi_env &NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetEnv()
248 {
249     return env_;
250 }
251 
GetCallback(const std::string & callbackName)252 std::shared_ptr<AutoRef> NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetCallback(
253     const std::string &callbackName)
254 {
255     std::shared_ptr<AutoRef> callbackCur = std::make_shared<AutoRef>(env_, nullptr);
256     return callbackCur;
257 }
258 
CheckIfTargetCallbackName(const std::string & callbackName)259 bool NapiAudioRendererOutputDeviceChangeWithInfoCallback::CheckIfTargetCallbackName(
260     const std::string &callbackName)
261 {
262     if (callbackName == OUTPUT_DEVICECHANGE_WITH_INFO) {
263         return true;
264     }
265     return false;
266 }
267 
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback,napi_value args)268 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveCallbackReference(const std::string &callbackName,
269     napi_env env, napi_value callback, napi_value args)
270 {
271     std::lock_guard<std::mutex> lock(mutex_);
272     bool isEquals = false;
273     napi_value copyValue = nullptr;
274 
275     if (args == nullptr) {
276         callbacks_.clear();
277         AUDIO_INFO_LOG("Remove all JS Callback");
278         return;
279     }
280 
281     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
282         napi_get_reference_value(env, (*autoRef)->cb_, &copyValue);
283         CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
284         CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
285             "get napi_strict_equals failed");
286 
287         if (isEquals == true) {
288             AUDIO_INFO_LOG("found JS Callback, delete it!");
289             callbacks_.remove(*autoRef);
290             return;
291         }
292     }
293 
294     AUDIO_INFO_LOG("RemoveCallbackReference success");
295 }
296 
RemoveAllCallbacks()297 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveAllCallbacks()
298 {
299     std::lock_guard<std::mutex> lock(mutex_);
300     callbacks_.clear();
301     AUDIO_INFO_LOG("RemoveAllCallbacks successful");
302 }
303 
GetCallbackListSize() const304 int32_t NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetCallbackListSize() const
305 {
306     return callbacks_.size();
307 }
308 
OnOutputDeviceChange(const AudioDeviceDescriptor & deviceInfo,const AudioStreamDeviceChangeReason reason)309 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnOutputDeviceChange(const AudioDeviceDescriptor &deviceInfo,
310     const AudioStreamDeviceChangeReason reason)
311 {
312     std::lock_guard<std::mutex> lock(mutex_);
313     for (auto autoRef = callbacks_.begin(); autoRef != callbacks_.end(); ++autoRef) {
314         OnJsCallbackOutputDeviceInfo((*autoRef)->cb_, deviceInfo, reason);
315     }
316 }
317 
SafeJsCallbackOutputDeviceInfoWork(napi_env env,napi_value js_cb,void * context,void * data)318 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::SafeJsCallbackOutputDeviceInfoWork(
319     napi_env env, napi_value js_cb, void *context, void *data)
320 {
321     AudioRendererOutputDeviceChangeWithInfoJsCallback *event =
322         reinterpret_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback *>(data);
323     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
324         "OnJsCallbackOutputDeviceInfo: No memory");
325     std::shared_ptr<AudioRendererOutputDeviceChangeWithInfoJsCallback> safeContext(
326         static_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback*>(data),
327         [](AudioRendererOutputDeviceChangeWithInfoJsCallback *ptr) {
328             delete ptr;
329     });
330     napi_ref callback = event->callback_;
331     napi_handle_scope scope = nullptr;
332     napi_open_handle_scope(env, &scope);
333     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
334     AUDIO_INFO_LOG("SafeJsCallbackOutputDeviceInfoWork: safe js callback working.");
335 
336     do {
337         napi_value jsCallback = nullptr;
338         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
339         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
340         // Call back function
341         constexpr size_t argCount = ARGS_ONE;
342         napi_value args[argCount] = {};
343         napi_create_object(env, &args[PARAM0]);
344         napi_value deviceObj = nullptr;
345         nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, deviceObj);
346         CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
347             " fail to convert to jsobj");
348         napi_set_named_property(env, args[PARAM0], "devices", deviceObj);
349         nstatus = NapiParamUtils::SetValueInt32(env, "changeReason", static_cast<const int32_t> (event->reason_),
350             args[PARAM0]);
351         CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
352             " fail to convert to jsobj");
353 
354         napi_value result = nullptr;
355         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
356         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
357     } while (0);
358     napi_close_handle_scope(env, scope);
359 }
360 
OutputDeviceInfoTsfnFinalize(napi_env env,void * data,void * hint)361 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OutputDeviceInfoTsfnFinalize(
362     napi_env env, void *data, void *hint)
363 {
364     AUDIO_INFO_LOG("OutputDeviceInfoTsfnFinalize: safe thread resource release.");
365 }
366 
OnJsCallbackOutputDeviceInfo(napi_ref method,const AudioDeviceDescriptor & deviceInfo,AudioStreamDeviceChangeReason reason)367 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnJsCallbackOutputDeviceInfo(napi_ref method,
368     const AudioDeviceDescriptor &deviceInfo, AudioStreamDeviceChangeReason reason)
369 {
370     CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackOutputDeviceInfo method is nullptr");
371     AudioRendererOutputDeviceChangeWithInfoJsCallback *event =
372         new AudioRendererOutputDeviceChangeWithInfoJsCallback {method, env_, deviceInfo, reason};
373     CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr.");
374 
375     napi_acquire_threadsafe_function(arOutputDevChgTsfn_);
376     napi_call_threadsafe_function(arOutputDevChgTsfn_, event, napi_tsfn_blocking);
377 }
378 }  // namespace AudioStandard
379 }  // namespace OHOS