• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 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 "avsession_errors.h"
17 #include "avsession_trace.h"
18 #include "napi_async_work.h"
19 #include "napi_avsession_manager.h"
20 #include "napi_avcast_picker_helper.h"
21 
22 namespace OHOS::AVSession {
23 
24 static const std::string ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE = "ability.want.params.uiExtensionType";
25 static const std::string ABILITY_WANT_ELEMENT_NAME = "com.ohos.mediacontroller";
26 
27 static __thread napi_ref AVCastPickerHelperConstructorRef = nullptr;
28 std::map<std::string, std::pair<NapiAVCastPickerHelper::OnEventHandlerType,
29     NapiAVCastPickerHelper::OffEventHandlerType>> NapiAVCastPickerHelper::eventHandlers_ = {
30     { "pickerStateChange", { OnPickerStateChange, OffPickerStateChange } },
31 };
32 
NapiAVCastPickerHelper(Ace::UIContent * uiContent)33 NapiAVCastPickerHelper::NapiAVCastPickerHelper(Ace::UIContent* uiContent)
34 {
35     SLOGI("construct");
36     uiContent_ = uiContent;
37     isValid_ = std::make_shared<bool>(true);
38 }
39 
~NapiAVCastPickerHelper()40 NapiAVCastPickerHelper::~NapiAVCastPickerHelper()
41 {
42     SLOGI("destroy");
43     *isValid_ = false;
44 }
45 
Init(napi_env env,napi_value exports)46 napi_value NapiAVCastPickerHelper::Init(napi_env env, napi_value exports)
47 {
48     napi_property_descriptor descriptors[] = {
49         DECLARE_NAPI_FUNCTION("select", SelectAVPicker),
50         DECLARE_NAPI_FUNCTION("on", OnEvent),
51         DECLARE_NAPI_FUNCTION("off", OffEvent),
52     };
53     auto propertyCount = sizeof(descriptors) / sizeof(napi_property_descriptor);
54     napi_value constructor {};
55     auto status = napi_define_class(env, "AVCastPickerHelper", NAPI_AUTO_LENGTH, ConstructorCallback, nullptr,
56                                     propertyCount, descriptors, &constructor);
57     if (status != napi_ok) {
58         SLOGE("define class failed");
59         return NapiUtils::GetUndefinedValue(env);
60     }
61     napi_create_reference(env, constructor, 1, &AVCastPickerHelperConstructorRef);
62     napi_set_named_property(env, exports, "AVCastPickerHelper", constructor);
63 
64     return exports;
65 }
66 
ConstructorCallback(napi_env env,napi_callback_info info)67 napi_value NapiAVCastPickerHelper::ConstructorCallback(napi_env env, napi_callback_info info)
68 {
69     struct ConcreteContext : public ContextBase {
70         Ace::UIContent* uiContent;
71     };
72     auto context = std::make_shared<ConcreteContext>();
73     if (context == nullptr) {
74         NapiUtils::ThrowError(env, "ConstructorCallback failed : no memory",
75             NapiAVSessionManager::errcode_[ERR_NO_MEMORY]);
76         return NapiUtils::GetUndefinedValue(env);
77     }
78     auto inputParser = [env, context](size_t argc, napi_value* argv) {
79         CHECK_ARGS_RETURN_VOID(context, argc == ARGC_ONE, "invalid arguments",
80             NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
81         auto stageContext = AbilityRuntime::GetStageModeContext(env, argv[ARGV_FIRST]);
82         if (stageContext == nullptr) {
83             context->status = napi_generic_failure;
84             NapiUtils::ThrowError(env, "get stageContext failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
85             return;
86         }
87         auto abilityContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::AbilityContext>(stageContext);
88         if (abilityContext != nullptr) {
89             context->uiContent = abilityContext->GetUIContent();
90         } else {
91             auto extensionContext =
92                 AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(stageContext);
93             CHECK_RETURN_VOID(extensionContext != nullptr, "convert to AbilityContext and ExtensionContext fail");
94             context->uiContent = extensionContext->GetUIContent();
95         }
96         context->status = napi_ok;
97     };
98     context->GetCbInfo(env, info, inputParser);
99     CHECK_AND_RETURN_RET_LOG(context->status == napi_ok, nullptr, "no need to wrap");
100 
101     auto* napiAVCastPickerHelper = new(std::nothrow) NapiAVCastPickerHelper(context->uiContent);
102     CHECK_AND_RETURN_RET_LOG(napiAVCastPickerHelper != nullptr, nullptr, "no memory");
103     auto finalize = [](napi_env env, void* data, void* hint) {
104         auto* napiAVCastPickerHelper = reinterpret_cast<NapiAVCastPickerHelper*>(data);
105         CHECK_AND_RETURN_LOG(napiAVCastPickerHelper != nullptr, "napiAVCastPickerHelper is nullptr");
106         napi_delete_reference(env, napiAVCastPickerHelper->wrapperRef_);
107         delete napiAVCastPickerHelper;
108         napiAVCastPickerHelper = nullptr;
109     };
110     if (napi_wrap(env, context->self, static_cast<void*>(napiAVCastPickerHelper),
111         finalize, nullptr, nullptr) != napi_ok) {
112         SLOGE("wrap failed");
113         delete napiAVCastPickerHelper;
114         napiAVCastPickerHelper = nullptr;
115         return nullptr;
116     }
117     return context->self;
118 }
119 
OnEvent(napi_env env,napi_callback_info info)120 napi_value NapiAVCastPickerHelper::OnEvent(napi_env env, napi_callback_info info)
121 {
122     auto context = std::make_shared<ContextBase>();
123     if (context == nullptr) {
124         SLOGE("OnEvent failed : no memory");
125         NapiUtils::ThrowError(env, "OnEvent failed : no memory", NapiAVSessionManager::errcode_[ERR_NO_MEMORY]);
126         return NapiUtils::GetUndefinedValue(env);
127     }
128 
129     std::string eventName;
130     napi_value callback {};
131     auto input = [&eventName, &callback, env, &context](size_t argc, napi_value* argv) {
132         CHECK_ARGS_RETURN_VOID(context, argc == ARGC_TWO, "invalid argument number",
133             NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
134         context->status = NapiUtils::GetValue(env, argv[ARGV_FIRST], eventName);
135         CHECK_STATUS_RETURN_VOID(context, "get event name failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
136         napi_valuetype type = napi_undefined;
137         context->status = napi_typeof(env, argv[ARGV_SECOND], &type);
138         CHECK_ARGS_RETURN_VOID(context, (context->status == napi_ok) && (type == napi_function),
139                                "callback type invalid", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
140         callback = argv[ARGV_SECOND];
141     };
142     context->GetCbInfo(env, info, input, true);
143     if (context->status != napi_ok) {
144         NapiUtils::ThrowError(env, context->errMessage.c_str(), context->errCode);
145         return NapiUtils::GetUndefinedValue(env);
146     }
147     auto it = eventHandlers_.find(eventName);
148     if (it == eventHandlers_.end()) {
149         SLOGE("event name invalid");
150         NapiUtils::ThrowError(env, "event name invalid", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
151         return NapiUtils::GetUndefinedValue(env);
152     }
153     auto* napiAVCastPickerHelper = reinterpret_cast<NapiAVCastPickerHelper*>(context->native);
154     if (it->second.first(env, napiAVCastPickerHelper, callback) != napi_ok) {
155         NapiUtils::ThrowError(env, "add event callback failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
156     }
157     return NapiUtils::GetUndefinedValue(env);
158 }
159 
OffEvent(napi_env env,napi_callback_info info)160 napi_value NapiAVCastPickerHelper::OffEvent(napi_env env, napi_callback_info info)
161 {
162     auto context = std::make_shared<ContextBase>();
163     if (context == nullptr) {
164         SLOGE("OffEvent failed : no memory");
165         NapiUtils::ThrowError(env, "OffEvent failed : no memory", NapiAVSessionManager::errcode_[ERR_NO_MEMORY]);
166         return NapiUtils::GetUndefinedValue(env);
167     }
168     std::string eventName;
169     napi_value callback = nullptr;
170     auto input = [&eventName, env, &context, &callback](size_t argc, napi_value* argv) {
171         CHECK_ARGS_RETURN_VOID(context, argc == ARGC_ONE || argc == ARGC_TWO,
172                                "invalid argument number", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
173         context->status = NapiUtils::GetValue(env, argv[ARGV_FIRST], eventName);
174         CHECK_STATUS_RETURN_VOID(context, "get event name failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
175         if (argc == ARGC_TWO) {
176             callback = argv[ARGV_SECOND];
177         }
178     };
179     context->GetCbInfo(env, info, input, true);
180     if (context->status != napi_ok) {
181         NapiUtils::ThrowError(env, context->errMessage.c_str(), context->errCode);
182         return NapiUtils::GetUndefinedValue(env);
183     }
184     auto it = eventHandlers_.find(eventName);
185     if (it == eventHandlers_.end()) {
186         SLOGE("event name invalid");
187         NapiUtils::ThrowError(env, "event name invalid", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
188         return NapiUtils::GetUndefinedValue(env);
189     }
190     auto* napiAVCastPickerHelper = reinterpret_cast<NapiAVCastPickerHelper*>(context->native);
191     if (napiAVCastPickerHelper == nullptr) {
192         SLOGE("OffEvent failed : napiAVCastPickerHelper is nullptr");
193         NapiUtils::ThrowError(env, "OffEvent failed : napiAVCastPickerHelper is nullptr",
194             NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
195         return NapiUtils::GetUndefinedValue(env);
196     }
197     if (napiAVCastPickerHelper != nullptr && it->second.second(env, napiAVCastPickerHelper, callback) != napi_ok) {
198         NapiUtils::ThrowError(env, "remove event callback failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
199     }
200     return NapiUtils::GetUndefinedValue(env);
201 }
202 
SelectAVPicker(napi_env env,napi_callback_info info)203 napi_value NapiAVCastPickerHelper::SelectAVPicker(napi_env env, napi_callback_info info)
204 {
205     struct ConcreteContext : public ContextBase {
206         NapiAVCastPickerOptions napiAVCastPickerOptions;
207     };
208     auto context = std::make_shared<ConcreteContext>();
209     auto inputParser = [env, context](size_t argc, napi_value* argv) {
210         CHECK_ARGS_RETURN_VOID(context, argc == ARGC_ZERO || argc == ARGC_ONE,
211                                "invalid argument number", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
212         if (argc == ARGC_ZERO) {
213             SLOGI("NapiAVCastPickerOptions use default options");
214         } else {
215             context->status = NapiUtils::GetValue(env, argv[ARGV_FIRST], context->napiAVCastPickerOptions);
216         }
217         CHECK_STATUS_RETURN_VOID(context, "get object failed", NapiAVSessionManager::errcode_[ERR_INVALID_PARAM]);
218     };
219     context->GetCbInfo(env, info, inputParser);
220     context->taskId = NAPI_CAST_PICKER_HELPER_TASK_ID;
221 
222     auto executor = [context]() {
223         auto* napiAVCastPickerHelper = reinterpret_cast<NapiAVCastPickerHelper*>(context->native);
224         AAFwk::Want request;
225         std::string targetType = "sysPicker/mediaControl";
226         request.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType);
227         request.SetParam("isAVCastPickerHelper", true);
228         request.SetParam("AVCastPickerOptionsType", context->napiAVCastPickerOptions.sessionType);
229         request.SetElementName(ABILITY_WANT_ELEMENT_NAME, "UIExtAbility");
230 
231         PickerCallBack pickerCallBack;
232         auto callback = std::make_shared<ModalUICallback>(napiAVCastPickerHelper->uiContent_, pickerCallBack);
233         Ace::ModalUIExtensionCallbacks extensionCallback = {
234             .onRelease = ([callback](auto arg) { callback->OnRelease(arg); }),
235             .onResult = ([callback](auto arg1, auto arg2) { callback->OnResult(arg1, arg2); }),
236             .onReceive = ([callback, napiAVCastPickerHelper](auto arg) {
237                 callback->OnReceive(arg);
238                 napiAVCastPickerHelper->HandleEvent(EVENT_PICPKER_STATE_CHANGE, STATE_DISAPPEARING);
239             }),
240             .onError = ([callback](auto arg1, auto arg2, auto arg3) { callback->OnError(arg1, arg2, arg3); }),
241             .onRemoteReady = ([callback, napiAVCastPickerHelper](auto arg) {
242                 callback->OnRemoteReady(arg);
243                 napiAVCastPickerHelper->HandleEvent(EVENT_PICPKER_STATE_CHANGE, STATE_APPEARING);
244             }),
245             .onDestroy = ([callback]() { callback->OnDestroy(); }),
246         };
247         Ace::ModalUIExtensionConfig config;
248         config.isProhibitBack = true;
249         int sessionId = napiAVCastPickerHelper->uiContent_->CreateModalUIExtension(request, extensionCallback, config);
250         callback->SetSessionId(sessionId);
251     };
252     auto complete = [env](napi_value& output) { output = NapiUtils::GetUndefinedValue(env); };
253     return NapiAsyncWork::Enqueue(env, context, "SelectAVPicker", executor, complete);
254 }
255 
OnPickerStateChange(napi_env env,NapiAVCastPickerHelper * napiAVCastPickerHelper,napi_value callback)256 napi_status NapiAVCastPickerHelper::OnPickerStateChange(napi_env env, NapiAVCastPickerHelper* napiAVCastPickerHelper,
257     napi_value callback)
258 {
259     SLOGI("NapiAVCastPickerHelper OnPickerStateChange");
260     CHECK_AND_RETURN_RET_LOG(napiAVCastPickerHelper != nullptr, napi_generic_failure, "input param is nullptr");
261     return napiAVCastPickerHelper->AddCallback(env, EVENT_PICPKER_STATE_CHANGE, callback);
262 }
263 
OffPickerStateChange(napi_env env,NapiAVCastPickerHelper * napiAVCastPickerHelper,napi_value callback)264 napi_status NapiAVCastPickerHelper::OffPickerStateChange(napi_env env, NapiAVCastPickerHelper* napiAVCastPickerHelper,
265     napi_value callback)
266 {
267     SLOGI("NapiAVCastPickerHelper OffPickerStateChange");
268     CHECK_AND_RETURN_RET_LOG(napiAVCastPickerHelper != nullptr, napi_generic_failure, "input param is nullptr");
269     return napiAVCastPickerHelper->RemoveCallback(env, EVENT_PICPKER_STATE_CHANGE, callback);
270 }
271 
272 template<typename T>
HandleEvent(int32_t event,const T & param)273 void NapiAVCastPickerHelper::HandleEvent(int32_t event, const T& param)
274 {
275     std::lock_guard<std::mutex> lockGuard(lock_);
276     if (callbacks_[event].empty()) {
277         SLOGE("not register callback event=%{public}d", event);
278         return;
279     }
280     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
281         asyncCallback_->CallWithFunc(*ref, isValid_,
282             [this, ref, event]() {
283                 std::lock_guard<std::mutex> lockGuard(lock_);
284                 if (callbacks_[event].empty()) {
285                     SLOGE("checkCallbackValid with empty list for event=%{public}d", event);
286                     return false;
287                 }
288                 bool hasFunc = false;
289                 for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
290                     hasFunc = (ref == it ? true : hasFunc);
291                 }
292                 if (!hasFunc) {
293                     SLOGE("checkCallbackValid res false for event=%{public}d", event);
294                 }
295                 return hasFunc;
296             },
297             [param](napi_env env, int& argc, napi_value* argv) {
298                 argc = NapiUtils::ARGC_ONE;
299                 NapiUtils::SetValue(env, param, *argv);
300             });
301     }
302 }
303 
AddCallback(napi_env env,int32_t event,napi_value callback)304 napi_status NapiAVCastPickerHelper::AddCallback(napi_env env, int32_t event, napi_value callback)
305 {
306     SLOGI("Add callback %{public}d", event);
307     std::lock_guard<std::mutex> lockGuard(lock_);
308     napi_ref ref = nullptr;
309     CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
310                              napi_generic_failure, "get callback reference failed");
311     CHECK_AND_RETURN_RET_LOG(ref == nullptr, napi_ok, "callback has been registered");
312     napi_status status = napi_create_reference(env, callback, NapiUtils::ARGC_ONE, &ref);
313     if (status != napi_ok) {
314         SLOGE("napi_create_reference failed");
315         return status;
316     }
317     if (asyncCallback_ == nullptr) {
318         asyncCallback_ = std::make_shared<NapiAsyncCallback>(env);
319         if (asyncCallback_ == nullptr) {
320             SLOGE("no memory");
321             return napi_generic_failure;
322         }
323     }
324     callbacks_[event].push_back(ref);
325     return napi_ok;
326 }
327 
RemoveCallback(napi_env env,int32_t event,napi_value callback)328 napi_status NapiAVCastPickerHelper::RemoveCallback(napi_env env, int32_t event, napi_value callback)
329 {
330     SLOGI("Remove callback %{public}d", event);
331     std::lock_guard<std::mutex> lockGuard(lock_);
332     if (callback == nullptr) {
333         for (auto callbackRef = callbacks_[event].begin(); callbackRef != callbacks_[event].end(); ++callbackRef) {
334             napi_status ret = napi_delete_reference(env, *callbackRef);
335             CHECK_AND_RETURN_RET_LOG(napi_ok == ret, ret, "delete callback reference failed");
336         }
337         callbacks_[event].clear();
338         return napi_ok;
339     }
340     napi_ref ref = nullptr;
341     CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
342                              napi_generic_failure, "get callback reference failed");
343     CHECK_AND_RETURN_RET_LOG(ref != nullptr, napi_ok, "callback has been remove");
344     callbacks_[event].remove(ref);
345     return napi_delete_reference(env, ref);
346 }
347 
IsCallbacksEmpty(int32_t event)348 bool NapiAVCastPickerHelper::IsCallbacksEmpty(int32_t event)
349 {
350     return callbacks_[event].empty();
351 }
352 
ModalUICallback(Ace::UIContent * uiContent,PickerCallBack & pickerCallBack)353 ModalUICallback::ModalUICallback(Ace::UIContent* uiContent, PickerCallBack& pickerCallBack)
354 {
355     this->uiContent_ = uiContent;
356     this->pickerCallBack_ = pickerCallBack;
357 }
358 
SetSessionId(int32_t sessionId)359 void ModalUICallback::SetSessionId(int32_t sessionId)
360 {
361     this->sessionId_ = sessionId;
362 }
363 
OnRelease(int32_t releaseCode)364 void ModalUICallback::OnRelease(int32_t releaseCode)
365 {
366     SLOGI("ModalUICallback OnRelease enter. release code is %{public}d", releaseCode);
367     this->uiContent_->CloseModalUIExtension(this->sessionId_);
368     pickerCallBack_.ready = true;
369 }
370 
OnResult(int32_t resultCode,const OHOS::AAFwk::Want & result)371 void ModalUICallback::OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result)
372 {
373     SLOGI("ModalUICallback OnResult enter. resultCode code is %{public}d", resultCode);
374     pickerCallBack_.resultCode = resultCode;
375 }
376 
OnReceive(const OHOS::AAFwk::WantParams & request)377 void ModalUICallback::OnReceive(const OHOS::AAFwk::WantParams& request)
378 {
379     SLOGI("ModalUICallback OnReceive enter.");
380 }
381 
OnError(int32_t code,const std::string & name,const std::string & message)382 void ModalUICallback::OnError(int32_t code, const std::string& name, const std::string& message)
383 {
384     SLOGI("ModalUICallback OnError enter. errorCode=%{public}d, name=%{public}s, message=%{public}s",
385         code, name.c_str(), message.c_str());
386     if (!pickerCallBack_.ready) {
387         this->uiContent_->CloseModalUIExtension(this->sessionId_);
388         pickerCallBack_.ready = true;
389     }
390 }
391 
OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy> & uiProxy)392 void ModalUICallback::OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy>& uiProxy)
393 {
394     SLOGI("ModalUICallback OnRemoteReady enter.");
395 }
396 
OnDestroy()397 void ModalUICallback::OnDestroy()
398 {
399     SLOGI("ModalUICallback OnDestroy enter.");
400 }
401 
402 } // namespace OHOS::AVSession
403