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("handle with thead safe without lock for event: %{public}d with num: %{public}d with 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 }
213 SLOGD("do CallWithThreadSafe with state %{public}d done", state);
214 }
215
ThreadSafeCallback(napi_env env,napi_value js_cb,void * context,void * data)216 void NapiAVControllerCallback::ThreadSafeCallback(napi_env env, napi_value js_cb, void* context, void* data)
217 {
218 SLOGD("do ThreadSafeCallback in");
219 AVSESSION_TRACE_SYNC_START("NapiAsyncCallback::ThreadSafeCallback");
220 std::shared_ptr<DataContextForThreadSafe> appData(static_cast<DataContextForThreadSafe*>(data),
221 [](DataContextForThreadSafe* ptr) {
222 delete ptr;
223 });
224
225 int argc = 0;
226 napi_value argv[ARGC_MAX] = { nullptr };
227 if (appData->getter) {
228 argc = ARGC_MAX;
229 appData->getter(env, argc, argv);
230 }
231
232 SLOGI("queue uv_after_work_cb with state %{public}d", static_cast<int>(appData->state));
233 if (!*appData->isValid) {
234 SLOGE("ThreadSafeCallback failed for appData is invalid.");
235 return;
236 }
237 napi_value global {};
238 napi_get_global(env, &global);
239 napi_value function {};
240 if (!appData->checkCallbackValid()) {
241 SLOGE("Get func reference failed for func has been deleted.");
242 return;
243 }
244 napi_get_reference_value(env, appData->method, &function);
245 napi_value result;
246 if (!appData->checkCallbackValid()) {
247 SLOGE("Call func failed for func has been deleted.");
248 return;
249 }
250 napi_status status = napi_call_function(env, global, function, argc, argv, &result);
251 if (status != napi_ok) {
252 SLOGE("call function failed status=%{public}d.", status);
253 }
254 SLOGD("do ThreadSafeCallback done with state %{public}d", static_cast<int>(appData->state));
255 }
256
OnAVCallStateChange(const AVCallState & avCallState)257 void NapiAVControllerCallback::OnAVCallStateChange(const AVCallState& avCallState)
258 {
259 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnAVCallStateChange");
260 HandleEvent(EVENT_AVCALL_STATE_CHANGE, avCallState);
261 }
262
OnAVCallMetaDataChange(const AVCallMetaData & avCallMetaData)263 void NapiAVControllerCallback::OnAVCallMetaDataChange(const AVCallMetaData& avCallMetaData)
264 {
265 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnAVCallMetaDataChange");
266 HandleEvent(EVENT_AVCALL_META_DATA_CHANGE, avCallMetaData);
267 }
268
OnSessionDestroy()269 void NapiAVControllerCallback::OnSessionDestroy()
270 {
271 HandleEvent(EVENT_SESSION_DESTROY);
272 SLOGD("callback for sessionDestroy, check callback");
273 if (sessionDestroyCallback_ != nullptr) {
274 SLOGI("notify session Destroy for repeat");
275 sessionDestroyCallback_();
276 sessionDestroyCallback_ = nullptr;
277 SLOGD("notify session Destroy for repeat done");
278 }
279 }
280
OnPlaybackStateChange(const AVPlaybackState & state)281 void NapiAVControllerCallback::OnPlaybackStateChange(const AVPlaybackState& state)
282 {
283 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnPlaybackStateChange");
284 SLOGI("do playbackstate change notify with state %{public}d", state.GetState());
285 HandleEventWithThreadSafe(EVENT_PLAYBACK_STATE_CHANGE, state.GetState(), state);
286 }
287
OnMetaDataChange(const AVMetaData & data)288 void NapiAVControllerCallback::OnMetaDataChange(const AVMetaData& data)
289 {
290 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnMetaDataChange");
291 SLOGI("do metadata change notify with title %{public}s", data.GetTitle().c_str());
292 HandleEventWithThreadSafe(EVENT_META_DATA_CHANGE, -1, data);
293 }
294
OnActiveStateChange(bool isActive)295 void NapiAVControllerCallback::OnActiveStateChange(bool isActive)
296 {
297 HandleEvent(EVENT_ACTIVE_STATE_CHANGE, isActive);
298 }
299
OnValidCommandChange(const std::vector<int32_t> & cmds)300 void NapiAVControllerCallback::OnValidCommandChange(const std::vector<int32_t>& cmds)
301 {
302 SLOGI("do OnValidCommandChange in NapiCallback with size %{public}d", static_cast<int>(cmds.size()));
303 std::vector<std::string> stringCmds = NapiControlCommand::ConvertCommands(cmds);
304 HandleEventWithThreadSafe(EVENT_VALID_COMMAND_CHANGE, static_cast<int>(cmds.size()), stringCmds);
305 }
306
OnOutputDeviceChange(const int32_t connectionState,const OutputDeviceInfo & info)307 void NapiAVControllerCallback::OnOutputDeviceChange(const int32_t connectionState, const OutputDeviceInfo& info)
308 {
309 HandleEvent(EVENT_OUTPUT_DEVICE_CHANGE, connectionState, info);
310 }
311
OnSessionEventChange(const std::string & event,const AAFwk::WantParams & args)312 void NapiAVControllerCallback::OnSessionEventChange(const std::string& event, const AAFwk::WantParams& args)
313 {
314 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnSessionEventChange");
315 HandleEvent(EVENT_SESSION_EVENT_CHANGE, event, args);
316 }
317
OnQueueItemsChange(const std::vector<AVQueueItem> & items)318 void NapiAVControllerCallback::OnQueueItemsChange(const std::vector<AVQueueItem>& items)
319 {
320 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueItemsChange");
321 HandleEvent(EVENT_QUEUE_ITEMS_CHANGE, items);
322 }
323
OnQueueTitleChange(const std::string & title)324 void NapiAVControllerCallback::OnQueueTitleChange(const std::string& title)
325 {
326 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnQueueTitleChange");
327 HandleEvent(EVENT_QUEUE_TITLE_CHANGE, title);
328 }
329
OnExtrasChange(const AAFwk::WantParams & extras)330 void NapiAVControllerCallback::OnExtrasChange(const AAFwk::WantParams& extras)
331 {
332 AVSESSION_TRACE_SYNC_START("NapiAVControllerCallback::OnExtrasChange");
333 HandleEvent(EVENT_EXTRAS_CHANGE, extras);
334 }
335
AddCallback(napi_env env,int32_t event,napi_value callback)336 napi_status NapiAVControllerCallback::AddCallback(napi_env env, int32_t event, napi_value callback)
337 {
338 std::lock_guard<std::mutex> lockGuard(lock_);
339 napi_ref ref = nullptr;
340
341 if (threadSafeFunction_ == nullptr) {
342 SLOGI("addcallback with thread safe init");
343 napi_value resourceName = nullptr;
344 napi_create_string_utf8(env, "ThreadSafeFunction in NapiAVControllerCallback", NAPI_AUTO_LENGTH, &resourceName);
345 napi_create_threadsafe_function(env, nullptr, nullptr, resourceName, 0, 1, nullptr, nullptr,
346 nullptr, ThreadSafeCallback, &threadSafeFunction_);
347 }
348 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
349 napi_generic_failure, "get callback reference failed");
350 CHECK_AND_RETURN_RET_LOG(ref == nullptr, napi_ok, "callback has been registered");
351 napi_status status = napi_create_reference(env, callback, NapiUtils::ARGC_ONE, &ref);
352 if (status != napi_ok) {
353 SLOGE("napi_create_reference failed");
354 return status;
355 }
356 if (asyncCallback_ == nullptr) {
357 asyncCallback_ = std::make_shared<NapiAsyncCallback>(env);
358 if (asyncCallback_ == nullptr) {
359 SLOGE("no memory");
360 return napi_generic_failure;
361 }
362 }
363 SLOGI("add callback with ref %{public}d, %{public}p, %{public}p", event, &ref, *(&ref));
364 callbacks_[event].push_back(ref);
365 return napi_ok;
366 }
367
RemoveCallback(napi_env env,int32_t event,napi_value callback)368 napi_status NapiAVControllerCallback::RemoveCallback(napi_env env, int32_t event, napi_value callback)
369 {
370 std::lock_guard<std::mutex> lockGuard(lock_);
371 SLOGI("remove callback for event %{public}d", event);
372 if (callback == nullptr) {
373 SLOGD("Remove callback, the callback is nullptr");
374 for (auto callbackRef = callbacks_[event].begin(); callbackRef != callbacks_[event].end(); ++callbackRef) {
375 napi_status ret = napi_delete_reference(env, *callbackRef);
376 CHECK_AND_RETURN_RET_LOG(ret == napi_ok, ret, "delete callback reference failed");
377 *callbackRef = nullptr;
378 }
379 callbacks_[event].clear();
380 return napi_ok;
381 }
382 napi_ref ref = nullptr;
383 CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
384 napi_generic_failure, "get callback reference failed");
385 CHECK_AND_RETURN_RET_LOG(ref != nullptr, napi_ok, "callback has been remove");
386 SLOGI("remove single callback with ref %{public}d, %{public}p, %{public}p", event, &ref, *(&ref));
387 callbacks_[event].remove(ref);
388 return napi_delete_reference(env, ref);
389 }
390
AddCallbackForSessionDestroy(const std::function<void (void)> & sessionDestroyCallback)391 void NapiAVControllerCallback::AddCallbackForSessionDestroy(const std::function<void(void)>& sessionDestroyCallback)
392 {
393 SLOGE("add callback for session destroy notify");
394 sessionDestroyCallback_ = sessionDestroyCallback;
395 }
396 }
397