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
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
OnPlaybackStateChange(const AVPlaybackState & state)283 void NapiAVControllerCallback::OnPlaybackStateChange(const AVPlaybackState& state)
284 {
285 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnPlaybackStateChange");
286 SLOGD("OnPlaybackStateChange %{public}d", state.GetState());
287 HandleEventWithThreadSafe(EVENT_PLAYBACK_STATE_CHANGE, state.GetState(), state);
288 }
289
OnMetaDataChange(const AVMetaData & data)290 void NapiAVControllerCallback::OnMetaDataChange(const AVMetaData& data)
291 {
292 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnMetaDataChange");
293 SLOGI("do metadata change notify with title %{public}s", data.GetTitle().c_str());
294 HandleEventWithThreadSafe(EVENT_META_DATA_CHANGE, -1, data);
295 }
296
OnActiveStateChange(bool isActive)297 void NapiAVControllerCallback::OnActiveStateChange(bool isActive)
298 {
299 HandleEvent(EVENT_ACTIVE_STATE_CHANGE, isActive);
300 }
301
OnValidCommandChange(const std::vector<int32_t> & cmds)302 void NapiAVControllerCallback::OnValidCommandChange(const std::vector<int32_t>& cmds)
303 {
304 SLOGI("do OnValidCommandChange in NapiCallback with size %{public}d", static_cast<int>(cmds.size()));
305 std::vector<std::string> stringCmds = NapiControlCommand::ConvertCommands(cmds);
306 HandleEventWithThreadSafe(EVENT_VALID_COMMAND_CHANGE, static_cast<int>(cmds.size()), stringCmds);
307 }
308
OnOutputDeviceChange(const int32_t connectionState,const OutputDeviceInfo & info)309 void NapiAVControllerCallback::OnOutputDeviceChange(const int32_t connectionState, const OutputDeviceInfo& info)
310 {
311 HandleEvent(EVENT_OUTPUT_DEVICE_CHANGE, connectionState, info);
312 }
313
OnSessionEventChange(const std::string & event,const AAFwk::WantParams & args)314 void NapiAVControllerCallback::OnSessionEventChange(const std::string& event, const AAFwk::WantParams& args)
315 {
316 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnSessionEventChange");
317 HandleEvent(EVENT_SESSION_EVENT_CHANGE, event, args);
318 }
319
OnQueueItemsChange(const std::vector<AVQueueItem> & items)320 void NapiAVControllerCallback::OnQueueItemsChange(const std::vector<AVQueueItem>& items)
321 {
322 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueItemsChange");
323 HandleEvent(EVENT_QUEUE_ITEMS_CHANGE, items);
324 }
325
OnQueueTitleChange(const std::string & title)326 void NapiAVControllerCallback::OnQueueTitleChange(const std::string& title)
327 {
328 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueTitleChange");
329 HandleEvent(EVENT_QUEUE_TITLE_CHANGE, title);
330 }
331
OnExtrasChange(const AAFwk::WantParams & extras)332 void NapiAVControllerCallback::OnExtrasChange(const AAFwk::WantParams& extras)
333 {
334 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnExtrasChange");
335 HandleEvent(EVENT_EXTRAS_CHANGE, extras);
336 }
337
AddCallback(napi_env env,int32_t event,napi_value callback)338 napi_status NapiAVControllerCallback::AddCallback(napi_env env, int32_t event, napi_value callback)
339 {
340 std::lock_guard<std::mutex> lockGuard(lock_);
341 napi_ref ref = nullptr;
342
343 if (threadSafeFunction_ == nullptr) {
344 SLOGI("addcallback with thread safe init");
345 napi_value resourceName = nullptr;
346 napi_create_string_utf8(env, "ThreadSafeFunction in NapiAVControllerCallback", NAPI_AUTO_LENGTH, &resourceName);
347 napi_create_threadsafe_function(env, nullptr, nullptr, resourceName, 0, 1, nullptr, nullptr,
348 nullptr, ThreadSafeCallback, &threadSafeFunction_);
349 }
350 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
351 napi_generic_failure, "get callback reference failed");
352 CHECK_AND_RETURN_RET_LOG(ref == nullptr, napi_ok, "callback has been registered");
353 napi_status status = napi_create_reference(env, callback, NapiUtils::ARGC_ONE, &ref);
354 if (status != napi_ok) {
355 SLOGE("napi_create_reference failed");
356 return status;
357 }
358 if (asyncCallback_ == nullptr) {
359 asyncCallback_ = std::make_shared<NapiAsyncCallback>(env);
360 if (asyncCallback_ == nullptr) {
361 SLOGE("no memory");
362 return napi_generic_failure;
363 }
364 }
365 SLOGI("add callback with ref %{public}d", event);
366 callbacks_[event].push_back(ref);
367 return napi_ok;
368 }
369
RemoveCallback(napi_env env,int32_t event,napi_value callback)370 napi_status NapiAVControllerCallback::RemoveCallback(napi_env env, int32_t event, napi_value callback)
371 {
372 std::lock_guard<std::mutex> lockGuard(lock_);
373 SLOGI("remove callback for event %{public}d", event);
374 if (callback == nullptr) {
375 SLOGD("Remove callback, the callback is nullptr");
376 for (auto callbackRef = callbacks_[event].begin(); callbackRef != callbacks_[event].end(); ++callbackRef) {
377 napi_status ret = napi_delete_reference(env, *callbackRef);
378 CHECK_AND_RETURN_RET_LOG(ret == napi_ok, ret, "delete callback reference failed");
379 *callbackRef = nullptr;
380 }
381 callbacks_[event].clear();
382 return napi_ok;
383 }
384 napi_ref ref = nullptr;
385 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
386 napi_generic_failure, "get callback reference failed");
387 CHECK_AND_RETURN_RET_LOG(ref != nullptr, napi_ok, "callback has been remove");
388 SLOGI("remove single callback with ref %{public}d", event);
389 callbacks_[event].remove(ref);
390 return napi_delete_reference(env, ref);
391 }
392
AddCallbackForSessionDestroy(const std::function<void (void)> & sessionDestroyCallback)393 void NapiAVControllerCallback::AddCallbackForSessionDestroy(const std::function<void(void)>& sessionDestroyCallback)
394 {
395 SLOGE("add callback for session destroy notify");
396 sessionDestroyCallback_ = sessionDestroyCallback;
397 }
398 }
399