1 /*
2 * Copyright (c) 2021-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_manager_callback_napi.h"
17
18 #include <uv.h>
19
20 #include "audio_errors.h"
21 #include "audio_log.h"
22
23 namespace OHOS {
24 namespace AudioStandard {
AudioManagerCallbackNapi(napi_env env)25 AudioManagerCallbackNapi::AudioManagerCallbackNapi(napi_env env)
26 : env_(env)
27 {
28 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: instance create");
29 }
30
~AudioManagerCallbackNapi()31 AudioManagerCallbackNapi::~AudioManagerCallbackNapi()
32 {
33 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: instance destroy");
34 }
35
SaveCallbackReference(const std::string & callbackName,napi_value args)36 void AudioManagerCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
37 {
38 std::lock_guard<std::mutex> lock(mutex_);
39 napi_ref callback = nullptr;
40 const int32_t refCount = 1;
41 napi_status status = napi_create_reference(env_, args, refCount, &callback);
42 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
43 "AudioManagerCallbackNapi: creating reference for callback fail");
44
45 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
46 if (callbackName == DEVICE_CHANGE_CALLBACK_NAME) {
47 deviceChangeCallback_ = cb;
48 } else {
49 AUDIO_ERR_LOG("AudioManagerCallbackNapi: Unknown callback type: %{public}s", callbackName.c_str());
50 }
51 }
52
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)53 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
54 {
55 napi_value value = nullptr;
56 napi_create_int32(env, intValue, &value);
57 napi_set_named_property(env, result, fieldStr.c_str(), value);
58 }
59
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string & stringValue,napi_value & result)60 static void SetValueString(const napi_env &env, const std::string &fieldStr, const std::string &stringValue,
61 napi_value &result)
62 {
63 napi_value value = nullptr;
64 napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
65 napi_set_named_property(env, result, fieldStr.c_str(), value);
66 }
67
NativeDeviceChangeActionToJsObj(const napi_env & env,napi_value & jsObj,const DeviceChangeAction & action)68 static void NativeDeviceChangeActionToJsObj(const napi_env& env, napi_value& jsObj, const DeviceChangeAction &action)
69 {
70 napi_create_object(env, &jsObj);
71 SetValueInt32(env, "type", static_cast<int32_t>(action.type), jsObj);
72
73 napi_value value = nullptr;
74 napi_value subValue = nullptr;
75 napi_value jsArray;
76 size_t size = action.deviceDescriptors.size();
77 napi_create_array_with_length(env, size, &jsArray);
78
79 for (size_t i = 0; i < size; i++) {
80 if (action.deviceDescriptors[i] != nullptr) {
81 (void)napi_create_object(env, &value);
82 SetValueInt32(env, "deviceRole", static_cast<int32_t>(action.deviceDescriptors[i]->deviceRole_), value);
83 SetValueInt32(env, "deviceType", static_cast<int32_t>(action.deviceDescriptors[i]->deviceType_), value);
84 SetValueInt32(env, "id", static_cast<int32_t>(action.deviceDescriptors[i]->deviceId_), value);
85 SetValueString(env, "name", action.deviceDescriptors[i]->deviceName_, value);
86 SetValueString(env, "address", action.deviceDescriptors[i]->macAddress_, value);
87
88 napi_value sampleRates;
89 napi_create_array_with_length(env, 1, &sampleRates);
90 napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.samplingRate, &subValue);
91 napi_set_element(env, sampleRates, 0, subValue);
92 napi_set_named_property(env, value, "sampleRates", sampleRates);
93
94 napi_value channelCounts;
95 napi_create_array_with_length(env, 1, &channelCounts);
96 napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.channels, &subValue);
97 napi_set_element(env, channelCounts, 0, subValue);
98 napi_set_named_property(env, value, "channelCounts", channelCounts);
99
100 napi_value channelMasks;
101 napi_create_array_with_length(env, 1, &channelMasks);
102 napi_create_int32(env, action.deviceDescriptors[i]->channelMasks_, &subValue);
103 napi_set_element(env, channelMasks, 0, subValue);
104 napi_set_named_property(env, value, "channelMasks", channelMasks);
105
106 napi_set_element(env, jsArray, i, value);
107 }
108 }
109
110 napi_set_named_property(env, jsObj, "deviceDescriptors", jsArray);
111 }
112
OnDeviceChange(const DeviceChangeAction & deviceChangeAction)113 void AudioManagerCallbackNapi::OnDeviceChange(const DeviceChangeAction &deviceChangeAction)
114 {
115 std::lock_guard<std::mutex> lock(mutex_);
116 AUDIO_DEBUG_LOG("OnDeviceChange: type[%{public}d]", deviceChangeAction.type);
117 CHECK_AND_RETURN_LOG(deviceChangeCallback_ != nullptr, "callback not registered by JS client");
118
119 std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
120 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
121
122 cb->callback = deviceChangeCallback_;
123 cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
124 cb->deviceChangeAction = deviceChangeAction;
125 return OnJsCallbackDeviceChange(cb);
126 }
127
OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> & jsCb)128 void AudioManagerCallbackNapi::OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> &jsCb)
129 {
130 uv_loop_s *loop = nullptr;
131 napi_get_uv_event_loop(env_, &loop);
132 if (loop == nullptr) {
133 return;
134 }
135
136 uv_work_t *work = new(std::nothrow) uv_work_t;
137 if (work == nullptr) {
138 AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: No memory");
139 return;
140 }
141
142 if (jsCb.get() == nullptr) {
143 AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: jsCb.get() is null");
144 delete work;
145 return;
146 }
147
148 work->data = reinterpret_cast<void *>(jsCb.get());
149
150 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
151 // Js Thread
152 AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(work->data);
153 std::string request = event->callbackName;
154 napi_env env = event->callback->env_;
155 napi_ref callback = event->callback->cb_;
156 AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: JsCallBack %{public}s, uv_queue_work start", request.c_str());
157 do {
158 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
159
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, "%{public}s get reference value fail",
163 request.c_str());
164
165 // Call back function
166 napi_value args[1] = { nullptr };
167 NativeDeviceChangeActionToJsObj(env, args[0], event->deviceChangeAction);
168 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
169 "%{public}s fail to create DeviceChange callback", request.c_str());
170
171 const size_t argCount = 1;
172 napi_value result = nullptr;
173 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
174 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call DeviceChange callback", request.c_str());
175 } while (0);
176 delete event;
177 delete work;
178 });
179 if (ret != 0) {
180 AUDIO_ERR_LOG("Failed to execute libuv work queue");
181 delete work;
182 } else {
183 jsCb.release();
184 }
185 }
186 } // namespace AudioStandard
187 } // namespace OHOS
188