• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             SetValueString(env, "displayName", action.deviceDescriptors[i]->displayName_, value);
88             SetValueString(env, "networkId", action.deviceDescriptors[i]->networkId_, value);
89             SetValueInt32(env, "interruptGroupId",
90                 static_cast<int32_t>(action.deviceDescriptors[i]->interruptGroupId_), value);
91             SetValueInt32(env, "volumeGroupId",
92                 static_cast<int32_t>(action.deviceDescriptors[i]->volumeGroupId_), value);
93 
94             napi_value sampleRates;
95             napi_create_array_with_length(env, 1, &sampleRates);
96             napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.samplingRate, &subValue);
97             napi_set_element(env, sampleRates, 0, subValue);
98             napi_set_named_property(env, value, "sampleRates", sampleRates);
99 
100             napi_value channelCounts;
101             napi_create_array_with_length(env, 1, &channelCounts);
102             napi_create_int32(env, action.deviceDescriptors[i]->audioStreamInfo_.channels, &subValue);
103             napi_set_element(env, channelCounts, 0, subValue);
104             napi_set_named_property(env, value, "channelCounts", channelCounts);
105 
106             napi_value channelMasks;
107             napi_create_array_with_length(env, 1, &channelMasks);
108             napi_create_int32(env, action.deviceDescriptors[i]->channelMasks_, &subValue);
109             napi_set_element(env, channelMasks, 0, subValue);
110             napi_set_named_property(env, value, "channelMasks", channelMasks);
111 
112             napi_set_element(env, jsArray, i, value);
113         }
114     }
115 
116     napi_set_named_property(env, jsObj, "deviceDescriptors", jsArray);
117 }
118 
SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)119 void AudioManagerCallbackNapi::SaveAudioManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
120 {
121     std::lock_guard<std::mutex> lock(mutex_);
122     napi_ref callbackRef = nullptr;
123     const int32_t refCount = 1;
124 
125     for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
126         bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
127         CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: audio manager has same callback, nothing to do");
128     }
129 
130     napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
131     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
132         "SaveCallbackReference: creating reference for callback fail");
133     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
134     audioManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
135     AUDIO_INFO_LOG("Save manager device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
136         deviceFlag, audioManagerDeviceChangeCbList_.size());
137 }
138 
RemoveAudioManagerDeviceChangeCbRef(napi_env env,napi_value callback)139 void AudioManagerCallbackNapi::RemoveAudioManagerDeviceChangeCbRef(napi_env env, napi_value callback)
140 {
141     std::lock_guard<std::mutex> lock(mutex_);
142 
143     for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
144         bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
145         if (isSameCallback) {
146             AUDIO_INFO_LOG("RemoveAudioManagerDeviceChangeCbRef: find js callback, delete it");
147             napi_delete_reference(env, (*it).first->cb_);
148             (*it).first->cb_ = nullptr;
149             audioManagerDeviceChangeCbList_.erase(it);
150             return;
151         }
152     }
153     AUDIO_INFO_LOG("RemoveCallbackReference: js callback no find");
154 }
155 
RemoveAllAudioManagerDeviceChangeCb()156 void AudioManagerCallbackNapi::RemoveAllAudioManagerDeviceChangeCb()
157 {
158     std::lock_guard<std::mutex> lock(mutex_);
159     for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); ++it) {
160         napi_delete_reference(env_, (*it).first->cb_);
161         (*it).first->cb_ = nullptr;
162     }
163     audioManagerDeviceChangeCbList_.clear();
164     AUDIO_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
165 }
166 
GetAudioManagerDeviceChangeCbListSize()167 int32_t AudioManagerCallbackNapi::GetAudioManagerDeviceChangeCbListSize()
168 {
169     std::lock_guard<std::mutex> lock(mutex_);
170     return audioManagerDeviceChangeCbList_.size();
171 }
172 
SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag,napi_value callback)173 void AudioManagerCallbackNapi::SaveRoutingManagerDeviceChangeCbRef(DeviceFlag deviceFlag, napi_value callback)
174 {
175     std::lock_guard<std::mutex> lock(mutex_);
176     napi_ref callbackRef = nullptr;
177     const int32_t refCount = 1;
178 
179     for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
180         bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
181         CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
182     }
183 
184     napi_status status = napi_create_reference(env_, callback, refCount, &callbackRef);
185     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
186         "SaveCallbackReference: creating reference for callback fail");
187     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callbackRef);
188 
189     routingManagerDeviceChangeCbList_.push_back({cb, deviceFlag});
190     AUDIO_INFO_LOG("Save routing device change callback ref success, deviceFlag [%{public}d], list size [%{public}zu]",
191         deviceFlag, routingManagerDeviceChangeCbList_.size());
192 }
193 
RemoveRoutingManagerDeviceChangeCbRef(napi_env env,napi_value callback)194 void AudioManagerCallbackNapi::RemoveRoutingManagerDeviceChangeCbRef(napi_env env, napi_value callback)
195 {
196     std::lock_guard<std::mutex> lock(mutex_);
197 
198     for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
199         bool isSameCallback = AudioCommonNapi::IsSameCallback(env_, callback, (*it).first->cb_);
200         if (isSameCallback) {
201             AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: find js callback, delete it");
202             napi_delete_reference(env, (*it).first->cb_);
203             (*it).first->cb_ = nullptr;
204             routingManagerDeviceChangeCbList_.erase(it);
205             return;
206         }
207     }
208     AUDIO_INFO_LOG("RemoveRoutingManagerDeviceChangeCbRef: js callback no find");
209 }
210 
RemoveAllRoutingManagerDeviceChangeCb()211 void AudioManagerCallbackNapi::RemoveAllRoutingManagerDeviceChangeCb()
212 {
213     std::lock_guard<std::mutex> lock(mutex_);
214     for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); ++it) {
215         napi_delete_reference(env_, (*it).first->cb_);
216         (*it).first->cb_ = nullptr;
217     }
218     routingManagerDeviceChangeCbList_.clear();
219     AUDIO_INFO_LOG("RemoveAllRoutingManagerDeviceChangeCb: remove all js callbacks success");
220 }
221 
GetRoutingManagerDeviceChangeCbListSize()222 int32_t AudioManagerCallbackNapi::GetRoutingManagerDeviceChangeCbListSize()
223 {
224     std::lock_guard<std::mutex> lock(mutex_);
225     return routingManagerDeviceChangeCbList_.size();
226 }
227 
OnDeviceChange(const DeviceChangeAction & deviceChangeAction)228 void AudioManagerCallbackNapi::OnDeviceChange(const DeviceChangeAction &deviceChangeAction)
229 {
230     std::lock_guard<std::mutex> lock(mutex_);
231     AUDIO_DEBUG_LOG("OnDeviceChange: type[%{public}d], flag [%{public}d]",
232         deviceChangeAction.type, deviceChangeAction.flag);
233 
234     for (auto it = audioManagerDeviceChangeCbList_.begin(); it != audioManagerDeviceChangeCbList_.end(); it++) {
235         if (deviceChangeAction.flag == (*it).second) {
236             std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
237             cb->callback = (*it).first;
238             cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
239             cb->deviceChangeAction = deviceChangeAction;
240             OnJsCallbackDeviceChange(cb);
241         }
242     }
243 
244     for (auto it = routingManagerDeviceChangeCbList_.begin(); it != routingManagerDeviceChangeCbList_.end(); it++) {
245         if (deviceChangeAction.flag == (*it).second) {
246             std::unique_ptr<AudioManagerJsCallback> cb = std::make_unique<AudioManagerJsCallback>();
247             cb->callback = (*it).first;
248             cb->callbackName = DEVICE_CHANGE_CALLBACK_NAME;
249             cb->deviceChangeAction = deviceChangeAction;
250             OnJsCallbackDeviceChange(cb);
251         }
252     }
253     return;
254 }
255 
OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> & jsCb)256 void AudioManagerCallbackNapi::OnJsCallbackDeviceChange(std::unique_ptr<AudioManagerJsCallback> &jsCb)
257 {
258     uv_loop_s *loop = nullptr;
259     napi_get_uv_event_loop(env_, &loop);
260     if (loop == nullptr) {
261         return;
262     }
263 
264     uv_work_t *work = new(std::nothrow) uv_work_t;
265     if (work == nullptr) {
266         AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: No memory");
267         return;
268     }
269 
270     if (jsCb.get() == nullptr) {
271         AUDIO_ERR_LOG("AudioManagerCallbackNapi: OnJsCallbackDeviceChange: jsCb.get() is null");
272         delete work;
273         return;
274     }
275 
276     work->data = reinterpret_cast<void *>(jsCb.get());
277 
278     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
279         // Js Thread
280         AudioManagerJsCallback *event = reinterpret_cast<AudioManagerJsCallback *>(work->data);
281         std::string request = event->callbackName;
282         napi_env env = event->callback->env_;
283         napi_ref callback = event->callback->cb_;
284         AUDIO_DEBUG_LOG("AudioManagerCallbackNapi: JsCallBack %{public}s, uv_queue_work start", request.c_str());
285         do {
286             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
287 
288             napi_value jsCallback = nullptr;
289             napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
290             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
291                 request.c_str());
292 
293             // Call back function
294             napi_value args[1] = { nullptr };
295             NativeDeviceChangeActionToJsObj(env, args[0], event->deviceChangeAction);
296             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
297                 "%{public}s fail to create DeviceChange callback", request.c_str());
298 
299             const size_t argCount = 1;
300             napi_value result = nullptr;
301             nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
302             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call DeviceChange callback", request.c_str());
303         } while (0);
304         delete event;
305         delete work;
306     });
307     if (ret != 0) {
308         AUDIO_ERR_LOG("Failed to execute libuv work queue");
309         delete work;
310     } else {
311         jsCb.release();
312     }
313 }
314 }  // namespace AudioStandard
315 }  // namespace OHOS
316