1 /*
2 * Copyright (c) 2025 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 "NapiAudioCollaborativeManager"
17 #endif
18
19 #include "napi_audio_collaborative_manager.h"
20
21 #include <vector>
22 #include "napi_audio_error.h"
23 #include "napi_param_utils.h"
24 #include "napi_audio_enum.h"
25 #include "audio_errors.h"
26 #include "audio_manager_log.h"
27
28 namespace OHOS {
29 namespace AudioStandard {
30 using namespace std;
31 using namespace HiviewDFX;
32 static __thread napi_ref g_collaborativeManagerConstructor = nullptr;
NapiAudioCollaborativeManager()33 NapiAudioCollaborativeManager::NapiAudioCollaborativeManager()
34 : audioCollaborativeMngr_(nullptr), env_(nullptr) {}
35 NapiAudioCollaborativeManager::~NapiAudioCollaborativeManager() = default;
36
CheckContextStatus(std::shared_ptr<AudioCollaborativeManagerAsyncContext> context)37 bool NapiAudioCollaborativeManager::CheckContextStatus(std::shared_ptr<AudioCollaborativeManagerAsyncContext> context)
38 {
39 CHECK_AND_RETURN_RET_LOG(context != nullptr, false, "context object is nullptr.");
40 if (context->native == nullptr) {
41 context->SignError(NAPI_ERR_SYSTEM);
42 AUDIO_ERR_LOG("context object state is error.");
43 return false;
44 }
45 return true;
46 }
47
CheckAudioCollaborativeManagerStatus(NapiAudioCollaborativeManager * napi,std::shared_ptr<AudioCollaborativeManagerAsyncContext> context)48 bool NapiAudioCollaborativeManager::CheckAudioCollaborativeManagerStatus(NapiAudioCollaborativeManager *napi,
49 std::shared_ptr<AudioCollaborativeManagerAsyncContext> context)
50 {
51 CHECK_AND_RETURN_RET_LOG(napi != nullptr, false, "napi object is nullptr.");
52 if (napi->audioCollaborativeMngr_ == nullptr) {
53 context->SignError(NAPI_ERR_SYSTEM);
54 AUDIO_ERR_LOG("audioCollaborativeMngr is nullptr.");
55 return false;
56 }
57 return true;
58 }
59
Destructor(napi_env env,void * nativeObject,void * finalizeHint)60 void NapiAudioCollaborativeManager::Destructor(napi_env env, void *nativeObject, void *finalizeHint)
61 {
62 CHECK_AND_RETURN_LOG(nativeObject, "Native object is null");
63 auto obj = static_cast<NapiAudioCollaborativeManager *>(nativeObject);
64 ObjectRefMap<NapiAudioCollaborativeManager>::DecreaseRef(obj);
65 AUDIO_INFO_LOG("Decrease obj count");
66 }
67
Construct(napi_env env,napi_callback_info info)68 napi_value NapiAudioCollaborativeManager::Construct(napi_env env, napi_callback_info info)
69 {
70 AUDIO_INFO_LOG("Construct");
71 napi_status status;
72 napi_value result = nullptr;
73 napi_get_undefined(env, &result);
74
75 size_t argc = ARGS_TWO;
76 napi_value argv[ARGS_TWO] = {0};
77 napi_value thisVar = nullptr;
78 void *data = nullptr;
79 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
80 unique_ptr<NapiAudioCollaborativeManager> napiAudioCollaborativeManager =
81 make_unique<NapiAudioCollaborativeManager>();
82 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager != nullptr, result, "No memory");
83
84 napiAudioCollaborativeManager->audioCollaborativeMngr_ = AudioCollaborativeManager::GetInstance();
85 napiAudioCollaborativeManager->env_ = env;
86
87 ObjectRefMap<NapiAudioCollaborativeManager>::Insert(napiAudioCollaborativeManager.get());
88 status = napi_wrap(env, thisVar, static_cast<void*>(napiAudioCollaborativeManager.get()),
89 NapiAudioCollaborativeManager::Destructor, nullptr, nullptr);
90 if (status != napi_ok) {
91 ObjectRefMap<NapiAudioCollaborativeManager>::Erase(napiAudioCollaborativeManager.get());
92 return result;
93 }
94 napiAudioCollaborativeManager.release();
95 return thisVar;
96 }
97
GetParamWithSync(const napi_env & env,napi_callback_info info,size_t & argc,napi_value * args)98 NapiAudioCollaborativeManager* NapiAudioCollaborativeManager::GetParamWithSync(const napi_env &env,
99 napi_callback_info info, size_t &argc, napi_value *args)
100 {
101 napi_status status;
102 NapiAudioCollaborativeManager *napiAudioCollaborativeManager = nullptr;
103 napi_value jsThis = nullptr;
104
105 status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
106 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr,
107 "GetParamWithSync fail to napi_get_cb_info");
108
109 status = napi_unwrap(env, jsThis, (void **)&napiAudioCollaborativeManager);
110 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "napi_unwrap failed");
111 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager != nullptr &&
112 napiAudioCollaborativeManager->audioCollaborativeMngr_ !=
113 nullptr, napiAudioCollaborativeManager, "GetParamWithSync fail to napi_unwrap");
114 return napiAudioCollaborativeManager;
115 }
116
CreateCollaborativeManagerWrapper(napi_env env)117 napi_value NapiAudioCollaborativeManager::CreateCollaborativeManagerWrapper(napi_env env)
118 {
119 napi_status status;
120 napi_value result = nullptr;
121 napi_value constructor;
122
123 auto HandleNapiError = [env](const char *logMsg, napi_status status, napi_value &result) {
124 AUDIO_INFO_LOG("%s, status %{public}d", logMsg, status);
125 napi_get_undefined(env, &result);
126 return result;
127 };
128 status = napi_get_reference_value(env, g_collaborativeManagerConstructor, &constructor);
129 CHECK_AND_RETURN_RET(status == napi_ok,
130 HandleNapiError("napi_get_reference_value fail!", status, result));
131
132 status = napi_new_instance(env, constructor, 0, nullptr, &result);
133 CHECK_AND_RETURN_RET(status == napi_ok,
134 HandleNapiError("napi_new_instance fail!", status, result));
135
136 return result;
137 }
138
Init(napi_env env,napi_value exports)139 napi_value NapiAudioCollaborativeManager::Init(napi_env env, napi_value exports)
140 {
141 AUDIO_DEBUG_LOG("Init");
142
143 napi_status status;
144 napi_value constructor;
145 napi_value result = nullptr;
146 const int32_t refCount = 1;
147 napi_get_undefined(env, &result);
148
149 napi_property_descriptor audio_collaborative_manager_properties[] = {
150 DECLARE_NAPI_FUNCTION("isCollaborativePlaybackSupported", IsCollaborativePlaybackSupported),
151 DECLARE_NAPI_FUNCTION("isCollaborativePlaybackEnabledForDevice", IsCollaborativePlaybackEnabledForDevice),
152 DECLARE_NAPI_FUNCTION("setCollaborativePlaybackEnabledForDevice", SetCollaborativePlaybackEnabledForDevice),
153 };
154
155 status = napi_define_class(env, AUDIO_COLLABORATIVE_MANAGER_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Construct,
156 nullptr,
157 sizeof(audio_collaborative_manager_properties) / sizeof(audio_collaborative_manager_properties[PARAM0]),
158 audio_collaborative_manager_properties, &constructor);
159 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_define_class fail");
160
161 status = napi_create_reference(env, constructor, refCount, &g_collaborativeManagerConstructor);
162 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_create_reference fail");
163 status = napi_set_named_property(env, exports, AUDIO_COLLABORATIVE_MANAGER_NAPI_CLASS_NAME.c_str(),
164 constructor);
165 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_set_named_property fail");
166 return exports;
167 }
168
IsCollaborativePlaybackSupported(napi_env env,napi_callback_info info)169 napi_value NapiAudioCollaborativeManager::IsCollaborativePlaybackSupported(napi_env env, napi_callback_info info)
170 {
171 AUDIO_DEBUG_LOG("in");
172 CHECK_AND_RETURN_RET_LOG(PermissionUtil::VerifySelfPermission(),
173 NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_PERMISSION_DENIED), "No system permission");
174
175 napi_value result = nullptr;
176 size_t argc = PARAM0;
177 auto *napiAudioCollaborativeManager = GetParamWithSync(env, info, argc, nullptr);
178 CHECK_AND_RETURN_RET_LOG(argc == PARAM0, NapiAudioError::ThrowErrorAndReturn(env,
179 NAPI_ERR_INPUT_INVALID), "invalid arguments");
180 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager != nullptr, result,
181 "napiAudioCollaborativeManager is nullptr");
182 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager->audioCollaborativeMngr_ != nullptr, result,
183 "audioCollaborativeMngr_ is nullptr");
184 bool isCollaborativeSupported =
185 napiAudioCollaborativeManager->audioCollaborativeMngr_->IsCollaborativePlaybackSupported();
186 NapiParamUtils::SetValueBoolean(env, isCollaborativeSupported, result);
187 return result;
188 }
189
IsCollaborativePlaybackEnabledForDevice(napi_env env,napi_callback_info info)190 napi_value NapiAudioCollaborativeManager::IsCollaborativePlaybackEnabledForDevice(
191 napi_env env, napi_callback_info info)
192 {
193 AUDIO_INFO_LOG("in");
194 napi_value result = nullptr;
195 CHECK_AND_RETURN_RET_LOG(PermissionUtil::VerifySelfPermission(),
196 NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_PERMISSION_DENIED), "No system permission");
197
198 bool isCollaborativeEnabled = false;
199 const size_t requireArgc = ARGS_ONE;
200 size_t argc = PARAM1;
201 napi_value args[ARGS_ONE] = {};
202 auto *napiAudioCollaborativeManager = GetParamWithSync(env, info, argc, args);
203 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager != nullptr, result,
204 "napiAudioCollaborativeManager is nullptr");
205 CHECK_AND_RETURN_RET_LOG(napiAudioCollaborativeManager->audioCollaborativeMngr_ != nullptr, result,
206 "audioCollaborativeMngr is nullptr");
207
208 if (argc == requireArgc) {
209 bool argTransFlag = true;
210 napi_valuetype valueType = napi_undefined;
211 napi_typeof(env, args[PARAM0], &valueType);
212 CHECK_AND_RETURN_RET_LOG(valueType == napi_object, NapiAudioError::ThrowErrorAndReturn(env,
213 NAPI_ERR_INPUT_INVALID,
214 "incorrect parameter types: The type of deviceDescriptor must be object"), "invalid valueType");
215
216 std::shared_ptr<AudioDeviceDescriptor> selectedAudioDevice = std::make_shared<AudioDeviceDescriptor>();
217 NapiParamUtils::GetAudioDeviceDescriptor(env, selectedAudioDevice, argTransFlag, args[PARAM0]);
218 CHECK_AND_RETURN_RET_LOG(argTransFlag == true, NapiAudioError::ThrowErrorAndReturn(env,
219 NAPI_ERR_INPUT_INVALID,
220 "parameter verification failed: The param of deviceDescriptor must be interface AudioDeviceDescriptor"),
221 "invalid parameter");
222 if ((selectedAudioDevice->deviceType_ != DEVICE_TYPE_BLUETOOTH_A2DP) ||
223 (selectedAudioDevice->connectState_ != CONNECTED)) {
224 NapiAudioError::ThrowError(env, NAPI_ERR_INVALID_PARAM,
225 "invalid arguments, device is not A2DP or device is connected");
226 return NapiParamUtils::GetUndefinedValue(env);
227 }
228
229 isCollaborativeEnabled = napiAudioCollaborativeManager->audioCollaborativeMngr_
230 ->IsCollaborativePlaybackEnabledForDevice(selectedAudioDevice);
231 } else {
232 NapiAudioError::ThrowError(env, NAPI_ERR_INPUT_INVALID, "invalid arguments");
233 return NapiParamUtils::GetUndefinedValue(env);
234 }
235 NapiParamUtils::SetValueBoolean(env, isCollaborativeEnabled, result);
236 return result;
237 }
238
SetCollaborativePlaybackEnabledForDevice(napi_env env,napi_callback_info info)239 napi_value NapiAudioCollaborativeManager::SetCollaborativePlaybackEnabledForDevice(
240 napi_env env, napi_callback_info info)
241 {
242 CHECK_AND_RETURN_RET_LOG(PermissionUtil::VerifySelfPermission(),
243 NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_PERMISSION_DENIED), "No system permission.");
244
245 auto context = std::make_shared<AudioCollaborativeManagerAsyncContext>();
246 if (context == nullptr) {
247 AUDIO_ERR_LOG("SetCollaborativePlaybackEnabledForDevice failed : no memory");
248 NapiAudioError::ThrowError(env, "SetCollaborativePlaybackEnabledForDevice failed : no memory",
249 NAPI_ERR_NO_MEMORY);
250 return NapiParamUtils::GetUndefinedValue(env);
251 }
252
253 auto inputParser = [env, context](size_t argc, napi_value *argv) {
254 NAPI_CHECK_ARGS_RETURN_VOID(context, argc == ARGS_TWO, "mandatory parameters are left unspecified",
255 NAPI_ERR_INPUT_INVALID);
256
257 napi_valuetype valueType = napi_undefined;
258 napi_typeof(env, argv[PARAM0], &valueType);
259 NAPI_CHECK_ARGS_RETURN_VOID(context, valueType == napi_object,
260 "incorrect parameter types: The type of deviceDescriptor must be object", NAPI_ERR_INPUT_INVALID);
261
262 bool argTransFlag = true;
263 context->status = NapiParamUtils::GetAudioDeviceDescriptor(env, context->deviceDescriptor, argTransFlag,
264 argv[PARAM0]);
265 NAPI_CHECK_ARGS_RETURN_VOID(context, context->status == napi_ok,
266 "incorrect parameter types: The param of deviceDescriptor must be interface AudioDeviceDescriptor",
267 NAPI_ERR_INPUT_INVALID);
268
269 context->status = NapiParamUtils::GetValueBoolean(env, context->collaborativeEnable, argv[PARAM1]);
270 NAPI_CHECK_ARGS_RETURN_VOID(context, context->status == napi_ok,
271 "incorrect parameter types: The type of enable must be boolean",
272 NAPI_ERR_INPUT_INVALID);
273 };
274
275 context->GetCbInfo(env, info, inputParser);
276 if (context->status != napi_ok) {
277 NapiAudioError::ThrowError(env, context->errCode, context->errMessage);
278 return NapiParamUtils::GetUndefinedValue(env);
279 }
280
281 return UpdateCollaborativeEnabled(env, context);
282 }
283
UpdateCollaborativeEnabled(napi_env env,std::shared_ptr<AudioCollaborativeManagerAsyncContext> & context)284 napi_value NapiAudioCollaborativeManager::UpdateCollaborativeEnabled(napi_env env,
285 std::shared_ptr<AudioCollaborativeManagerAsyncContext> &context)
286 {
287 auto executor = [context]() {
288 CHECK_AND_RETURN_LOG(CheckContextStatus(context), "context object state is error.");
289 auto obj = reinterpret_cast<NapiAudioCollaborativeManager*>(context->native);
290 ObjectRefMap objectGuard(obj);
291 auto *napiAudioCollaborativeManager = objectGuard.GetPtr();
292 CHECK_AND_RETURN_LOG(CheckAudioCollaborativeManagerStatus(napiAudioCollaborativeManager, context),
293 "audio collaborative manager state is error.");
294
295 if (!napiAudioCollaborativeManager->audioCollaborativeMngr_->IsCollaborativePlaybackSupported()) {
296 context->SignError(NAPI_ERR_UNAVAILABLE_ON_DEVICE);
297 return;
298 }
299 if ((context->deviceDescriptor->deviceType_ != DEVICE_TYPE_BLUETOOTH_A2DP) ||
300 (context->deviceDescriptor->connectState_ != CONNECTED)) {
301 context->SignError(NAPI_ERR_INVALID_PARAM);
302 return;
303 }
304 context->intValue =
305 napiAudioCollaborativeManager->audioCollaborativeMngr_->SetCollaborativePlaybackEnabledForDevice(
306 context->deviceDescriptor, context->collaborativeEnable);
307
308 if (context->intValue == ERR_PERMISSION_DENIED) {
309 context->SignError(NAPI_ERR_NO_PERMISSION);
310 } else if (context->intValue != SUCCESS) {
311 context->SignError(NAPI_ERR_SYSTEM);
312 }
313 };
314
315 auto complete = [env](napi_value &output) {
316 output = NapiParamUtils::GetUndefinedValue(env);
317 };
318 return NapiAsyncWork::Enqueue(env, context, "SetCollaborativeEnabled", executor, complete);
319 }
320
321 }
322 }