• 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 
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