1 /*
2 * Copyright (c) 2025 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 "NapiActiveVolumeTypeChangeCallback"
17 #endif
18
19 #include "napi_active_volume_type_change_callback.h"
20 #include "js_native_api.h"
21 #include "audio_errors.h"
22 #include "audio_manager_log.h"
23 #include "napi_param_utils.h"
24 #include "napi_audio_error.h"
25 #include "napi_audio_enum.h"
26 #include "napi/native_api.h"
27
28 namespace OHOS {
29 namespace AudioStandard {
NapiAudioManagerActiveVolumeTypeChangeCallback(napi_env env)30 NapiAudioManagerActiveVolumeTypeChangeCallback::NapiAudioManagerActiveVolumeTypeChangeCallback(napi_env env)
31 : env_(env)
32 {
33 AUDIO_DEBUG_LOG("instance create");
34 }
35
~NapiAudioManagerActiveVolumeTypeChangeCallback()36 NapiAudioManagerActiveVolumeTypeChangeCallback::~NapiAudioManagerActiveVolumeTypeChangeCallback()
37 {
38 if (regAmActiveVolumeTypeChgTsfn_) {
39 napi_release_threadsafe_function(amActiveVolumeTypeChgTsfn_, napi_tsfn_abort);
40 }
41 AUDIO_DEBUG_LOG("instance destroy");
42 }
43
CreateManagerActiveVolumeTypeChangeTsfn(napi_env env)44 void NapiAudioManagerActiveVolumeTypeChangeCallback::CreateManagerActiveVolumeTypeChangeTsfn(napi_env env)
45 {
46 regAmActiveVolumeTypeChgTsfn_ = true;
47 napi_value cbName;
48 std::string callbackName = "ManagerActiveVolumeTypeChange";
49 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
50 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr, ActiveVolumeTypeChangeTsfnFinalize,
51 nullptr, SafeJsCallbackActiveVolumeTypeChangeWork, &amActiveVolumeTypeChgTsfn_);
52 }
53
ActiveVolumeTypeChangeTsfnFinalize(napi_env env,void * data,void * hint)54 void NapiAudioManagerActiveVolumeTypeChangeCallback::ActiveVolumeTypeChangeTsfnFinalize(napi_env env, void *data,
55 void *hint)
56 {
57 AUDIO_INFO_LOG("safe thread resource release.");
58 }
59
SafeJsCallbackActiveVolumeTypeChangeWork(napi_env env,napi_value js_cb,void * context,void * data)60 void NapiAudioManagerActiveVolumeTypeChangeCallback::SafeJsCallbackActiveVolumeTypeChangeWork(
61 napi_env env, napi_value js_cb, void *context, void *data)
62 {
63 AudioManagerActiveVolumeTypeChangeJsCallback *event =
64 reinterpret_cast<AudioManagerActiveVolumeTypeChangeJsCallback *>(data);
65 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
66 "no memory");
67 std::shared_ptr<AudioManagerActiveVolumeTypeChangeJsCallback> safeContext(
68 static_cast<AudioManagerActiveVolumeTypeChangeJsCallback*>(data),
69 [](AudioManagerActiveVolumeTypeChangeJsCallback *ptr) {
70 delete ptr;
71 });
72 std::string request = event->callbackName;
73 napi_ref callback = event->callback->cb_;
74 napi_handle_scope scope = nullptr;
75 napi_open_handle_scope(env, &scope);
76 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
77 AUDIO_INFO_LOG("safe js callback working.");
78
79 do {
80 napi_value jsCallback = nullptr;
81 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
82 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "get reference value fail, %{public}s",
83 request.c_str());
84 napi_value args[ARGS_ONE] = { nullptr };
85 NapiParamUtils::SetValueInt32(env, NapiAudioEnum::GetJsAudioVolumeType(event->activeVolumeTypeChangeEvent),
86 args[PARAM0]);
87 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
88 "fail to create active volume type change callback, %{public}s", request.c_str());
89 const size_t argCount = ARGS_ONE;
90 napi_value result = nullptr;
91 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
92 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "fail to call DeviceChange callback, %{public}s",
93 request.c_str());
94 } while (0);
95 napi_close_handle_scope(env, scope);
96 }
97
OnActiveVolumeTypeChanged(const AudioVolumeType & event)98 void NapiAudioManagerActiveVolumeTypeChangeCallback::OnActiveVolumeTypeChanged(const AudioVolumeType &event)
99 {
100 std::lock_guard<std::mutex> lock(mutex_);
101 AUDIO_DEBUG_LOG("callback change enter");
102 for (auto iter : activeVolumeTypeChangeList_) {
103 std::unique_ptr<AudioManagerActiveVolumeTypeChangeJsCallback> cb =
104 std::make_unique<AudioManagerActiveVolumeTypeChangeJsCallback>();
105 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
106 cb->callback = iter;
107 cb->callbackName = ACTIVE_VOLUME_TYPE_CHANGE_CALLBACK_NAME;
108 cb->activeVolumeTypeChangeEvent = event;
109 OnJsCallbackActiveVolumeTypeChange(cb);
110 }
111 }
112
OnJsCallbackActiveVolumeTypeChange(std::unique_ptr<AudioManagerActiveVolumeTypeChangeJsCallback> & jsCb)113 void NapiAudioManagerActiveVolumeTypeChangeCallback::OnJsCallbackActiveVolumeTypeChange(
114 std::unique_ptr<AudioManagerActiveVolumeTypeChangeJsCallback> &jsCb)
115 {
116 if (jsCb.get() == nullptr) {
117 AUDIO_ERR_LOG("jsCb.get() is null");
118 return;
119 }
120 AudioManagerActiveVolumeTypeChangeJsCallback *event = jsCb.release();
121 CHECK_AND_RETURN_LOG(event != nullptr, "event is nullptr.");
122 CHECK_AND_RETURN_LOG(event->callback != nullptr, "callback is nullptr.");
123
124 napi_acquire_threadsafe_function(amActiveVolumeTypeChgTsfn_);
125 napi_call_threadsafe_function(amActiveVolumeTypeChgTsfn_, event, napi_tsfn_blocking);
126 }
127
SaveActiveVolumeTypeChangeCallbackReference(const std::string & callbackName,napi_value args)128 void NapiAudioManagerActiveVolumeTypeChangeCallback::SaveActiveVolumeTypeChangeCallbackReference(
129 const std::string &callbackName, napi_value args)
130 {
131 std::lock_guard<std::mutex> lock(mutex_);
132 for (auto iter : activeVolumeTypeChangeList_) {
133 if (IsSameCallback(env_, args, iter->cb_)) {
134 AUDIO_ERR_LOG("activeVolumeTypeChangeList_ has same callback, nothing to do");
135 return;
136 }
137 }
138 napi_ref callback = nullptr;
139 const int32_t refCount = ARGS_ONE;
140 napi_status status = napi_create_reference(env_, args, refCount, &callback);
141 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
142 "creating reference for callback fail");
143 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
144 if (callbackName == ACTIVE_VOLUME_TYPE_CHANGE_CALLBACK_NAME) {
145 activeVolumeTypeChangeList_.push_back(cb);
146 } else {
147 AUDIO_ERR_LOG("Unknown callback type: %{public}s",
148 callbackName.c_str());
149 }
150 }
151
RemoveSelfActiveVolumeTypeChangeCbRef(napi_env env,napi_value callback)152 void NapiAudioManagerActiveVolumeTypeChangeCallback::RemoveSelfActiveVolumeTypeChangeCbRef(napi_env env,
153 napi_value callback)
154 {
155 std::lock_guard<std::mutex> lock(mutex_);
156 for (auto iter = activeVolumeTypeChangeList_.begin(); iter != activeVolumeTypeChangeList_.end();) {
157 if (IsSameCallback(env, callback, (*iter)->cb_)) {
158 AUDIO_INFO_LOG("find js callback, erase it");
159 activeVolumeTypeChangeList_.erase(iter++);
160 } else {
161 iter++;
162 }
163 }
164 AUDIO_INFO_LOG("remove callback finish");
165 }
166
IsSameCallback(napi_env env,napi_value callback,napi_ref refCallback)167 bool NapiAudioManagerActiveVolumeTypeChangeCallback::IsSameCallback(napi_env env, napi_value callback,
168 napi_ref refCallback)
169 {
170 bool isEquals = false;
171 napi_value copyValue = nullptr;
172
173 napi_get_reference_value(env, refCallback, ©Value);
174 if (napi_strict_equals(env, copyValue, callback, &isEquals) != napi_ok) {
175 AUDIO_ERR_LOG("get napi_strict_equals failed");
176 return false;
177 }
178
179 return isEquals;
180 }
181
RemoveAllActiveVolumeTypeChangeCbRef()182 void NapiAudioManagerActiveVolumeTypeChangeCallback::RemoveAllActiveVolumeTypeChangeCbRef()
183 {
184 std::lock_guard<std::mutex> lock(mutex_);
185 activeVolumeTypeChangeList_.clear();
186 AUDIO_INFO_LOG("remove callback finish");
187 }
188
RemoveCallbackReference(const napi_value args)189 void NapiAudioManagerActiveVolumeTypeChangeCallback::RemoveCallbackReference(const napi_value args)
190 {
191 std::lock_guard<std::mutex> lock(mutex_);
192 CHECK_AND_RETURN_LOG(activeVolumeTypeChangeCallback_ != nullptr, "activeVolumeTypeChangeCallback_ is null");
193
194 napi_delete_reference(env_, activeVolumeTypeChangeCallback_->cb_);
195 activeVolumeTypeChangeCallback_->cb_ = nullptr;
196 activeVolumeTypeChangeCallback_ = nullptr;
197 AUDIO_INFO_LOG("remove callback reference successful.");
198 }
199
GetActiveVolumeTypeChangeListSize()200 int32_t NapiAudioManagerActiveVolumeTypeChangeCallback::GetActiveVolumeTypeChangeListSize()
201 {
202 return activeVolumeTypeChangeList_.size();
203 }
204
GetManagerActiveVolumeTypeChangeTsfnFlag()205 bool NapiAudioManagerActiveVolumeTypeChangeCallback::GetManagerActiveVolumeTypeChangeTsfnFlag()
206 {
207 return regAmActiveVolumeTypeChgTsfn_;
208 }
209 }
210 }