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>
31 #include <app_core_multi_window_base.hh>
33 #include "common/log_private.hh"
34 #include "include/widget_base.hh"
39 constexpr const char kStatusForeground[] = "fg";
40 constexpr const char kStatusBackground[] = "bg";
44 void SetAppId(const std::string& app_id) {
49 const std::string& GetAppId() const {
54 void SetPackageId(const std::string& package_id) {
55 package_id_ = package_id;
58 const std::string& GetPackageId() const {
62 void SetViewerEndpoint(const std::string& viewer_endpoint) {
63 viewer_endpoint_ = viewer_endpoint;
66 const std::string& GetViewerEndpoint() const {
67 return viewer_endpoint_;
70 void SetPermanent(bool permanent) {
71 permanent_ = permanent;
74 bool IsPermanent() const {
78 void SetFgSignal(bool fg_signal) {
79 fg_signal_ = fg_signal;
82 bool IsFgSignal() const {
87 int SendUpdateStatus(const std::string& class_id,
88 const std::string& instance_id, int status, int err,
90 int ret = aul_widget_send_status_to_viewer(class_id.c_str(),
91 instance_id.c_str(), viewer_endpoint_.c_str(), status, err, extra);
95 int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
97 ret = aul_widget_send_status_to_service(class_id.c_str(),
98 instance_id.c_str(), package_id_.c_str(), lifecycle);
106 std::string package_id_;
107 std::string viewer_endpoint_;
108 bool permanent_ = false;
109 bool fg_signal_ = false;
112 AppContext __context;
116 class WidgetBase::Impl {
118 explicit Impl(WidgetBase* parent) : parent_(parent) {}
121 friend class WidgetBase;
124 void ControlCreate(const std::string& class_id, const std::string& id,
125 const tizen_base::Bundle& b);
126 void ControlDestroy(const std::string& class_id, const std::string& id,
127 const tizen_base::Bundle& b);
128 void ControlResume(const std::string& class_id, const std::string& id,
129 const tizen_base::Bundle& b);
130 void ControlPause(const std::string& class_id, const std::string& id,
131 const tizen_base::Bundle& b);
132 void ControlChangePeriod(const std::string& class_id, const std::string& id,
133 const tizen_base::Bundle& b);
134 void ControlUpdate(const std::string& class_id, const std::string& id,
135 const tizen_base::Bundle& b);
136 void ControlResize(const std::string& class_id, const std::string& id,
137 const tizen_base::Bundle& b);
138 void GetContent(tizen_base::Bundle& b);
141 WidgetBase* __widget;
143 class WidgetContext::Impl {
145 explicit Impl(WidgetContext* parent) : parent_(parent) {}
148 UnsetPeriodicTimer();
152 void UpdateProcess(const tizen_base::Bundle& b);
153 void OnUpdate(bool force);
154 void SetPeriod(double period);
155 void SetPeriodicTimer();
156 void UnsetPeriodicTimer();
157 static gboolean TimedOutCb(gpointer user_data);
160 friend class WidgetContext;
161 friend class WidgetBase;
162 friend class WidgetBase::Impl;
164 WidgetContext* parent_;
166 std::unique_ptr<tizen_base::Bundle> args_;
167 std::string content_;
168 double period_ = 0.0f;
169 guint periodic_timer_ = 0;
170 bool pending_update_ = false;
171 std::string pending_content_;
174 void WidgetBase::Impl::ControlCreate(const std::string& class_id,
175 const std::string& id, const tizen_base::Bundle& b) {
176 if (parent_->FindById(id).get() != nullptr) {
177 _E("Already exists. ID(%s)", id.c_str());
181 std::shared_ptr<Context> context;
183 context = parent_->CreateContext(class_id, id);
184 } catch (std::runtime_error& e) {
185 _E("runtime_error exception occurs");
189 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
193 wc->impl_->args_.reset(new tizen_base::Bundle(b));
194 auto ctx = parent_->RunContext(context);
195 if (ctx.get() == nullptr)
198 wc->impl_->args_.reset();
199 wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
202 void WidgetBase::Impl::ControlDestroy(const std::string& class_id,
203 const std::string& id, const tizen_base::Bundle& b) {
204 auto context = parent_->FindById(id);
205 if (context.get() == nullptr) {
206 _E("Failed to find widget context. ID(%s)", id.c_str());
207 aul_widget_instance_del(class_id.c_str(), id.c_str());
211 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
215 wc->impl_->args_.reset(new tizen_base::Bundle(b));
216 parent_->ExitContext(context);
219 void WidgetBase::Impl::ControlResume(const std::string& class_id,
220 const std::string& id, const tizen_base::Bundle& b) {
221 auto context = parent_->FindById(id);
222 if (context.get() == nullptr) {
223 _E("Failed to find widget context. ID(%s)", id.c_str());
230 void WidgetBase::Impl::ControlPause(const std::string& class_id,
231 const std::string& id, const tizen_base::Bundle& b) {
232 auto context = parent_->FindById(id);
233 if (context.get() == nullptr) {
234 _E("Failed to find widget context. ID(%s)", id.c_str());
241 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
242 const std::string& id, const tizen_base::Bundle& b) {
244 for (auto& i : parent_->GetContexts()) {
245 if (i->GetContextId() == class_id) {
246 WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
250 cxt->impl_->UpdateProcess(b);
256 auto context = parent_->FindById(id);
257 if (context.get() == nullptr) {
258 _E("Failed to find widget context. ID(%s)", id.c_str());
262 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
266 wc->impl_->UpdateProcess(b);
269 void WidgetBase::Impl::ControlResize(const std::string& class_id,
270 const std::string& id, const tizen_base::Bundle& b) {
271 auto context = parent_->FindById(id);
272 if (context.get() == nullptr) {
273 _E("Failed to find widget context. ID(%s)", id.c_str());
278 std::string w_str = b.GetString(WIDGET_K_WIDTH);
279 if (!w_str.empty()) {
280 char* remain = nullptr;
281 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
285 std::string h_str = b.GetString(WIDGET_K_HEIGHT);
286 if (!h_str.empty()) {
287 char* remain = nullptr;
288 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
291 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
297 _D("WidgetContext(%s) is resized to %dx%d", id.c_str(), w, h);
298 __context.SendUpdateStatus(class_id, id,
299 WIDGET_INSTANCE_EVENT_SIZE_CHANGED, 0, nullptr);
302 void WidgetBase::Impl::ControlChangePeriod(const std::string& class_id,
303 const std::string& id, const tizen_base::Bundle& b) {
304 auto context = parent_->FindById(id);
305 if (context.get() == nullptr) {
306 _E("Failed to find widget context. ID(%s)", id.c_str());
310 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
314 wc->impl_->UnsetPeriodicTimer();
316 auto v = b.GetByte(WIDGET_K_PERIOD);
320 const double* period = reinterpret_cast<double*>(v.data());
321 wc->impl_->SetPeriod(*period);
322 wc->impl_->SetPeriodicTimer();
325 void WidgetBase::Impl::GetContent(tizen_base::Bundle& b) {
326 std::string instance_id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
327 if (instance_id.empty()) {
328 _E("Instance ID is nullptr");
332 auto context = parent_->FindById(instance_id);
333 if (context.get() == nullptr) {
334 _E("Failed to find widget context. ID(%s)", instance_id.c_str());
338 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
342 if (!wc->impl_->content_.empty()) {
343 b.Add(AUL_K_WIDGET_CONTENT_INFO, wc->impl_->content_);
344 _D("Content info of %s found", instance_id.c_str());
346 b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
347 _D("Empty content info added");
351 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
354 WidgetBase::~WidgetBase() = default;
356 std::string WidgetBase::GetViewerEndpoint() {
357 return __context.GetViewerEndpoint();
360 void WidgetBase::Run(int argc, char** argv) {
361 bundle* kb = bundle_import_from_argv(argc, argv);
363 tizen_base::Bundle b(kb, false, true);
364 __context.SetViewerEndpoint(b.GetString(AUL_K_WIDGET_VIEWER));
365 if (!__context.GetViewerEndpoint().empty())
366 _D("Viewer endpoint :%s", __context.GetViewerEndpoint().c_str());
368 _E("Endpoint is missing");
370 _E("Failed to get launch argv");
371 throw std::runtime_error("Failed to get launch argv");
374 AppCoreMultiWindowBase::Run(argc, argv);
377 void WidgetBase::Dispose() {
378 AppCoreMultiWindowBase::Dispose();
379 if (getenv("AUL_LOADER_INIT")) {
380 unsetenv("AUL_LOADER_INIT");
385 void WidgetBase::ExitContext(
386 std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
387 AppCoreMultiWindowBase::ExitContext(context);
388 int cnt = GetContextCnt();
392 aul_widget_write_log(LOG_TAG,
393 "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
394 context->GetInstId().c_str());
397 void WidgetBase::Exit() {
398 AppCoreMultiWindowBase::Exit();
399 int cnt = GetContextCnt();
401 if (cnt == 0 && __context.IsPermanent())
402 ret = aul_notify_exit();
404 aul_widget_write_log(LOG_TAG,
405 "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
406 __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
409 int WidgetBase::OnCreate() {
410 AppCoreMultiWindowBase::OnCreate();
412 char appid[256] = { 0, };
413 int ret = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
414 if (ret != AUL_R_OK) {
415 _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
419 __context.SetAppId(appid);
421 char pkgid[256] = { 0, };
422 ret = aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid));
423 if (ret != AUL_R_OK) {
424 _E("aul_app_get_pkgid_bypid() is failed. error(%d)", ret);
428 __context.SetPackageId(pkgid);
430 screen_connector_provider_init();
431 _D("Widget base is created");
436 int WidgetBase::OnTerminate() {
437 screen_connector_provider_fini();
440 AppCoreMultiWindowBase::OnTerminate();
441 _D("Widget base is terminated");
445 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
446 AppCoreMultiWindowBase::OnReceive(type,
447 tizen_base::Bundle(b.GetHandle(), false, false));
451 for (auto& i : GetContexts())
458 case AUL_WIDGET_CONTENT:
459 impl_->GetContent(b);
468 int WidgetBase::OnControl(tizen_base::Bundle b) {
469 AppCoreMultiWindowBase::OnControl(
470 tizen_base::Bundle(b.GetHandle(), false, false));
472 std::string class_id = b.GetString(WIDGET_K_CLASS);
473 /* for previous version compatibility, use appid for default class id */
474 if (class_id.empty())
475 class_id = __context.GetAppId();
477 std::string id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
478 std::string operation = b.GetString(WIDGET_K_OPERATION);
479 if (operation.empty()) {
480 _E("Operation is empty");
484 _I("Operation(%s)", operation.c_str());
485 if (operation == "create")
486 impl_->ControlCreate(class_id, id, b);
487 else if (operation == "resize")
488 impl_->ControlResize(class_id, id, b);
489 else if (operation == "update")
490 impl_->ControlUpdate(class_id, id, b);
491 else if (operation == "destroy")
492 impl_->ControlDestroy(class_id, id, b);
493 else if (operation == "resume")
494 impl_->ControlResume(class_id, id, b);
495 else if (operation == "pause")
496 impl_->ControlPause(class_id, id, b);
497 else if (operation == "terminate")
498 impl_->ControlDestroy(class_id, id, b);
499 else if (operation == "period")
500 impl_->ControlChangePeriod(class_id, id, b);
505 /* LCOV_EXCL_START */
506 void WidgetContext::Impl::OnUpdate(bool force) {
507 parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
508 tizen_base::Bundle(pending_content_), force);
510 std::string class_id = parent_->GetContextId();
511 std::string id = parent_->GetInstId();
512 __context.SendUpdateStatus(class_id, id,
513 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
514 _D("Updated: %s", id.c_str());
518 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
520 std::string force_str = b.GetString(WIDGET_K_FORCE);
521 if (force_str == "true")
526 std::string content_raw = b.GetString(WIDGET_K_CONTENT_INFO);
527 if (!parent_->IsResumed() && !force) {
528 pending_content_ = content_raw;
529 pending_update_ = true;
531 parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
532 tizen_base::Bundle(content_raw), force);
534 std::string class_id = parent_->GetContextId();
535 std::string id = parent_->GetInstId();
536 __context.SendUpdateStatus(class_id, id,
537 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
538 _D("Updated: %s", id.c_str());
542 /* LCOV_EXCL_START */
543 void WidgetContext::Impl::SetPeriod(double period) {
547 void WidgetContext::Impl::SetPeriodicTimer() {
552 _D("Restart timer!");
553 periodic_timer_ = g_timeout_add_seconds(period_,
554 TimedOutCb, parent_);
559 void WidgetContext::Impl::UnsetPeriodicTimer() {
560 if (periodic_timer_) {
562 g_source_remove(periodic_timer_);
567 /* LCOV_EXCL_START */
568 gboolean WidgetContext::Impl::TimedOutCb(gpointer user_data) {
569 WidgetContext* wc = reinterpret_cast<WidgetContext*>(user_data);
570 if (wc->IsResumed()) {
571 _D("Periodic update!");
572 wc->OnUpdate(tizen_base::Bundle(), false);
574 __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
575 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
576 _D("Updated: %s", wc->GetInstId().c_str());
578 wc->impl_->pending_update_ = true;
579 wc->impl_->UnsetPeriodicTimer();
582 return G_SOURCE_CONTINUE;
586 WidgetContext::WidgetContext(std::string context_id, std::string inst_id,
587 AppCoreMultiWindowBase* app)
588 : AppCoreMultiWindowBase::Context(
589 std::move(context_id), std::move(inst_id), app),
590 impl_(std::make_unique<WidgetContext::Impl>(this)) {
593 WidgetContext::~WidgetContext() = default;
595 /* LCOV_EXCL_START */
596 void WidgetContext::OnPause() {
597 std::string id = GetInstId();
598 _D("WidgetContext(%s) is paused", id.c_str());
599 std::string class_id = GetContextId();
600 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
601 WIDGET_INSTANCE_EVENT_PAUSE, 0, nullptr);
603 if (__context.IsFgSignal()) {
604 _D("Send bg signal to resourceD");
605 aul_widget_instance_change_status(class_id.c_str(), kStatusBackground);
606 __context.SetFgSignal(false);
610 void WidgetContext::OnResume() {
611 if (impl_->pending_update_) {
612 _D("Pending update!");
613 impl_->pending_update_ = false;
614 impl_->OnUpdate(false);
615 impl_->SetPeriodicTimer();
618 std::string id = GetInstId();
619 _D("WidgetContext(%s) is resumed", id.c_str());
620 std::string class_id = GetContextId();
621 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
622 WIDGET_INSTANCE_EVENT_RESUME, 0, nullptr);
624 if (!__context.IsFgSignal()) {
625 _D("Send fg signal to resourceD");
626 aul_widget_instance_change_status(class_id.c_str(), kStatusForeground);
627 __context.SetFgSignal(true);
632 void WidgetContext::OnResize(int w, int h) {
635 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
638 void WidgetContext::ExitAsync() {
639 tizen_base::Bundle b;
640 b.Add(WIDGET_K_OPERATION, "terminate");
642 impl_->args_.reset(new tizen_base::Bundle(std::move(b)));
643 g_idle_add([](gpointer user_data) -> gboolean {
644 if (__widget == nullptr)
645 return G_SOURCE_REMOVE;
647 WidgetContext* wc = static_cast<WidgetContext*>(user_data);
649 std::string id = wc->GetInstId();
650 auto context = __widget->FindById(id);
651 if (context.get() == nullptr) {
652 _E("Failed to find context. ID(%s)", id.c_str());
653 return G_SOURCE_REMOVE;
656 __widget->ExitContext(context);
657 return G_SOURCE_REMOVE;
661 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
662 std::string id = GetInstId();
664 return WIDGET_ERROR_FAULT;
666 std::string class_id = GetContextId();
667 if (class_id.empty())
668 return WIDGET_ERROR_FAULT;
670 int ret = __context.SendUpdateStatus(class_id, id,
671 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
673 _E("Failed to send content info: %s of %s (%d)",
674 id.c_str(), class_id.c_str(), ret);
675 return WIDGET_ERROR_FAULT;
679 auto contents_raw = const_cast<tizen_base::Bundle&>(contents).ToRaw();
680 impl_->content_ = std::string(
681 reinterpret_cast<char*>(contents_raw.first.get()));
682 } catch (const std::bad_alloc& e) {
683 impl_->content_ = "";
684 _E("Exception(%s) occurs", e.what());
687 return WIDGET_ERROR_NONE;
690 int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
691 wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
692 if (surface == nullptr) {
693 _E("Failed to get surface, wl_win(%p)", wl_win);
697 screen_connector_provider_remote_enable(id.c_str(), surface);
702 void WidgetContext::OnCreate() {
703 auto& b = impl_->args_;
704 std::string class_id = b->GetString(WIDGET_K_CLASS);
705 /* For previous version compatibility, use appid for default class id */
706 if (class_id.empty())
707 class_id = __context.GetAppId();
709 std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
710 std::string operation = b->GetString(WIDGET_K_OPERATION);
711 if (operation.empty()) {
712 _E("Operation is empty");
716 std::string w_str = b->GetString(WIDGET_K_WIDTH);
718 if (!w_str.empty()) {
719 char* remain = nullptr;
720 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
723 std::string h_str = b->GetString(WIDGET_K_HEIGHT);
725 if (!h_str.empty()) {
726 char* remain = nullptr;
727 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
730 tizen_base::Bundle content_info;
731 std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
733 if (!content.empty())
734 content_info = tizen_base::Bundle(content);
735 } catch (std::bad_alloc& e) {
736 _W("Failed to decode content info(%s)", content.c_str());
739 bool r = OnCreate(content_info, w, h);
741 _W("Create callback returns error");
742 int ret = __context.SendUpdateStatus(class_id, id,
743 WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
745 _E("Failed to send abrot status. error(%d)", ret);
747 throw std::runtime_error("Create callback returns error");
749 _D("WidgetContext(%s) is created", id.c_str());
750 aul_widget_instance_add(class_id.c_str(), id.c_str());
751 int ret = __context.SendUpdateStatus(class_id, id,
752 WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
754 _E("Failed to send create status. error(%d)", ret);
756 auto v = b->GetByte(WIDGET_K_PERIOD);
760 const double* period = reinterpret_cast<double*>(v.data());
762 _I("Set periodic update timer. period(%lf)", *period);
763 impl_->SetPeriod(*period);
764 impl_->SetPeriodicTimer();
769 void WidgetContext::OnTerminate() {
770 DestroyType reason = DestroyType::TEMPORARY;
771 int event = WIDGET_INSTANCE_EVENT_TERMINATE;
772 std::string id = GetInstId();
773 std::string class_id = GetContextId();
774 auto& b = impl_->args_;
776 std::string operation = b->GetString(WIDGET_K_OPERATION);
777 if (operation == "destroy")
778 reason = DestroyType::PERMANENT;
781 tizen_base::Bundle content_info;
783 if (!impl_->content_.empty())
784 content_info = tizen_base::Bundle(impl_->content_);
785 } catch (const std::bad_alloc& e) {
786 _E("Exception(%s) occurs", e.what());
789 OnDestroy(reason, content_info);
791 _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
792 reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
793 if (reason == DestroyType::PERMANENT) {
794 __context.SetPermanent(true);
795 event = WIDGET_INSTANCE_EVENT_DESTROY;
796 aul_widget_instance_del(class_id.c_str(), id.c_str());
798 __context.SetPermanent(false);
799 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
800 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
803 impl_->UnsetPeriodicTimer();
805 __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
808 } // namespace tizen_cpp