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 "NapiAudioRoutingMgrCallbacks"
17 #endif
18
19 #include "js_native_api.h"
20 #include "napi_audio_routing_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 #include "napi_audio_manager_callbacks.h"
27
28 namespace OHOS {
29 namespace AudioStandard {
NapiAudioPreferredOutputDeviceChangeCallback(napi_env env)30 NapiAudioPreferredOutputDeviceChangeCallback::NapiAudioPreferredOutputDeviceChangeCallback(napi_env env)
31 : env_(env)
32 {
33 AUDIO_DEBUG_LOG("NapiAudioPreferredOutputDeviceChangeCallback: instance create");
34 }
35
~NapiAudioPreferredOutputDeviceChangeCallback()36 NapiAudioPreferredOutputDeviceChangeCallback::~NapiAudioPreferredOutputDeviceChangeCallback()
37 {
38 if (regAmOutputDevChgTsfn_) {
39 napi_release_threadsafe_function(amOutputDevChgTsfn_, napi_tsfn_abort);
40 }
41 AUDIO_DEBUG_LOG("NapiAudioPreferredOutputDeviceChangeCallback: instance destroy");
42 }
43
CreatePreferredOutTsfn(napi_env env)44 void NapiAudioPreferredOutputDeviceChangeCallback::CreatePreferredOutTsfn(napi_env env)
45 {
46 regAmOutputDevChgTsfn_ = true;
47 napi_value cbName;
48 std::string callbackName = "PreferredOutputDeviceChange";
49 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
50 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
51 ActiveOutputDeviceChangeTsfnFinalize, nullptr, SafeJsCallbackActiveOutputDeviceChangeWork,
52 &amOutputDevChgTsfn_);
53 }
54
GetPreferredOutTsfnFlag()55 bool NapiAudioPreferredOutputDeviceChangeCallback::GetPreferredOutTsfnFlag()
56 {
57 return regAmOutputDevChgTsfn_;
58 }
59
SaveCallbackReference(napi_value callback)60 void NapiAudioPreferredOutputDeviceChangeCallback::SaveCallbackReference(napi_value callback)
61 {
62 napi_ref callbackRef = nullptr;
63 const int32_t refCount = ARGS_ONE;
64
65 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
66 CHECK_AND_RETURN_LOG(status == napi_ok && callbackRef != nullptr,
67 "SaveCallbackReference: creating reference for callback fail");
68 callback_ = std::make_shared<AutoRef>(env_, callbackRef);
69 }
70
OnPreferredOutputDeviceUpdated(const std::vector<std::shared_ptr<AudioDeviceDescriptor>> & desc)71 void NapiAudioPreferredOutputDeviceChangeCallback::OnPreferredOutputDeviceUpdated(
72 const std::vector<std::shared_ptr<AudioDeviceDescriptor>> &desc)
73 {
74 std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> cb =
75 std::make_unique<AudioActiveOutputDeviceChangeJsCallback>();
76 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
77
78 cb->callback = callback_;
79 cb->callbackName = PREFERRED_OUTPUT_DEVICE_CALLBACK_NAME;
80 cb->desc = desc;
81 OnJsCallbackActiveOutputDeviceChange(cb);
82 return;
83 }
84
SafeJsCallbackActiveOutputDeviceChangeWork(napi_env env,napi_value js_cb,void * context,void * data)85 void NapiAudioPreferredOutputDeviceChangeCallback::SafeJsCallbackActiveOutputDeviceChangeWork(
86 napi_env env, napi_value js_cb, void *context, void *data)
87 {
88 AudioActiveOutputDeviceChangeJsCallback *event = reinterpret_cast<AudioActiveOutputDeviceChangeJsCallback *>(data);
89 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
90 "OnJsCallbackActiveOutputDeviceChange: no memory");
91 std::shared_ptr<AudioActiveOutputDeviceChangeJsCallback> safeContext(
92 static_cast<AudioActiveOutputDeviceChangeJsCallback*>(data),
93 [](AudioActiveOutputDeviceChangeJsCallback *ptr) {
94 delete ptr;
95 });
96 std::string request = event->callbackName;
97 napi_ref callback = event->callback->cb_;
98 napi_handle_scope scope = nullptr;
99 napi_open_handle_scope(env, &scope);
100 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
101 AUDIO_INFO_LOG("SafeJsCallbackActiveOutputDeviceChangeWork: safe js callback working.");
102
103 do {
104 napi_value jsCallback = nullptr;
105 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
106 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
107 request.c_str());
108
109 // Call back function
110 napi_value args[ARGS_ONE] = { nullptr };
111 NapiParamUtils::SetDeviceDescriptors(env, event->desc, args[PARAM0]);
112 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
113 "%{public}s fail to create ringer mode callback", request.c_str());
114
115 const size_t argCount = ARGS_ONE;
116 napi_value result = nullptr;
117 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
118 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call ringer mode callback", request.c_str());
119 } while (0);
120 napi_close_handle_scope(env, scope);
121 }
122
ActiveOutputDeviceChangeTsfnFinalize(napi_env env,void * data,void * hint)123 void NapiAudioPreferredOutputDeviceChangeCallback::ActiveOutputDeviceChangeTsfnFinalize(
124 napi_env env, void *data, void *hint)
125 {
126 AUDIO_INFO_LOG("RingModeTsfnFinalize: safe thread resource release.");
127 }
128
OnJsCallbackActiveOutputDeviceChange(std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> & jsCb)129 void NapiAudioPreferredOutputDeviceChangeCallback::OnJsCallbackActiveOutputDeviceChange(
130 std::unique_ptr<AudioActiveOutputDeviceChangeJsCallback> &jsCb)
131 {
132 if (jsCb.get() == nullptr) {
133 AUDIO_ERR_LOG("OnJsCallbackDeviceChange: jsCb.get() is null");
134 return;
135 }
136
137 AudioActiveOutputDeviceChangeJsCallback *event = jsCb.release();
138 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
139
140 napi_acquire_threadsafe_function(amOutputDevChgTsfn_);
141 napi_call_threadsafe_function(amOutputDevChgTsfn_, event, napi_tsfn_blocking);
142 }
143
ContainSameJsCallback(napi_value args)144 bool NapiAudioPreferredOutputDeviceChangeCallback::ContainSameJsCallback(napi_value args)
145 {
146 bool isEquals = false;
147 napi_value copyValue = nullptr;
148
149 napi_get_reference_value(env_, callback_->cb_, ©Value);
150 CHECK_AND_RETURN_RET_LOG(args != nullptr, false, "args is nullptr");
151
152 CHECK_AND_RETURN_RET_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok, false,
153 "Get napi_strict_equals failed");
154
155 return isEquals;
156 }
157
NapiAudioPreferredInputDeviceChangeCallback(napi_env env)158 NapiAudioPreferredInputDeviceChangeCallback::NapiAudioPreferredInputDeviceChangeCallback(napi_env env)
159 : env_(env)
160 {
161 AUDIO_DEBUG_LOG("NapiAudioPreferredInputDeviceChangeCallback: instance create");
162 }
163
~NapiAudioPreferredInputDeviceChangeCallback()164 NapiAudioPreferredInputDeviceChangeCallback::~NapiAudioPreferredInputDeviceChangeCallback()
165 {
166 if (regAmInputDevChgTsfn_) {
167 napi_release_threadsafe_function(amInputDevChgTsfn_, napi_tsfn_abort);
168 }
169 AUDIO_DEBUG_LOG("NapiAudioPreferredInputDeviceChangeCallback: instance destroy");
170 }
171
SaveCallbackReference(napi_value callback)172 void NapiAudioPreferredInputDeviceChangeCallback::SaveCallbackReference(napi_value callback)
173 {
174 napi_ref callbackRef = nullptr;
175 const int32_t refCount = ARGS_ONE;
176
177 napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
178 CHECK_AND_RETURN_LOG(status == napi_ok && callbackRef != nullptr,
179 "SaveCallbackReference: creating reference for callback fail");
180
181 callback_ = std::make_shared<AutoRef>(env_, callbackRef);
182 }
183
CreatePreferredInTsfn(napi_env env)184 void NapiAudioPreferredInputDeviceChangeCallback::CreatePreferredInTsfn(napi_env env)
185 {
186 regAmInputDevChgTsfn_ = true;
187 napi_value cbName;
188 std::string callbackName = "PreferredInputDeviceChange";
189 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
190 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
191 ActiveInputDeviceChangeTsfnFinalize, nullptr, SafeJsCallbackActiveInputDeviceChangeWork,
192 &amInputDevChgTsfn_);
193 }
194
GetPreferredInTsfnFlag()195 bool NapiAudioPreferredInputDeviceChangeCallback::GetPreferredInTsfnFlag()
196 {
197 return regAmInputDevChgTsfn_;
198 }
199
OnPreferredInputDeviceUpdated(const std::vector<std::shared_ptr<AudioDeviceDescriptor>> & desc)200 void NapiAudioPreferredInputDeviceChangeCallback::OnPreferredInputDeviceUpdated(
201 const std::vector<std::shared_ptr<AudioDeviceDescriptor>> &desc)
202 {
203 std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> cb =
204 std::make_unique<AudioActiveInputDeviceChangeJsCallback>();
205 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
206
207 cb->callback = callback_;
208 cb->callbackName = PREFERRED_INPUT_DEVICE_CALLBACK_NAME;
209 cb->desc = desc;
210 OnJsCallbackActiveInputDeviceChange(cb);
211 return;
212 }
213
SafeJsCallbackActiveInputDeviceChangeWork(napi_env env,napi_value js_cb,void * context,void * data)214 void NapiAudioPreferredInputDeviceChangeCallback::SafeJsCallbackActiveInputDeviceChangeWork(
215 napi_env env, napi_value js_cb, void *context, void *data)
216 {
217 AudioActiveInputDeviceChangeJsCallback *event = reinterpret_cast<AudioActiveInputDeviceChangeJsCallback *>(data);
218 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
219 "OnJsCallbackActiveInputDeviceChange: no memory");
220 std::shared_ptr<AudioActiveInputDeviceChangeJsCallback> safeContext(
221 static_cast<AudioActiveInputDeviceChangeJsCallback*>(data),
222 [](AudioActiveInputDeviceChangeJsCallback *ptr) {
223 delete ptr;
224 });
225 std::string request = event->callbackName;
226 napi_ref callback = event->callback->cb_;
227 napi_handle_scope scope = nullptr;
228 napi_open_handle_scope(env, &scope);
229 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
230 do {
231 napi_value jsCallback = nullptr;
232 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
233 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
234 request.c_str());
235
236 // Call back function
237 napi_value args[ARGS_ONE] = { nullptr };
238 NapiParamUtils::SetDeviceDescriptors(env, event->desc, args[PARAM0]);
239 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
240 "%{public}s fail to create input device change callback", request.c_str());
241
242 const size_t argCount = ARGS_ONE;
243 napi_value result = nullptr;
244 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
245 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call device change callback", request.c_str());
246 } while (0);
247 napi_close_handle_scope(env, scope);
248 }
249
ActiveInputDeviceChangeTsfnFinalize(napi_env env,void * data,void * hint)250 void NapiAudioPreferredInputDeviceChangeCallback::ActiveInputDeviceChangeTsfnFinalize(
251 napi_env env, void *data, void *hint)
252 {
253 AUDIO_INFO_LOG("ActiveInputDeviceChangeTsfnFinalize: safe thread resource release.");
254 }
255
OnJsCallbackActiveInputDeviceChange(std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> & jsCb)256 void NapiAudioPreferredInputDeviceChangeCallback::OnJsCallbackActiveInputDeviceChange(
257 std::unique_ptr<AudioActiveInputDeviceChangeJsCallback> &jsCb)
258 {
259 if (jsCb.get() == nullptr) {
260 AUDIO_ERR_LOG("OnJsCallbackDeviceChange: jsCb.get() is null");
261 return;
262 }
263
264 AudioActiveInputDeviceChangeJsCallback *event = jsCb.release();
265 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
266
267 napi_acquire_threadsafe_function(amInputDevChgTsfn_);
268 napi_call_threadsafe_function(amInputDevChgTsfn_, event, napi_tsfn_blocking);
269 }
270
ContainSameJsCallback(napi_value args)271 bool NapiAudioPreferredInputDeviceChangeCallback::ContainSameJsCallback(napi_value args)
272 {
273 bool isEquals = false;
274 napi_value copyValue = nullptr;
275
276 napi_get_reference_value(env_, callback_->cb_, ©Value);
277 CHECK_AND_RETURN_RET_LOG(args != nullptr, false, "args is nullptr");
278
279 CHECK_AND_RETURN_RET_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok, false,
280 "Get napi_strict_equals failed");
281
282 return isEquals;
283 }
284 } // namespace AudioStandard
285 } // namespace OHOS
286