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
16 #include "system_tone_callback_napi.h"
17 #include "media_errors.h"
18 #include "system_sound_log.h"
19
20 namespace {
21 const std::string PLAY_FINISHED_CALLBACK_NAME = "playFinished";
22 const std::string ERROR_CALLBACK_NAME = "error";
23 const int32_t ALL_STREAMID = 0;
24
25 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "SystemTonePlayerCallbackNapi"};
26 }
27
28 namespace OHOS {
29 namespace Media {
SystemTonePlayerCallbackNapi(napi_env env)30 SystemTonePlayerCallbackNapi::SystemTonePlayerCallbackNapi(napi_env env)
31 : env_(env)
32 {
33 MEDIA_LOGD("SystemTonePlayerCallbackNapi: instance create");
34 }
35
~SystemTonePlayerCallbackNapi()36 SystemTonePlayerCallbackNapi::~SystemTonePlayerCallbackNapi()
37 {
38 MEDIA_LOGD("SystemTonePlayerCallbackNapi: instance destroy");
39 }
40
IsSameCallback(std::shared_ptr<AutoRef> cb,const napi_value args)41 bool SystemTonePlayerCallbackNapi::IsSameCallback(std::shared_ptr<AutoRef> cb, const napi_value args)
42 {
43 if (args == nullptr) {
44 MEDIA_LOGE("args is null");
45 return true;
46 }
47 if (cb == nullptr) {
48 MEDIA_LOGE("cb is null");
49 return false;
50 }
51 napi_value callback = nullptr;
52 napi_get_reference_value(env_, cb->cb_, &callback);
53 bool isEquals = false;
54 CHECK_AND_RETURN_RET_LOG(napi_strict_equals(env_, args, callback, &isEquals) == napi_ok, false,
55 "get napi_strict_equals failed");
56 return isEquals;
57 }
58
IsExistCallback(const std::list<std::shared_ptr<AutoRef>> & cbList,const napi_value args)59 bool SystemTonePlayerCallbackNapi::IsExistCallback(const std::list<std::shared_ptr<AutoRef>> &cbList,
60 const napi_value args)
61 {
62 if (args == nullptr) {
63 MEDIA_LOGE("args is null");
64 return true;
65 }
66 if (cbList.empty()) {
67 MEDIA_LOGD("cbList is empty");
68 return false;
69 }
70 for (auto &cb : cbList) {
71 if (IsSameCallback(cb, args)) {
72 return true;
73 }
74 }
75 return false;
76 }
77
SavePlayFinishedCallbackReference(const std::string & callbackName,napi_value args,int32_t streamId)78 void SystemTonePlayerCallbackNapi::SavePlayFinishedCallbackReference(const std::string &callbackName, napi_value args,
79 int32_t streamId)
80 {
81 std::lock_guard<std::mutex> lock(mutex_);
82 if (callbackName != PLAY_FINISHED_CALLBACK_NAME) {
83 MEDIA_LOGE("Unknown callback type: %{public}s", callbackName.c_str());
84 return;
85 }
86 if (playFinishedCallbackMap_.count(streamId) != 0 && IsExistCallback(playFinishedCallbackMap_[streamId], args)) {
87 MEDIA_LOGI("playFinished cb exist streamId: %{public}d", streamId);
88 return;
89 }
90
91 napi_ref callback = nullptr;
92 const int32_t refCount = 1;
93 napi_status status = napi_create_reference(env_, args, refCount, &callback);
94 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback fail");
95
96 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
97 playFinishedCallbackMap_[streamId].emplace_back(cb);
98 }
99
SaveCallbackReference(const std::string & callbackName,napi_value args)100 void SystemTonePlayerCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
101 {
102 std::lock_guard<std::mutex> lock(mutex_);
103 if (callbackName != ERROR_CALLBACK_NAME) {
104 MEDIA_LOGE("Unknown callback type: %{public}s", callbackName.c_str());
105 return;
106 }
107 if (IsExistCallback(errorCallback_, args)) {
108 MEDIA_LOGI("error cb exist");
109 return;
110 }
111
112 napi_ref callback = nullptr;
113 const int32_t refCount = 1;
114 napi_status status = napi_create_reference(env_, args, refCount, &callback);
115 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback fail");
116
117 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
118 errorCallback_.emplace_back(cb);
119 }
120
RemovePlayFinishedCallbackReference(int32_t streamId)121 void SystemTonePlayerCallbackNapi::RemovePlayFinishedCallbackReference(int32_t streamId)
122 {
123 std::lock_guard<std::mutex> lock(mutex_);
124 if (playFinishedCallbackMap_.count(streamId) != 0) {
125 MEDIA_LOGI("Remove PlayFinished Callback streamId:%{public}d", streamId);
126 playFinishedCallbackMap_.erase(streamId);
127 } else {
128 MEDIA_LOGI("Remove PlayFinished Callback streamId:%{public}d not exist", streamId);
129 }
130 }
131
RemoveCallbackReference(const std::string & callbackName,const napi_value & args)132 void SystemTonePlayerCallbackNapi::RemoveCallbackReference(const std::string &callbackName, const napi_value &args)
133 {
134 std::lock_guard<std::mutex> lock(mutex_);
135 if (callbackName == PLAY_FINISHED_CALLBACK_NAME) {
136 if (args == nullptr) {
137 playFinishedCallbackMap_.clear();
138 MEDIA_LOGI("remove playFinished all cb succeed");
139 return;
140 }
141 for (auto &playFinishedCallback : playFinishedCallbackMap_) {
142 auto it = std::find_if(playFinishedCallback.second.begin(), playFinishedCallback.second.end(),
143 [&args, this](const auto& cb) { return IsSameCallback(cb, args); });
144 if (it != playFinishedCallback.second.end()) {
145 playFinishedCallback.second.erase(it);
146 MEDIA_LOGI("remove playFinished cb succeed");
147 } else {
148 MEDIA_LOGE("remove playFinished cb failed");
149 }
150 }
151 } else if (callbackName == ERROR_CALLBACK_NAME) {
152 if (args == nullptr) {
153 errorCallback_.clear();
154 MEDIA_LOGI("remove error all cb succeed");
155 return;
156 }
157 auto it = std::find_if(errorCallback_.begin(), errorCallback_.end(),
158 [&args, this](const auto& cb) { return IsSameCallback(cb, args); });
159 if (it != errorCallback_.end()) {
160 errorCallback_.erase(it);
161 MEDIA_LOGI("remove error cb succeed");
162 } else {
163 MEDIA_LOGE("remove error cb failed");
164 }
165 } else {
166 MEDIA_LOGE("Unknown callback type: %{public}s", callbackName.c_str());
167 }
168 }
169
OnEndOfStream(int32_t streamId)170 void SystemTonePlayerCallbackNapi::OnEndOfStream(int32_t streamId)
171 {
172 std::lock_guard<std::mutex> lock(mutex_);
173 CHECK_AND_RETURN_LOG(playFinishedCallbackMap_.count(streamId) != 0 ||
174 playFinishedCallbackMap_.count(ALL_STREAMID) != 0,
175 "Cannot find the reference of playFinised callback, streamId: %{public}d", streamId);
176 MEDIA_LOGI("OnEndOfStream is called, streamId: %{public}d", streamId);
177
178 ProcessFinishedCallbacks(streamId, streamId);
179 ProcessFinishedCallbacks(ALL_STREAMID, streamId);
180 }
181
ProcessFinishedCallbacks(int32_t listenerId,int32_t streamId)182 void SystemTonePlayerCallbackNapi::ProcessFinishedCallbacks(int32_t listenerId, int32_t streamId)
183 {
184 if (playFinishedCallbackMap_.count(listenerId) == 0) {
185 MEDIA_LOGD("Cannot find the reference of playFinised callback, listenerId: %{public}d", listenerId);
186 return;
187 }
188 for (auto &callback : playFinishedCallbackMap_[listenerId]) {
189 auto cb = std::make_shared<SystemTonePlayerJsCallback>();
190 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
191 cb->callback = callback;
192 cb->callbackName = PLAY_FINISHED_CALLBACK_NAME;
193 cb->streamId = streamId;
194 OnJsCallbackPlayFinished(cb);
195 }
196 }
197
OnJsCallbackPlayFinished(std::shared_ptr<SystemTonePlayerJsCallback> & jsCb)198 void SystemTonePlayerCallbackNapi::OnJsCallbackPlayFinished(std::shared_ptr<SystemTonePlayerJsCallback> &jsCb)
199 {
200 if (jsCb == nullptr) {
201 MEDIA_LOGE("OnJsCallbackPlayFinished: jsCb is null");
202 return;
203 }
204 auto task = [event = std::move(jsCb)]() {
205 std::string request = event->callbackName;
206 napi_env env = event->callback->env_;
207 napi_ref callback = event->callback->cb_;
208 napi_handle_scope scope = nullptr;
209 napi_open_handle_scope(env, &scope);
210 MEDIA_LOGI("JsCallBack %{public}s, uv_queue_work start", request.c_str());
211 do {
212 napi_value jsCallback = nullptr;
213 napi_status napiStatus = napi_get_reference_value(env, callback, &jsCallback);
214 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && jsCallback != nullptr,
215 "%{public}s get reference value fail", request.c_str());
216
217 // Call back function
218 napi_value args[1] = { nullptr };
219 napiStatus = napi_create_int32(env, event->streamId, &args[0]);
220 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && args[0] != nullptr,
221 "%{public}s fail to create PlayFinished callback", request.c_str());
222
223 const size_t argCount = 1;
224 napi_value result = nullptr;
225 napiStatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
226 CHECK_AND_BREAK_LOG(napiStatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
227 } while (0);
228 napi_close_handle_scope(env, scope);
229 };
230 auto ret = napi_send_event(env_, task, napi_eprio_high);
231 if (ret != napi_status::napi_ok) {
232 MEDIA_LOGE("Failed to SendEvent, ret = %{public}d", ret);
233 }
234 }
235
OnError(int32_t errorCode)236 void SystemTonePlayerCallbackNapi::OnError(int32_t errorCode)
237 {
238 std::lock_guard<std::mutex> lock(mutex_);
239 CHECK_AND_RETURN_LOG(!errorCallback_.empty(), "Cannot find the reference of error callback");
240 MEDIA_LOGI("OnError is calleerrorCallback_d, errorCode: %{public}d", errorCode);
241
242 for (auto &callback : errorCallback_) {
243 auto cb = std::make_shared<SystemTonePlayerJsCallback>();
244 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
245 cb->callback = callback;
246 cb->callbackName = ERROR_CALLBACK_NAME;
247 cb->errorCode = errorCode;
248 cb->errorMsg = "error";
249 OnJsCallbackError(cb);
250 }
251 }
252
OnJsCallbackError(std::shared_ptr<SystemTonePlayerJsCallback> & jsCb)253 void SystemTonePlayerCallbackNapi::OnJsCallbackError(std::shared_ptr<SystemTonePlayerJsCallback> &jsCb)
254 {
255 if (jsCb == nullptr) {
256 MEDIA_LOGE("OnJsCallbackError: jsCb is null");
257 return;
258 }
259 auto task = [event = std::move(jsCb)]() {
260 std::string request = event->callbackName;
261 napi_env env = event->callback->env_;
262 napi_ref callback = event->callback->cb_;
263 napi_handle_scope scope = nullptr;
264 napi_open_handle_scope(env, &scope);
265 MEDIA_LOGI("JsCallBack %{public}s, uv_queue_work start", request.c_str());
266 do {
267 napi_value jsCallback = nullptr;
268 napi_status napiStatus = napi_get_reference_value(env, callback, &jsCallback);
269 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && jsCallback != nullptr,
270 "%{public}s get reference value fail", request.c_str());
271
272 // Call back function
273 napi_value msgValStr = nullptr;
274 napiStatus = napi_create_string_utf8(env, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
275 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && msgValStr != nullptr, "create error message str fail");
276
277 napi_value args[1] = { nullptr };
278 napiStatus = napi_create_error(env, nullptr, msgValStr, &args[0]);
279 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && args[0] != nullptr, "create error callback fail");
280
281 napiStatus = CommonNapi::FillErrorArgs(env, event->errorCode, args[0]);
282 CHECK_AND_BREAK_LOG(napiStatus == napi_ok, "create error callback fail");
283
284 napi_value result = nullptr;
285 napiStatus = napi_call_function(env, nullptr, jsCallback, 1, args, &result);
286 CHECK_AND_BREAK_LOG(napiStatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
287 } while (0);
288 napi_close_handle_scope(env, scope);
289 };
290 auto ret = napi_send_event(env_, task, napi_eprio_high);
291 if (ret != napi_status::napi_ok) {
292 MEDIA_LOGE("Failed to SendEvent, ret = %{public}d", ret);
293 }
294 }
295 } // namespace Media
296 } // namespace OHOS