1 /*
2  * Copyright (c) 2023 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 "NapiAudioManagerInterruptCallback"
17 #endif
18 
19 #include "napi_audio_manager_interrupt_callback.h"
20 #include "napi_audio_enum.h"
21 #include "napi_audio_error.h"
22 #include "napi_param_utils.h"
23 #include "audio_errors.h"
24 #include "audio_manager_log.h"
25 #include "napi_audio_manager_callbacks.h"
26 
27 namespace OHOS {
28 namespace AudioStandard {
NapiAudioManagerInterruptCallback(napi_env env)29 NapiAudioManagerInterruptCallback::NapiAudioManagerInterruptCallback(napi_env env)
30     : env_(env)
31 {
32     AUDIO_INFO_LOG("instance create");
33 }
34 
~NapiAudioManagerInterruptCallback()35 NapiAudioManagerInterruptCallback::~NapiAudioManagerInterruptCallback()
36 {
37     AUDIO_INFO_LOG("instance destroy");
38 }
39 
SaveCallbackReference(const std::string & callbackName,napi_value args)40 void NapiAudioManagerInterruptCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
41 {
42     CHECK_AND_RETURN_LOG(!callbackName.compare(INTERRUPT_CALLBACK_NAME),
43         "SaveCallbackReference: Unknown callback type: %{public}s", callbackName.c_str());
44 
45     std::lock_guard<std::mutex> lock(mutex_);
46     bool isSameCallback = true;
47     for (auto it = audioManagerInterruptCallbackList_.begin(); it != audioManagerInterruptCallbackList_.end(); ++it) {
48         isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, args, (*it)->cb_);
49         CHECK_AND_RETURN_LOG(!isSameCallback, "SaveCallbackReference: the callback already exists");
50     }
51     napi_ref callback = nullptr;
52     const int32_t refCount = 1;
53     napi_status status = napi_create_reference(env_, args, refCount, &callback);
54     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
55         "SaveCallbackReference: creating reference for callback fail");
56     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
57     audioManagerInterruptCallbackList_.push_back(cb);
58     AUDIO_INFO_LOG("SaveCallbackReference success, list size [%{public}zu]", audioManagerInterruptCallbackList_.size());
59 }
60 
RemoveCallbackReference(const std::string & callbackName,napi_value args)61 void NapiAudioManagerInterruptCallback::RemoveCallbackReference(const std::string &callbackName, napi_value args)
62 {
63     CHECK_AND_RETURN_LOG(!callbackName.compare(INTERRUPT_CALLBACK_NAME),
64         "RemoveCallbackReference: Unknown callback type: %{public}s", callbackName.c_str());
65 
66     std::lock_guard<std::mutex> lock(mutex_);
67     for (auto it = audioManagerInterruptCallbackList_.begin(); it != audioManagerInterruptCallbackList_.end(); ++it) {
68         bool isSameCallback = NapiAudioManagerCallback::IsSameCallback(env_, args, (*it)->cb_);
69         if (isSameCallback) {
70             napi_status status = napi_delete_reference(env_, (*it)->cb_);
71             (*it)->cb_ = nullptr;
72             CHECK_AND_RETURN_LOG(status == napi_ok, "RemoveCallbackReference: delete reference for callback fail");
73             audioManagerInterruptCallbackList_.erase(it);
74             AUDIO_INFO_LOG("RemoveCallbackReference success, list size [%{public}zu]",
75                 audioManagerInterruptCallbackList_.size());
76             return;
77         }
78     }
79     AUDIO_ERR_LOG("RemoveCallbackReference: js callback no find");
80 }
81 
RemoveAllCallbackReferences(const std::string & callbackName)82 void NapiAudioManagerInterruptCallback::RemoveAllCallbackReferences(const std::string &callbackName)
83 {
84     CHECK_AND_RETURN_LOG(!callbackName.compare(INTERRUPT_CALLBACK_NAME),
85         "RemoveCallbackReference: Unknown callback type: %{public}s", callbackName.c_str());
86 
87     std::lock_guard<std::mutex> lock(mutex_);
88     for (auto it = audioManagerInterruptCallbackList_.begin(); it != audioManagerInterruptCallbackList_.end(); ++it) {
89         napi_status ret = napi_delete_reference(env_, (*it)->cb_);
90         if (ret != napi_ok) {
91             AUDIO_ERR_LOG("RemoveAllCallbackReferences: napi_delete_reference err.");
92         }
93         (*it)->cb_ = nullptr;
94     }
95     audioManagerInterruptCallbackList_.clear();
96     AUDIO_INFO_LOG("RemoveAllCallbackReference: remove all js callbacks success");
97 }
98 
GetInterruptCallbackListSize()99 int32_t NapiAudioManagerInterruptCallback::GetInterruptCallbackListSize()
100 {
101     std::lock_guard<std::mutex> lock(mutex_);
102     return audioManagerInterruptCallbackList_.size();
103 }
104 
OnInterrupt(const InterruptAction & interruptAction)105 void NapiAudioManagerInterruptCallback::OnInterrupt(const InterruptAction &interruptAction)
106 {
107     std::lock_guard<std::mutex> lock(mutex_);
108     AUDIO_INFO_LOG("OnInterrupt action: %{public}d IntType: %{public}d, IntHint: %{public}d, activated: %{public}d",
109         interruptAction.actionType, interruptAction.interruptType, interruptAction.interruptHint,
110         interruptAction.activated);
111     CHECK_AND_RETURN_LOG(audioManagerInterruptCallbackList_.size() != 0,
112         "Cannot find the reference of interrupt callback");
113     for (auto it = audioManagerInterruptCallbackList_.begin(); it != audioManagerInterruptCallbackList_.end(); ++it) {
114         std::unique_ptr<AudioManagerInterruptJsCallback> cb = std::make_unique<AudioManagerInterruptJsCallback>();
115         CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
116         cb->callback = *it;
117         cb->callbackName = INTERRUPT_CALLBACK_NAME;
118         cb->interruptAction = interruptAction;
119         OnJsCallbackAudioManagerInterrupt(cb);
120     }
121 }
122 
WorkCallbackInterruptDone(uv_work_t * work,int status)123 void NapiAudioManagerInterruptCallback::WorkCallbackInterruptDone(uv_work_t *work, int status)
124 {
125     std::shared_ptr<AudioManagerInterruptJsCallback> context(
126         static_cast<AudioManagerInterruptJsCallback*>(work->data),
127         [work](AudioManagerInterruptJsCallback* ptr) {
128             delete ptr;
129             delete work;
130     });
131     CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
132     AudioManagerInterruptJsCallback *event = reinterpret_cast<AudioManagerInterruptJsCallback *>(work->data);
133     CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr");
134     std::string request = event->callbackName;
135     CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
136     napi_env env = event->callback->env_;
137     napi_ref callback = event->callback->cb_;
138     napi_handle_scope scope = nullptr;
139     napi_open_handle_scope(env, &scope);
140     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
141     AUDIO_DEBUG_LOG("JsCallBack %{public}s, uv_queue_work_with_qos start", request.c_str());
142     do {
143         CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
144         napi_value jsCallback = nullptr;
145         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
146         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
147             request.c_str());
148         napi_value args[ARGS_ONE] = { nullptr };
149         NapiParamUtils::SetValueInterruptAction(env, event->interruptAction, args[PARAM0]);
150         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
151             "%{public}s fail to create Interrupt callback", request.c_str());
152 
153         const size_t argCount = ARGS_ONE;
154         napi_value result = nullptr;
155         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
156         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback",
157             request.c_str());
158     } while (0);
159     napi_close_handle_scope(env, scope);
160 }
161 
OnJsCallbackAudioManagerInterrupt(std::unique_ptr<AudioManagerInterruptJsCallback> & jsCb)162 void NapiAudioManagerInterruptCallback::OnJsCallbackAudioManagerInterrupt(
163     std::unique_ptr<AudioManagerInterruptJsCallback> &jsCb)
164 {
165     uv_loop_s *loop = nullptr;
166     napi_get_uv_event_loop(env_, &loop);
167     CHECK_AND_RETURN_LOG(loop != nullptr, "loop is nullptr");
168 
169     uv_work_t *work = new(std::nothrow) uv_work_t;
170     CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackAudioManagerInterrupt: No memory");
171 
172     if (jsCb.get() == nullptr) {
173         AUDIO_ERR_LOG("OnJsCallbackAudioManagerInterrupt: jsCb.get() is null");
174         delete work;
175         return;
176     }
177     work->data = reinterpret_cast<void *>(jsCb.get());
178     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackInterruptDone,
179         uv_qos_default);
180     if (ret != 0) {
181         AUDIO_ERR_LOG("OnJsCallbackAudioManagerInterrupt: Failed to execute libuv work queue");
182         delete work;
183     } else {
184         jsCb.release();
185     }
186 }
187 } // namespace AudioStandard
188 } // namespace OHOS