2 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <aul_app_com.h>
19 #include <aul_widget.h>
20 #include <bundle_internal.h>
23 #include <screen_connector_provider.h>
26 #include <widget_errno.h>
27 #include <widget_instance.h>
29 #include <app_core_multi_window_base.hh>
31 #include "common/log_private.hh"
32 #include "include/widget_base.hh"
37 constexpr const char kStatusForeground[] = "fg";
38 constexpr const char kStatusBackground[] = "bg";
42 void SetAppId(const std::string& app_id) {
46 const std::string& GetAppId() const {
50 void SetPackageId(const std::string& package_id) {
51 package_id_ = package_id;
54 const std::string& GetPackageId() const {
58 void SetViewerEndpoint(const std::string& viewer_endpoint) {
59 viewer_endpoint_ = viewer_endpoint;
62 const std::string& GetViewerEndpoint() const {
63 return viewer_endpoint_;
66 void SetPermanent(bool permanent) {
67 permanent_ = permanent;
70 bool IsPermanent() const {
74 void SetFgSignal(bool fg_signal) {
75 fg_signal_ = fg_signal;
78 bool IsFgSignal() const {
82 int SendUpdateStatus(const std::string& class_id,
83 const std::string& instance_id, int status, int err,
85 int ret = aul_widget_send_status_to_viewer(class_id.c_str(),
86 instance_id.c_str(), viewer_endpoint_.c_str(), status, err, extra);
90 int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
92 ret = aul_widget_send_status_to_service(class_id.c_str(),
93 instance_id.c_str(), package_id_.c_str(), lifecycle);
101 std::string package_id_;
102 std::string viewer_endpoint_;
103 bool permanent_ = false;
104 bool fg_signal_ = false;
107 AppContext __context;
111 class WidgetBase::Impl {
113 explicit Impl(WidgetBase* parent) : parent_(parent) {}
116 friend class WidgetBase;
119 void ControlCreate(const std::string& class_id, const std::string& id,
120 const tizen_base::Bundle& b);
121 void ControlDestroy(const std::string& class_id, const std::string& id,
122 const tizen_base::Bundle& b);
123 void ControlResume(const std::string& class_id, const std::string& id,
124 const tizen_base::Bundle& b);
125 void ControlPause(const std::string& class_id, const std::string& id,
126 const tizen_base::Bundle& b);
127 void ControlChangePeriod(const std::string& class_id, const std::string& id,
128 const tizen_base::Bundle& b);
129 void ControlUpdate(const std::string& class_id, const std::string& id,
130 const tizen_base::Bundle& b);
131 void ControlResize(const std::string& class_id, const std::string& id,
132 const tizen_base::Bundle& b);
133 void GetContent(tizen_base::Bundle& b);
136 WidgetBase* __widget;
138 class WidgetContext::Impl {
140 explicit Impl(WidgetContext* parent) : parent_(parent) {}
143 UnsetPeriodicTimer();
147 void UpdateProcess(const tizen_base::Bundle& b);
148 void OnUpdate(bool force);
149 void SetPeriod(double period);
150 void SetPeriodicTimer();
151 void UnsetPeriodicTimer();
152 static gboolean TimedOutCb(gpointer user_data);
155 friend class WidgetContext;
156 friend class WidgetBase;
157 friend class WidgetBase::Impl;
159 WidgetContext* parent_;
161 std::unique_ptr<tizen_base::Bundle> args_;
162 std::string content_;
163 double period_ = 0.0f;
164 guint periodic_timer_ = 0;
165 bool pending_update_ = false;
166 std::string pending_content_;
169 void WidgetBase::Impl::ControlCreate(const std::string& class_id,
170 const std::string& id, const tizen_base::Bundle& b) {
171 if (parent_->FindById(id).get() != nullptr) {
172 _E("Already exists. ID(%s)", id.c_str());
176 std::shared_ptr<Context> context;
178 context = parent_->CreateContext(class_id, id);
179 } catch (std::runtime_error& e) {
180 _E("runtime_error exception occurs");
184 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
188 wc->impl_->args_.reset(new tizen_base::Bundle(b));
189 auto ctx = parent_->RunContext(context);
190 if (ctx.get() == nullptr)
193 wc->impl_->args_.reset();
194 wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
197 void WidgetBase::Impl::ControlDestroy(const std::string& class_id,
198 const std::string& id, const tizen_base::Bundle& b) {
199 auto context = parent_->FindById(id);
200 if (context.get() == nullptr) {
201 _E("Failed to find widget context. ID(%s)", id.c_str());
202 aul_widget_instance_del(class_id.c_str(), id.c_str());
206 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
210 wc->impl_->args_.reset(new tizen_base::Bundle(b));
211 parent_->ExitContext(context);
214 void WidgetBase::Impl::ControlResume(const std::string& class_id,
215 const std::string& id, const tizen_base::Bundle& b) {
216 auto context = parent_->FindById(id);
217 if (context.get() == nullptr) {
218 _E("Failed to find widget context. ID(%s)", id.c_str());
225 void WidgetBase::Impl::ControlPause(const std::string& class_id,
226 const std::string& id, const tizen_base::Bundle& b) {
227 auto context = parent_->FindById(id);
228 if (context.get() == nullptr) {
229 _E("Failed to find widget context. ID(%s)", id.c_str());
236 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
237 const std::string& id, const tizen_base::Bundle& b) {
239 for (auto& i : parent_->GetContexts()) {
240 if (i->GetContextId() == class_id) {
241 WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
245 cxt->impl_->UpdateProcess(b);
251 auto context = parent_->FindById(id);
252 if (context.get() == nullptr) {
253 _E("Failed to find widget context. ID(%s)", id.c_str());
257 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
261 wc->impl_->UpdateProcess(b);
264 void WidgetBase::Impl::ControlResize(const std::string& class_id,
265 const std::string& id, const tizen_base::Bundle& b) {
266 auto context = parent_->FindById(id);
267 if (context.get() == nullptr) {
268 _E("Failed to find widget context. ID(%s)", id.c_str());
273 std::string w_str = b.GetString(WIDGET_K_WIDTH);
274 if (!w_str.empty()) {
275 char* remain = nullptr;
276 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
280 std::string h_str = b.GetString(WIDGET_K_HEIGHT);
281 if (!h_str.empty()) {
282 char* remain = nullptr;
283 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
286 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
292 _D("WidgetContext(%s) is resized to %dx%d", id.c_str(), w, h);
293 __context.SendUpdateStatus(class_id, id,
294 WIDGET_INSTANCE_EVENT_SIZE_CHANGED, 0, nullptr);
297 void WidgetBase::Impl::ControlChangePeriod(const std::string& class_id,
298 const std::string& id, const tizen_base::Bundle& b) {
299 auto context = parent_->FindById(id);
300 if (context.get() == nullptr) {
301 _E("Failed to find widget context. ID(%s)", id.c_str());
305 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
309 wc->impl_->UnsetPeriodicTimer();
311 auto v = b.GetByte(WIDGET_K_PERIOD);
315 const double* period = reinterpret_cast<double*>(v.data());
316 wc->impl_->SetPeriod(*period);
317 wc->impl_->SetPeriodicTimer();
320 void WidgetBase::Impl::GetContent(tizen_base::Bundle& b) {
321 std::string instance_id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
322 if (instance_id.empty()) {
323 _E("Instance ID is nullptr");
327 auto context = parent_->FindById(instance_id);
328 if (context.get() == nullptr) {
329 _E("Failed to find widget context. ID(%s)", instance_id.c_str());
333 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
337 if (!wc->impl_->content_.empty()) {
338 b.Add(AUL_K_WIDGET_CONTENT_INFO, wc->impl_->content_);
339 _D("Content info of %s found", instance_id.c_str());
341 b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
342 _D("Empty content info added");
346 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
349 WidgetBase::~WidgetBase() = default;
351 std::string WidgetBase::GetViewerEndpoint() {
352 return __context.GetViewerEndpoint();
355 void WidgetBase::Run(int argc, char** argv) {
356 bundle* kb = bundle_import_from_argv(argc, argv);
358 tizen_base::Bundle b(kb, false, true);
359 __context.SetViewerEndpoint(b.GetString(AUL_K_WIDGET_VIEWER));
360 if (!__context.GetViewerEndpoint().empty())
361 _D("Viewer endpoint :%s", __context.GetViewerEndpoint().c_str());
363 _E("Endpoint is missing");
365 _E("Failed to get launch argv");
366 throw std::runtime_error("Failed to get launch argv");
369 AppCoreMultiWindowBase::Run(argc, argv);
372 void WidgetBase::Dispose() {
373 AppCoreMultiWindowBase::Dispose();
374 if (getenv("AUL_LOADER_INIT")) {
375 unsetenv("AUL_LOADER_INIT");
380 void WidgetBase::ExitContext(
381 std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
382 AppCoreMultiWindowBase::ExitContext(context);
383 int cnt = GetContextCnt();
387 aul_widget_write_log(LOG_TAG,
388 "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
389 context->GetInstId().c_str());
392 void WidgetBase::Exit() {
393 AppCoreMultiWindowBase::Exit();
394 int cnt = GetContextCnt();
396 if (cnt == 0 && __context.IsPermanent())
397 ret = aul_notify_exit();
399 aul_widget_write_log(LOG_TAG,
400 "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
401 __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
404 int WidgetBase::OnCreate() {
405 AppCoreMultiWindowBase::OnCreate();
407 char appid[256] = { 0, };
408 int ret = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
409 if (ret != AUL_R_OK) {
410 _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
414 __context.SetAppId(appid);
416 char pkgid[256] = { 0, };
417 ret = aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid));
418 if (ret != AUL_R_OK) {
419 _E("aul_app_get_pkgid_bypid() is failed. error(%d)", ret);
423 __context.SetPackageId(pkgid);
425 screen_connector_provider_init();
426 _D("Widget base is created");
431 int WidgetBase::OnTerminate() {
432 screen_connector_provider_fini();
435 AppCoreMultiWindowBase::OnTerminate();
436 _D("Widget base is terminated");
440 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
441 AppCoreMultiWindowBase::OnReceive(type,
442 tizen_base::Bundle(b.GetHandle(), false, false));
446 for (auto& i : GetContexts())
453 case AUL_WIDGET_CONTENT:
454 impl_->GetContent(b);
463 int WidgetBase::OnControl(tizen_base::Bundle b) {
464 AppCoreMultiWindowBase::OnControl(
465 tizen_base::Bundle(b.GetHandle(), false, false));
467 std::string class_id = b.GetString(WIDGET_K_CLASS);
468 /* for previous version compatibility, use appid for default class id */
469 if (class_id.empty())
470 class_id = __context.GetAppId();
472 std::string id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
473 std::string operation = b.GetString(WIDGET_K_OPERATION);
474 if (operation.empty()) {
475 _E("Operation is empty");
479 _I("Operation(%s)", operation.c_str());
480 if (operation == "create")
481 impl_->ControlCreate(class_id, id, b);
482 else if (operation == "resize")
483 impl_->ControlResize(class_id, id, b);
484 else if (operation == "update")
485 impl_->ControlUpdate(class_id, id, b);
486 else if (operation == "destroy")
487 impl_->ControlDestroy(class_id, id, b);
488 else if (operation == "resume")
489 impl_->ControlResume(class_id, id, b);
490 else if (operation == "pause")
491 impl_->ControlPause(class_id, id, b);
492 else if (operation == "terminate")
493 impl_->ControlDestroy(class_id, id, b);
494 else if (operation == "period")
495 impl_->ControlChangePeriod(class_id, id, b);
500 void WidgetContext::Impl::OnUpdate(bool force) {
501 parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
502 tizen_base::Bundle(pending_content_), force);
504 std::string class_id = parent_->GetContextId();
505 std::string id = parent_->GetInstId();
506 __context.SendUpdateStatus(class_id, id,
507 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
508 _D("Updated: %s", id.c_str());
511 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
513 std::string force_str = b.GetString(WIDGET_K_FORCE);
514 if (force_str == "true")
519 std::string content_raw = b.GetString(WIDGET_K_CONTENT_INFO);
520 if (!parent_->IsResumed() && !force) {
521 pending_content_ = content_raw;
522 pending_update_ = true;
524 parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
525 tizen_base::Bundle(content_raw), force);
527 std::string class_id = parent_->GetContextId();
528 std::string id = parent_->GetInstId();
529 __context.SendUpdateStatus(class_id, id,
530 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
531 _D("Updated: %s", id.c_str());
535 void WidgetContext::Impl::SetPeriod(double period) {
539 void WidgetContext::Impl::SetPeriodicTimer() {
544 _D("Restart timer!");
545 periodic_timer_ = g_timeout_add_seconds(period_,
546 TimedOutCb, parent_);
550 void WidgetContext::Impl::UnsetPeriodicTimer() {
551 if (periodic_timer_) {
553 g_source_remove(periodic_timer_);
558 gboolean WidgetContext::Impl::TimedOutCb(gpointer user_data) {
559 WidgetContext* wc = reinterpret_cast<WidgetContext*>(user_data);
560 if (wc->IsResumed()) {
561 _D("Periodic update!");
562 wc->OnUpdate(tizen_base::Bundle(), false);
564 __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
565 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
566 _D("Updated: %s", wc->GetInstId().c_str());
568 wc->impl_->pending_update_ = true;
569 wc->impl_->UnsetPeriodicTimer();
572 return G_SOURCE_CONTINUE;
575 WidgetContext::WidgetContext(std::string context_id, std::string inst_id,
576 AppCoreMultiWindowBase* app)
577 : AppCoreMultiWindowBase::Context(
578 std::move(context_id), std::move(inst_id), app),
579 impl_(std::make_unique<WidgetContext::Impl>(this)) {
582 WidgetContext::~WidgetContext() = default;
584 void WidgetContext::OnPause() {
585 std::string id = GetInstId();
586 _D("WidgetContext(%s) is paused", id.c_str());
587 std::string class_id = GetContextId();
588 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
589 WIDGET_INSTANCE_EVENT_PAUSE, 0, nullptr);
591 if (__context.IsFgSignal()) {
592 _D("Send bg signal to resourceD");
593 aul_widget_instance_change_status(class_id.c_str(), kStatusBackground);
594 __context.SetFgSignal(false);
598 void WidgetContext::OnResume() {
599 if (impl_->pending_update_) {
600 _D("Pending update!");
601 impl_->pending_update_ = false;
602 impl_->OnUpdate(false);
603 impl_->SetPeriodicTimer();
606 std::string id = GetInstId();
607 _D("WidgetContext(%s) is resumed", id.c_str());
608 std::string class_id = GetContextId();
609 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
610 WIDGET_INSTANCE_EVENT_RESUME, 0, nullptr);
612 if (!__context.IsFgSignal()) {
613 _D("Send fg signal to resourceD");
614 aul_widget_instance_change_status(class_id.c_str(), kStatusForeground);
615 __context.SetFgSignal(true);
619 void WidgetContext::OnResize(int w, int h) {
622 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
625 void WidgetContext::ExitAsync() {
626 tizen_base::Bundle b;
627 b.Add(WIDGET_K_OPERATION, "terminate");
629 impl_->args_.reset(new tizen_base::Bundle(std::move(b)));
630 g_idle_add([](gpointer user_data) -> gboolean {
631 if (__widget == nullptr)
632 return G_SOURCE_REMOVE;
634 WidgetContext* wc = static_cast<WidgetContext*>(user_data);
636 std::string id = wc->GetInstId();
637 auto context = __widget->FindById(id);
638 if (context.get() == nullptr) {
639 _E("Failed to find context. ID(%s)", id.c_str());
640 return G_SOURCE_REMOVE;
643 __widget->ExitContext(context);
644 return G_SOURCE_REMOVE;
648 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
649 std::string id = GetInstId();
651 return WIDGET_ERROR_FAULT;
653 std::string class_id = GetContextId();
654 if (class_id.empty())
655 return WIDGET_ERROR_FAULT;
657 int ret = __context.SendUpdateStatus(class_id, id,
658 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
660 _E("Failed to send content info: %s of %s (%d)",
661 id.c_str(), class_id.c_str(), ret);
662 return WIDGET_ERROR_FAULT;
665 return WIDGET_ERROR_NONE;
668 int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
669 wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
670 if (surface == nullptr) {
671 _E("Failed to get surface, wl_win(%p)", wl_win);
675 screen_connector_provider_remote_enable(id.c_str(), surface);
680 void WidgetContext::OnCreate() {
681 auto& b = impl_->args_;
682 std::string class_id = b->GetString(WIDGET_K_CLASS);
683 /* For previous version compatibility, use appid for default class id */
684 if (class_id.empty())
685 class_id = __context.GetAppId();
687 std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
688 std::string operation = b->GetString(WIDGET_K_OPERATION);
689 if (operation.empty()) {
690 _E("Operation is empty");
694 std::string w_str = b->GetString(WIDGET_K_WIDTH);
696 if (!w_str.empty()) {
697 char* remain = nullptr;
698 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
701 std::string h_str = b->GetString(WIDGET_K_HEIGHT);
703 if (!h_str.empty()) {
704 char* remain = nullptr;
705 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
708 tizen_base::Bundle content_info;
709 std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
711 if (!content.empty())
712 content_info = tizen_base::Bundle(content);
713 } catch (std::bad_alloc& e) {
714 _W("Failed to decode content info(%s)", content.c_str());
717 bool r = OnCreate(content_info, w, h);
719 _W("Create callback returns error");
720 int ret = __context.SendUpdateStatus(class_id, id,
721 WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
723 _E("Failed to send abrot status. error(%d)", ret);
725 throw std::runtime_error("Create callback returns error");
727 _D("WidgetContext(%s) is created", id.c_str());
728 aul_widget_instance_add(class_id.c_str(), id.c_str());
729 int ret = __context.SendUpdateStatus(class_id, id,
730 WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
732 _E("Failed to send create status. error(%d)", ret);
734 auto v = b->GetByte(WIDGET_K_PERIOD);
738 const double* period = reinterpret_cast<double*>(v.data());
740 _I("Set periodic update timer. period(%lf)", *period);
741 impl_->SetPeriod(*period);
742 impl_->SetPeriodicTimer();
747 void WidgetContext::OnTerminate() {
748 DestroyType reason = DestroyType::TEMPORARY;
749 int event = WIDGET_INSTANCE_EVENT_TERMINATE;
750 std::string id = GetInstId();
751 std::string class_id = GetContextId();
752 auto& b = impl_->args_;
754 std::string operation = b->GetString(WIDGET_K_OPERATION);
755 if (operation == "destroy")
756 reason = DestroyType::PERMANENT;
759 tizen_base::Bundle content_info = impl_->content_.empty() ?
760 tizen_base::Bundle() : tizen_base::Bundle(impl_->content_);
762 OnDestroy(reason, content_info);
764 _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
765 reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
766 if (reason == DestroyType::PERMANENT) {
767 __context.SetPermanent(true);
768 event = WIDGET_INSTANCE_EVENT_DESTROY;
769 aul_widget_instance_del(class_id.c_str(), id.c_str());
771 __context.SetPermanent(false);
772 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
773 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
776 impl_->UnsetPeriodicTimer();
778 __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
781 } // namespace tizen_cpp