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