1 /*
2 * Copyright (c) 2023-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 "napi_avcontroller_callback.h"
17 #include "avsession_log.h"
18 #include "avsession_trace.h"
19 #include "napi_control_command.h"
20 #include "napi_meta_data.h"
21 #include "napi_playback_state.h"
22 #include "napi_media_description.h"
23 #include "napi_queue_item.h"
24 #include "napi_utils.h"
25
26 namespace OHOS::AVSession {
NapiAVControllerCallback()27 NapiAVControllerCallback::NapiAVControllerCallback()
28 {
29 SLOGI("Construct NapiAVControllerCallback");
30 isValid_ = std::make_shared<bool>(true);
31 }
32
~NapiAVControllerCallback()33 NapiAVControllerCallback::~NapiAVControllerCallback()
34 {
35 SLOGI("Destroy NapiAVControllerCallback");
36 *isValid_ = false;
37 }
38
HandleEvent(int32_t event)39 void NapiAVControllerCallback::HandleEvent(int32_t event)
40 {
41 std::lock_guard<std::mutex> lockGuard(lock_);
42 if (callbacks_[event].empty()) {
43 SLOGE("not register callback event=%{public}d", event);
44 return;
45 }
46 SLOGI("handle event for %{public}d", event);
47 for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
48 asyncCallback_->CallWithFunc(*ref, isValid_,
49 [this, ref, event]() {
50 std::lock_guard<std::mutex> lockGuard(lock_);
51 if (callbacks_[event].empty()) {
52 SLOGE("checkCallbackValid with empty list for event %{public}d", event);
53 return false;
54 }
55 bool hasFunc = false;
56 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
57 hasFunc = (ref == it ? true : hasFunc);
58 }
59 SLOGD("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
60 return hasFunc;
61 });
62 }
63 }
64
65 template<typename T>
HandleEvent(int32_t event,const T & param)66 void NapiAVControllerCallback::HandleEvent(int32_t event, const T& param)
67 {
68 std::lock_guard<std::mutex> lockGuard(lock_);
69 if (callbacks_[event].empty()) {
70 SLOGE("not register callback event=%{public}d", event);
71 return;
72 }
73 SLOGI("handle for event: %{public}d with size: %{public}d", event, static_cast<int>(callbacks_[event].size()));
74 for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
75 asyncCallback_->CallWithFunc(*ref, isValid_,
76 [this, ref, event]() {
77 std::lock_guard<std::mutex> lockGuard(lock_);
78 if (callbacks_[event].empty()) {
79 SLOGE("checkCallbackValid with empty list for event %{public}d", event);
80 return false;
81 }
82 bool hasFunc = false;
83 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
84 hasFunc = (ref == it ? true : hasFunc);
85 }
86 SLOGD("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
87 return hasFunc;
88 },
89 [param](napi_env env, int& argc, napi_value *argv) {
90 argc = NapiUtils::ARGC_ONE;
91 auto status = NapiUtils::SetValue(env, param, *argv);
92 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
93 });
94 }
95 }
96
97 template<typename T>
HandleEvent(int32_t event,const std::string & firstParam,const T & secondParam)98 void NapiAVControllerCallback::HandleEvent(int32_t event, const std::string& firstParam, const T& secondParam)
99 {
100 std::lock_guard<std::mutex> lockGuard(lock_);
101 if (callbacks_[event].empty()) {
102 SLOGE("not register callback event=%{public}d", event);
103 return;
104 }
105 SLOGI("handle event for %{public}d", event);
106 for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
107 asyncCallback_->CallWithFunc(*ref, isValid_,
108 [this, ref, event]() {
109 std::lock_guard<std::mutex> lockGuard(lock_);
110 if (callbacks_[event].empty()) {
111 SLOGE("checkCallbackValid with empty list for event %{public}d", event);
112 return false;
113 }
114 bool hasFunc = false;
115 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
116 hasFunc = (ref == it ? true : hasFunc);
117 }
118 SLOGD("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
119 return hasFunc;
120 },
121 [firstParam, secondParam](napi_env env, int& argc,
122 napi_value *argv) {
123 argc = NapiUtils::ARGC_TWO;
124 auto status = NapiUtils::SetValue(env, firstParam, argv[0]);
125 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
126 status = NapiUtils::SetValue(env, secondParam, argv[1]);
127 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
128 });
129 }
130 }
131
132 template<typename T>
HandleEvent(int32_t event,const int32_t firstParam,const T & secondParam)133 void NapiAVControllerCallback::HandleEvent(int32_t event, const int32_t firstParam, const T& secondParam)
134 {
135 std::lock_guard<std::mutex> lockGuard(lock_);
136 if (callbacks_[event].empty()) {
137 SLOGE("not register callback event=%{public}d", event);
138 return;
139 }
140 SLOGI("handle event for %{public}d", event);
141 for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
142 asyncCallback_->CallWithFunc(*ref, isValid_,
143 [this, ref, event]() {
144 std::lock_guard<std::mutex> lockGuard(lock_);
145 if (callbacks_[event].empty()) {
146 SLOGE("checkCallbackValid with empty list for event %{public}d", event);
147 return false;
148 }
149 bool hasFunc = false;
150 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
151 hasFunc = (ref == it ? true : hasFunc);
152 }
153 SLOGD("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
154 return hasFunc;
155 },
156 [firstParam, secondParam](napi_env env, int& argc,
157 napi_value *argv) {
158 argc = NapiUtils::ARGC_TWO;
159 auto status = NapiUtils::SetValue(env, firstParam, argv[0]);
160 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
161 status = NapiUtils::SetValue(env, secondParam, argv[1]);
162 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
163 });
164 }
165 }
166
167 template<typename T>
HandleEventWithThreadSafe(int32_t event,int state,const T & param)168 void NapiAVControllerCallback::HandleEventWithThreadSafe(int32_t event, int state, const T& param)
169 {
170 std::lock_guard<std::mutex> lockGuard(lock_);
171 if (callbacks_[event].empty()) {
172 SLOGE("not register callback event=%{public}d", event);
173 return;
174 }
175 SLOGI("event:%{public}d|num:%{public}d|state:%{public}d",
176 event, static_cast<int>(callbacks_[event].size()), state);
177 for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
178 CallWithThreadSafe(*ref, isValid_, state, threadSafeFunction_,
179 [this, ref, event]() {
180 if (callbacks_[event].empty()) {
181 SLOGE("checkCallbackValid with empty list for event %{public}d", event);
182 return false;
183 }
184 bool hasFunc = false;
185 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
186 hasFunc = (ref == it ? true : hasFunc);
187 }
188 SLOGD("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
189 return hasFunc;
190 },
191 [param](napi_env env, int& argc, napi_value *argv) {
192 argc = NapiUtils::ARGC_ONE;
193 auto status = NapiUtils::SetValue(env, param, *argv);
194 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
195 });
196 }
197 }
198
CallWithThreadSafe(napi_ref & method,std::shared_ptr<bool> isValid,int state,napi_threadsafe_function threadSafeFunction,const std::function<bool ()> & checkCallbackValid,NapiArgsGetter getter)199 void NapiAVControllerCallback::CallWithThreadSafe(napi_ref& method, std::shared_ptr<bool> isValid, int state,
200 napi_threadsafe_function threadSafeFunction, const std::function<bool()>& checkCallbackValid, NapiArgsGetter getter)
201 {
202 CHECK_RETURN_VOID(method != nullptr, "method is nullptr");
203 SLOGD("do CallWithThreadSafe with state %{public}d", state);
204 DataContextForThreadSafe* data =
205 new DataContextForThreadSafe { method, state, isValid, std::move(getter), checkCallbackValid };
206 if (threadSafeFunction != nullptr) {
207 SLOGD("do CallWithThreadSafe check threadSafeFunction alive and cache data");
208 napi_call_threadsafe_function(threadSafeFunction, data, napi_tsfn_nonblocking);
209 } else {
210 SLOGE("do CallWithThreadSafe check threadSafeFunction with null");
211 delete data;
212 data = nullptr;
213 }
214 SLOGD("do CallWithThreadSafe with state %{public}d done", state);
215 }
216
ThreadSafeCallback(napi_env env,napi_value js_cb,void * context,void * data)217 void NapiAVControllerCallback::ThreadSafeCallback(napi_env env, napi_value js_cb, void* context, void* data)
218 {
219 SLOGD("do ThreadSafeCallback in");
220 AVSESSION_TRACE_SYNC_START("NapiAsyncCallback::ThreadSafeCallback");
221 std::shared_ptr<DataContextForThreadSafe> appData(static_cast<DataContextForThreadSafe*>(data),
222 [](DataContextForThreadSafe* ptr) {
223 delete ptr;
224 ptr = nullptr;
225 });
226
227 int argc = 0;
228 napi_value argv[ARGC_MAX] = { nullptr };
229 if (appData->getter) {
230 argc = ARGC_MAX;
231 appData->getter(env, argc, argv);
232 }
233
234 SLOGD("queue uv_after_work_cb with state %{public}d", static_cast<int>(appData->state));
235 if (!*appData->isValid) {
236 SLOGE("ThreadSafeCallback failed for appData is invalid.");
237 return;
238 }
239 napi_value global {};
240 napi_get_global(env, &global);
241 napi_value function {};
242 if (!appData->checkCallbackValid()) {
243 SLOGE("Get func reference failed for func has been deleted.");
244 return;
245 }
246 napi_get_reference_value(env, appData->method, &function);
247 napi_value result;
248 if (!appData->checkCallbackValid()) {
249 SLOGE("Call func failed for func has been deleted.");
250 return;
251 }
252 napi_status status = napi_call_function(env, global, function, argc, argv, &result);
253 if (status != napi_ok) {
254 SLOGE("call function failed status=%{public}d.", status);
255 }
256 SLOGD("do ThreadSafeCallback done with state %{public}d", static_cast<int>(appData->state));
257 }
258
OnAVCallStateChange(const AVCallState & avCallState)259 void NapiAVControllerCallback::OnAVCallStateChange(const AVCallState& avCallState)
260 {
261 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnAVCallStateChange");
262 HandleEvent(EVENT_AVCALL_STATE_CHANGE, avCallState);
263 }
264
OnAVCallMetaDataChange(const AVCallMetaData & avCallMetaData)265 void NapiAVControllerCallback::OnAVCallMetaDataChange(const AVCallMetaData& avCallMetaData)
266 {
267 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnAVCallMetaDataChange");
268 HandleEvent(EVENT_AVCALL_META_DATA_CHANGE, avCallMetaData);
269 }
270
OnSessionDestroy()271 void NapiAVControllerCallback::OnSessionDestroy()
272 {
273 HandleEvent(EVENT_SESSION_DESTROY);
274 SLOGD("callback for sessionDestroy, check callback");
275 if (sessionDestroyCallback_ != nullptr) {
276 SLOGI("notify session Destroy for repeat");
277 sessionDestroyCallback_();
278 sessionDestroyCallback_ = nullptr;
279 SLOGD("notify session Destroy for repeat done");
280 }
281 }
282
OnCustomData(const AAFwk::WantParams & data)283 void NapiAVControllerCallback::OnCustomData(const AAFwk::WantParams& data)
284 {
285 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnCustomData");
286 HandleEvent(EVENT_CUSTOM_DATA, data);
287 }
288
OnPlaybackStateChange(const AVPlaybackState & state)289 void NapiAVControllerCallback::OnPlaybackStateChange(const AVPlaybackState& state)
290 {
291 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnPlaybackStateChange");
292 SLOGD("OnPlaybackStateChange %{public}d", state.GetState());
293 HandleEventWithThreadSafe(EVENT_PLAYBACK_STATE_CHANGE, state.GetState(), state);
294 }
295
OnMetaDataChange(const AVMetaData & data)296 void NapiAVControllerCallback::OnMetaDataChange(const AVMetaData& data)
297 {
298 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnMetaDataChange");
299 SLOGI("do metadata change notify with title %{public}s", data.GetTitle().c_str());
300 HandleEventWithThreadSafe(EVENT_META_DATA_CHANGE, -1, data);
301 }
302
OnActiveStateChange(bool isActive)303 void NapiAVControllerCallback::OnActiveStateChange(bool isActive)
304 {
305 HandleEvent(EVENT_ACTIVE_STATE_CHANGE, isActive);
306 }
307
OnValidCommandChange(const std::vector<int32_t> & cmds)308 void NapiAVControllerCallback::OnValidCommandChange(const std::vector<int32_t>& cmds)
309 {
310 SLOGI("do OnValidCommandChange in NapiCallback with size %{public}d", static_cast<int>(cmds.size()));
311 std::vector<std::string> stringCmds = NapiControlCommand::ConvertCommands(cmds);
312 HandleEventWithThreadSafe(EVENT_VALID_COMMAND_CHANGE, static_cast<int>(cmds.size()), stringCmds);
313 }
314
OnOutputDeviceChange(const int32_t connectionState,const OutputDeviceInfo & info)315 void NapiAVControllerCallback::OnOutputDeviceChange(const int32_t connectionState, const OutputDeviceInfo& info)
316 {
317 HandleEvent(EVENT_OUTPUT_DEVICE_CHANGE, connectionState, info);
318 }
319
OnSessionEventChange(const std::string & event,const AAFwk::WantParams & args)320 void NapiAVControllerCallback::OnSessionEventChange(const std::string& event, const AAFwk::WantParams& args)
321 {
322 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnSessionEventChange");
323 HandleEvent(EVENT_SESSION_EVENT_CHANGE, event, args);
324 }
325
OnQueueItemsChange(const std::vector<AVQueueItem> & items)326 void NapiAVControllerCallback::OnQueueItemsChange(const std::vector<AVQueueItem>& items)
327 {
328 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueItemsChange");
329 HandleEvent(EVENT_QUEUE_ITEMS_CHANGE, items);
330 }
331
OnQueueTitleChange(const std::string & title)332 void NapiAVControllerCallback::OnQueueTitleChange(const std::string& title)
333 {
334 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueTitleChange");
335 HandleEvent(EVENT_QUEUE_TITLE_CHANGE, title);
336 }
337
OnExtrasChange(const AAFwk::WantParams & extras)338 void NapiAVControllerCallback::OnExtrasChange(const AAFwk::WantParams& extras)
339 {
340 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnExtrasChange");
341 HandleEvent(EVENT_EXTRAS_CHANGE, extras);
342 }
343
AddCallback(napi_env env,int32_t event,napi_value callback)344 napi_status NapiAVControllerCallback::AddCallback(napi_env env, int32_t event, napi_value callback)
345 {
346 std::lock_guard<std::mutex> lockGuard(lock_);
347 CHECK_AND_RETURN_RET_LOG(event >= 0 && event < EVENT_TYPE_MAX, napi_generic_failure, "has no event");
348 napi_ref ref = nullptr;
349
350 if (threadSafeFunction_ == nullptr) {
351 SLOGI("addcallback with thread safe init");
352 napi_value resourceName = nullptr;
353 napi_create_string_utf8(env, "ThreadSafeFunction in NapiAVControllerCallback", NAPI_AUTO_LENGTH, &resourceName);
354 napi_create_threadsafe_function(env, nullptr, nullptr, resourceName, 0, 1, nullptr, nullptr,
355 nullptr, ThreadSafeCallback, &threadSafeFunction_);
356 }
357 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
358 napi_generic_failure, "get callback reference failed");
359 CHECK_AND_RETURN_RET_LOG(ref == nullptr, napi_ok, "callback has been registered");
360 napi_status status = napi_create_reference(env, callback, NapiUtils::ARGC_ONE, &ref);
361 if (status != napi_ok) {
362 SLOGE("napi_create_reference failed");
363 return status;
364 }
365 if (asyncCallback_ == nullptr) {
366 asyncCallback_ = std::make_shared<NapiAsyncCallback>(env);
367 if (asyncCallback_ == nullptr) {
368 SLOGE("no memory");
369 return napi_generic_failure;
370 }
371 }
372 SLOGI("add callback with ref %{public}d", event);
373 callbacks_[event].push_back(ref);
374 return napi_ok;
375 }
376
RemoveCallback(napi_env env,int32_t event,napi_value callback)377 napi_status NapiAVControllerCallback::RemoveCallback(napi_env env, int32_t event, napi_value callback)
378 {
379 std::lock_guard<std::mutex> lockGuard(lock_);
380 SLOGI("remove callback for event %{public}d", event);
381 CHECK_AND_RETURN_RET_LOG(event >= 0 && event < EVENT_TYPE_MAX, napi_generic_failure, "has no event");
382 if (callback == nullptr) {
383 SLOGD("Remove callback, the callback is nullptr");
384 for (auto callbackRef = callbacks_[event].begin(); callbackRef != callbacks_[event].end(); ++callbackRef) {
385 napi_status ret = napi_delete_reference(env, *callbackRef);
386 CHECK_AND_RETURN_RET_LOG(ret == napi_ok, ret, "delete callback reference failed");
387 *callbackRef = nullptr;
388 }
389 callbacks_[event].clear();
390 return napi_ok;
391 }
392 napi_ref ref = nullptr;
393 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
394 napi_generic_failure, "get callback reference failed");
395 CHECK_AND_RETURN_RET_LOG(ref != nullptr, napi_ok, "callback has been remove");
396 SLOGI("remove single callback with ref %{public}d", event);
397 callbacks_[event].remove(ref);
398 return napi_delete_reference(env, ref);
399 }
400
AddCallbackForSessionDestroy(const std::function<void (void)> & sessionDestroyCallback)401 void NapiAVControllerCallback::AddCallbackForSessionDestroy(const std::function<void(void)>& sessionDestroyCallback)
402 {
403 SLOGE("add callback for session destroy notify");
404 sessionDestroyCallback_ = sessionDestroyCallback;
405 }
406 }
407