1 /*
2 * Copyright (c) 2022 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
16 #include "audio_volume_manager_napi.h"
17 #include "audio_volume_group_manager_napi.h"
18
19 #include "audio_common_napi.h"
20 #include "audio_volume_key_event_napi.h"
21 #include "audio_errors.h"
22 #include "audio_log.h"
23 #include "hilog/log.h"
24 #include "napi_base_context.h"
25
26 using namespace std;
27 using OHOS::HiviewDFX::HiLog;
28 using OHOS::HiviewDFX::HiLogLabel;
29
30 namespace OHOS {
31 namespace AudioStandard {
32 static __thread napi_ref g_volumeManagerConstructor = nullptr;
33
34 namespace {
35 const int ARGS_ONE = 1;
36 const int ARGS_TWO = 2;
37 const int PARAM0 = 0;
38 const int PARAM1 = 1;
39 constexpr HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "AudioVolumeManagerNapi"};
40 const std::string VOLUME_CHANGE_CALLBACK_NAME = "volumeChange";
41 }
42
43 struct AudioVolumeManagerAsyncContext {
44 napi_env env;
45 napi_async_work work;
46 napi_deferred deferred;
47 napi_ref callbackRef = nullptr;
48 int32_t deviceFlag;
49 bool bArgTransFlag = true;
50 int32_t status = SUCCESS;
51 int32_t groupId;
52 int32_t intValue;
53 int32_t ringMode;
54 bool isMute;
55 bool isTrue;
56 std::string networkId;
57 AudioVolumeManagerNapi *objectInfo;
58 vector<sptr<VolumeGroupInfo>> volumeGroupInfos;
59 };
60
AudioVolumeManagerNapi()61 AudioVolumeManagerNapi::AudioVolumeManagerNapi()
62 : audioSystemMngr_(nullptr), env_(nullptr) {}
63
64 AudioVolumeManagerNapi::~AudioVolumeManagerNapi() = default;
65
Destructor(napi_env env,void * nativeObject,void * finalize_hint)66 void AudioVolumeManagerNapi::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
67 {
68 if (nativeObject != nullptr) {
69 auto obj = static_cast<AudioVolumeManagerNapi *>(nativeObject);
70 delete obj;
71 obj = nullptr;
72 }
73 }
74
Construct(napi_env env,napi_callback_info info)75 napi_value AudioVolumeManagerNapi::Construct(napi_env env, napi_callback_info info)
76 {
77 AUDIO_INFO_LOG("AudioVolumeManagerNapi::Construct");
78 napi_status status;
79 napi_value result = nullptr;
80 napi_get_undefined(env, &result);
81
82 size_t argc = ARGS_TWO;
83 napi_value argv[ARGS_TWO] = {0};
84 napi_value thisVar = nullptr;
85 void *data = nullptr;
86
87 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
88 unique_ptr<AudioVolumeManagerNapi> audioVolumeManagerNapi = make_unique<AudioVolumeManagerNapi>();
89 CHECK_AND_RETURN_RET_LOG(audioVolumeManagerNapi != nullptr, result, "No memory");
90
91 audioVolumeManagerNapi->audioSystemMngr_ = AudioSystemManager::GetInstance();
92 audioVolumeManagerNapi->env_ = env;
93 audioVolumeManagerNapi->cachedClientId_ = getpid();
94
95 status = napi_wrap(env, thisVar, static_cast<void*>(audioVolumeManagerNapi.get()),
96 AudioVolumeManagerNapi::Destructor, nullptr, nullptr);
97 if (status == napi_ok) {
98 audioVolumeManagerNapi.release();
99 return thisVar;
100 }
101
102 HiLog::Error(LABEL, "Failed in AudioVolumeManager::Construct()!");
103 return result;
104 }
105
CreateVolumeManagerWrapper(napi_env env)106 napi_value AudioVolumeManagerNapi::CreateVolumeManagerWrapper(napi_env env)
107 {
108 napi_status status;
109 napi_value result = nullptr;
110 napi_value constructor;
111
112 status = napi_get_reference_value(env, g_volumeManagerConstructor, &constructor);
113 if (status == napi_ok) {
114 status = napi_new_instance(env, constructor, 0, nullptr, &result);
115 if (status == napi_ok) {
116 return result;
117 }
118 }
119 HiLog::Error(LABEL, "Failed in AudioVolumeManagerNapi::CreateVolumeManagerWrapper!");
120 napi_get_undefined(env, &result);
121
122 return result;
123 }
124
Init(napi_env env,napi_value exports)125 napi_value AudioVolumeManagerNapi::Init(napi_env env, napi_value exports)
126 {
127 AUDIO_INFO_LOG("AudioVolumeManagerNapi::Init");
128 napi_status status;
129 napi_value constructor;
130 napi_value result = nullptr;
131 const int32_t refCount = 1;
132 napi_get_undefined(env, &result);
133
134 napi_property_descriptor audio_volume_manager_properties[] = {
135 DECLARE_NAPI_FUNCTION("getVolumeGroupInfos", GetVolumeGroupInfos),
136 DECLARE_NAPI_FUNCTION("getVolumeGroupManager", GetVolumeGroupManager),
137 DECLARE_NAPI_FUNCTION("on", On),
138
139 };
140
141 status = napi_define_class(env, AUDIO_VOLUME_MANAGER_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Construct, nullptr,
142 sizeof(audio_volume_manager_properties) / sizeof(audio_volume_manager_properties[PARAM0]),
143 audio_volume_manager_properties, &constructor);
144 if (status != napi_ok) {
145 return result;
146 }
147 status = napi_create_reference(env, constructor, refCount, &g_volumeManagerConstructor);
148 if (status == napi_ok) {
149 status = napi_set_named_property(env, exports, AUDIO_VOLUME_MANAGER_NAPI_CLASS_NAME.c_str(), constructor);
150 if (status == napi_ok) {
151 return exports;
152 }
153 }
154
155 HiLog::Error(LABEL, "Failure in AudioVolumeManagerNapi::Init()");
156 return result;
157 }
158
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)159 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
160 {
161 napi_value value = nullptr;
162 napi_create_int32(env, intValue, &value);
163 napi_set_named_property(env, result, fieldStr.c_str(), value);
164 }
165
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string stringValue,napi_value & result)166 static void SetValueString(const napi_env& env, const std::string& fieldStr, const std::string stringValue,
167 napi_value& result)
168 {
169 napi_value value = nullptr;
170 napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
171 napi_set_named_property(env, result, fieldStr.c_str(), value);
172 }
173
CommonCallbackRoutine(napi_env env,AudioVolumeManagerAsyncContext * & asyncContext,const napi_value & valueParam)174 static void CommonCallbackRoutine(napi_env env, AudioVolumeManagerAsyncContext *&asyncContext,
175 const napi_value &valueParam)
176 {
177 napi_value result[ARGS_TWO] = {0};
178 napi_value retVal;
179
180 if (!asyncContext->status) {
181 napi_get_undefined(env, &result[PARAM0]);
182 result[PARAM1] = valueParam;
183 } else {
184 napi_value message = nullptr;
185 std::string messageValue = AudioCommonNapi::getMessageByCode(asyncContext->status);
186 napi_create_string_utf8(env, messageValue.c_str(), NAPI_AUTO_LENGTH, &message);
187
188 napi_value code = nullptr;
189 napi_create_string_utf8(env, (std::to_string(asyncContext->status)).c_str(), NAPI_AUTO_LENGTH, &code);
190
191 napi_create_error(env, code, message, &result[PARAM0]);
192 napi_get_undefined(env, &result[PARAM1]);
193 }
194
195 if (asyncContext->deferred) {
196 if (!asyncContext->status) {
197 napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]);
198 } else {
199 napi_reject_deferred(env, asyncContext->deferred, result[PARAM0]);
200 }
201 } else {
202 napi_value callback = nullptr;
203 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
204 napi_call_function(env, nullptr, callback, ARGS_TWO, result, &retVal);
205 napi_delete_reference(env, asyncContext->callbackRef);
206 }
207 napi_delete_async_work(env, asyncContext->work);
208
209 delete asyncContext;
210 asyncContext = nullptr;
211 }
212
GetVolumeGroupInfosAsyncCallbackComplete(napi_env env,napi_status status,void * data)213 static void GetVolumeGroupInfosAsyncCallbackComplete(napi_env env, napi_status status, void *data)
214 {
215 auto asyncContext = static_cast<AudioVolumeManagerAsyncContext*>(data);
216
217 napi_value result[ARGS_TWO] = { 0 };
218 napi_value valueParam = nullptr;
219 if (asyncContext != nullptr) {
220 if (asyncContext->status) {
221 napi_get_undefined(env, &valueParam);
222 } else {
223 size_t size = asyncContext->volumeGroupInfos.size();
224 HiLog::Info(LABEL, "number of devices = %{public}zu", size);
225 napi_create_array_with_length(env, size, &result[PARAM1]);
226 for (size_t i = 0; i < size; i++) {
227 if (asyncContext->volumeGroupInfos[i] != nullptr) {
228 (void)napi_create_object(env, &valueParam);
229 SetValueString(env, "networkId", static_cast<std::string>(
230 asyncContext->volumeGroupInfos[i]->networkId_), valueParam);
231 SetValueInt32(env, "groupId", static_cast<int32_t>(
232 asyncContext->volumeGroupInfos[i]->volumeGroupId_), valueParam);
233 SetValueInt32(env, "mappingId", static_cast<int32_t>(
234 asyncContext->volumeGroupInfos[i]->mappingId_), valueParam);
235 SetValueString(env, "groupName", static_cast<std::string>(
236 asyncContext->volumeGroupInfos[i]->groupName_), valueParam);
237 SetValueInt32(env, "ConnectType", static_cast<int32_t>(
238 asyncContext->volumeGroupInfos[i]->connectType_), valueParam);
239 napi_set_element(env, result[PARAM1], i, valueParam);
240 }
241 }
242 }
243 CommonCallbackRoutine(env, asyncContext, result[PARAM1]);
244 } else {
245 HiLog::Error(LABEL, "ERROR: AudioRoutingManagerAsyncContext* is Null!");
246 }
247 }
248
GetVolumeGroupInfos(napi_env env,napi_callback_info info)249 napi_value AudioVolumeManagerNapi::GetVolumeGroupInfos(napi_env env, napi_callback_info info)
250 {
251 HiLog::Info(LABEL, " %{public}s IN", __func__);
252
253 napi_status status;
254 const int32_t refCount = 1;
255 napi_value result = nullptr;
256
257 GET_PARAMS(env, info, ARGS_TWO);
258
259 unique_ptr<AudioVolumeManagerAsyncContext> asyncContext = make_unique<AudioVolumeManagerAsyncContext>();
260 if (argc < ARGS_ONE) {
261 asyncContext->status = NAPI_ERR_INVALID_PARAM;
262 }
263 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
264 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
265 for (size_t i = PARAM0; i < argc; i++) {
266 napi_valuetype valueType = napi_undefined;
267 napi_typeof(env, argv[i], &valueType);
268 if (i == PARAM0 && valueType == napi_string) {
269 asyncContext->networkId = AudioCommonNapi::GetStringArgument(env, argv[i]);
270 } else if (i == PARAM1) {
271 if (valueType == napi_function) {
272 napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef);
273 }
274 break;
275 } else {
276 asyncContext->status = NAPI_ERR_INVALID_PARAM;
277 }
278 }
279
280 if (asyncContext->callbackRef == nullptr) {
281 napi_create_promise(env, &asyncContext->deferred, &result);
282 } else {
283 napi_get_undefined(env, &result);
284 }
285
286 napi_value resource = nullptr;
287 napi_create_string_utf8(env, "GetVolumeGroupInfos", NAPI_AUTO_LENGTH, &resource);
288
289 status = napi_create_async_work(
290 env, nullptr, resource,
291 [](napi_env env, void* data) {
292 auto context = static_cast<AudioVolumeManagerAsyncContext*>(data);
293 if (!context->status) {
294 context->volumeGroupInfos = context->objectInfo->audioSystemMngr_->
295 GetVolumeGroups(context->networkId);
296 context->status = 0;
297 }
298 },
299 GetVolumeGroupInfosAsyncCallbackComplete, static_cast<void*>(asyncContext.get()), &asyncContext->work);
300 if (status != napi_ok) {
301 AudioCommonNapi::throwError(env, NAPI_ERR_SYSTEM);
302 result = nullptr;
303 } else {
304 status = napi_queue_async_work(env, asyncContext->work);
305 if (status == napi_ok) {
306 asyncContext.release();
307 } else {
308 result = nullptr;
309 }
310 }
311 }
312
313 return result;
314 }
315
GetGroupMgrAsyncCallbackComplete(napi_env env,napi_status status,void * data)316 static void GetGroupMgrAsyncCallbackComplete(napi_env env, napi_status status, void* data)
317 {
318 napi_value valueParam = nullptr;
319 auto asyncContext = static_cast<AudioVolumeManagerAsyncContext *>(data);
320
321 if (asyncContext != nullptr) {
322 if (!asyncContext->status) {
323 valueParam = AudioVolumeGroupManagerNapi::CreateAudioVolumeGroupManagerWrapper(env, asyncContext->groupId);
324 asyncContext->status = AudioVolumeGroupManagerNapi::isConstructSuccess_;
325 AudioVolumeGroupManagerNapi::isConstructSuccess_ = SUCCESS;
326 }
327 CommonCallbackRoutine(env, asyncContext, valueParam);
328 } else {
329 HiLog::Error(LABEL, "ERROR: GetStreamMgrAsyncCallbackComplete asyncContext is Null!");
330 }
331 }
332
GetVolumeGroupManager(napi_env env,napi_callback_info info)333 napi_value AudioVolumeManagerNapi::GetVolumeGroupManager(napi_env env, napi_callback_info info)
334 {
335 HiLog::Info(LABEL, " %{public}s IN", __func__);
336 napi_status status;
337 const int32_t refCount = 1;
338 napi_value result = nullptr;
339
340 GET_PARAMS(env, info, ARGS_TWO);
341
342 unique_ptr<AudioVolumeManagerAsyncContext> asyncContext = make_unique<AudioVolumeManagerAsyncContext>();
343 CHECK_AND_RETURN_RET_LOG(asyncContext != nullptr, nullptr, "AudioVolumeManagerAsyncContext object creation failed");
344 if (argc < ARGS_ONE) {
345 asyncContext->status = NAPI_ERR_INVALID_PARAM;
346 }
347 for (size_t i = PARAM0; i < argc; i++) {
348 napi_valuetype valueType = napi_undefined;
349 napi_typeof(env, argv[i], &valueType);
350 if (i == PARAM0 && valueType == napi_number) {
351 napi_get_value_int32(env, argv[i], &asyncContext->groupId);
352 } else if (i == PARAM1) {
353 if (valueType == napi_function) {
354 napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef);
355 }
356 break;
357 } else {
358 asyncContext->status = NAPI_ERR_INVALID_PARAM;
359 }
360 }
361
362 if (asyncContext->callbackRef == nullptr) {
363 napi_create_promise(env, &asyncContext->deferred, &result);
364 } else {
365 napi_get_undefined(env, &result);
366 }
367
368 napi_value resource = nullptr;
369 napi_create_string_utf8(env, "GetVolumeGroupManager", NAPI_AUTO_LENGTH, &resource);
370
371 status = napi_create_async_work(
372 env, nullptr, resource,
373 [](napi_env env, void *data) {
374 auto context = static_cast<AudioVolumeManagerAsyncContext *>(data);
375 if (!context->status) {
376 context->status = SUCCESS;
377 }
378 },
379 GetGroupMgrAsyncCallbackComplete, static_cast<void *>(asyncContext.get()), &asyncContext->work);
380 if (status != napi_ok) {
381 result = nullptr;
382 } else {
383 status = napi_queue_async_work(env, asyncContext->work);
384 if (status == napi_ok) {
385 asyncContext.release();
386 } else {
387 result = nullptr;
388 }
389 }
390
391 return result;
392 }
393
On(napi_env env,napi_callback_info info)394 napi_value AudioVolumeManagerNapi::On(napi_env env, napi_callback_info info)
395 {
396 napi_value undefinedResult = nullptr;
397 napi_get_undefined(env, &undefinedResult);
398
399 const size_t minArgCount = 2;
400 size_t argCount = 3;
401 napi_value args[minArgCount + 1] = {nullptr, nullptr, nullptr};
402 napi_value jsThis = nullptr;
403 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
404 if (status != napi_ok || argCount < minArgCount) {
405 AUDIO_ERR_LOG("On fail to napi_get_cb_info/Requires min 2 parameters");
406 AudioCommonNapi::throwError(env, NAPI_ERR_INPUT_INVALID);
407 }
408
409 napi_valuetype eventType = napi_undefined;
410 if (napi_typeof(env, args[PARAM0], &eventType) != napi_ok || eventType != napi_string) {
411 AudioCommonNapi::throwError(env, NAPI_ERR_INPUT_INVALID);
412 return undefinedResult;
413 }
414 std::string callbackName = AudioCommonNapi::GetStringArgument(env, args[0]);
415 AUDIO_INFO_LOG("AudioVolumeManagerNapi::On callbackName: %{public}s", callbackName.c_str());
416
417 AudioVolumeManagerNapi *volumeManagerNapi = nullptr;
418 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&volumeManagerNapi));
419 NAPI_ASSERT(env, status == napi_ok && volumeManagerNapi != nullptr,
420 "Failed to retrieve audio manager napi instance.");
421 NAPI_ASSERT(env, volumeManagerNapi->audioSystemMngr_ != nullptr,
422 "audio system manager instance is null.");
423
424 napi_valuetype handler = napi_undefined;
425 if (napi_typeof(env, args[PARAM1], &handler) != napi_ok || handler != napi_function) {
426 AUDIO_ERR_LOG("AudioVolumeManagerNapi::On type mismatch for parameter 2");
427 AudioCommonNapi::throwError(env, NAPI_ERR_INPUT_INVALID);
428 return undefinedResult;
429 }
430
431 if (!callbackName.compare(VOLUME_CHANGE_CALLBACK_NAME)) {
432 if (volumeManagerNapi->volumeKeyEventCallbackNapi_ == nullptr) {
433 volumeManagerNapi->volumeKeyEventCallbackNapi_ = std::make_shared<AudioVolumeKeyEventNapi>(env);
434 int32_t ret = volumeManagerNapi->audioSystemMngr_->RegisterVolumeKeyEventCallback(
435 volumeManagerNapi->cachedClientId_, volumeManagerNapi->volumeKeyEventCallbackNapi_);
436 if (ret) {
437 AUDIO_ERR_LOG("AudioVolumeManagerNapi:: RegisterVolumeKeyEventCallback Failed");
438 } else {
439 AUDIO_DEBUG_LOG("AudioVolumeManagerNapi:: RegisterVolumeKeyEventCallback Success");
440 }
441 }
442 std::shared_ptr<AudioVolumeKeyEventNapi> cb =
443 std::static_pointer_cast<AudioVolumeKeyEventNapi>(volumeManagerNapi->volumeKeyEventCallbackNapi_);
444 cb->SaveCallbackReference(callbackName, args[PARAM1]);
445 } else {
446 AUDIO_ERR_LOG("AudioVolumeManagerNapi::No such callback supported");
447 AudioCommonNapi::throwError(env, NAPI_ERR_INVALID_PARAM);
448 }
449 return undefinedResult;
450 }
451 } // namespace AudioStandard
452 } // namespace OHOS
453