• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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