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) {
48 const std::string& GetAppId() const {
52 void SetPackageId(const std::string& package_id) {
53 package_id_ = package_id;
56 const std::string& GetPackageId() const {
60 void SetViewerEndpoint(const std::string& viewer_endpoint) {
61 viewer_endpoint_ = viewer_endpoint;
64 const std::string& GetViewerEndpoint() const {
65 return viewer_endpoint_;
68 void SetPermanent(bool permanent) {
69 permanent_ = permanent;
72 bool IsPermanent() const {
76 void SetFgSignal(bool fg_signal) {
77 fg_signal_ = fg_signal;
80 bool IsFgSignal() const {
84 int SendUpdateStatus(const std::string& class_id,
85 const std::string& instance_id, int status, int err,
87 int ret = aul_widget_send_status_to_viewer(class_id.c_str(),
88 instance_id.c_str(), viewer_endpoint_.c_str(), status, err, extra);
92 int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
94 ret = aul_widget_send_status_to_service(class_id.c_str(),
95 instance_id.c_str(), package_id_.c_str(), lifecycle);
103 std::string package_id_;
104 std::string viewer_endpoint_;
105 bool permanent_ = false;
106 bool fg_signal_ = false;
109 AppContext __context;
113 class WidgetBase::Impl {
115 explicit Impl(WidgetBase* parent) : parent_(parent) {}
118 friend class WidgetBase;
121 void ControlCreate(const std::string& class_id, const std::string& id,
122 const tizen_base::Bundle& b);
123 void ControlDestroy(const std::string& class_id, const std::string& id,
124 const tizen_base::Bundle& b);
125 void ControlResume(const std::string& class_id, const std::string& id,
126 const tizen_base::Bundle& b);
127 void ControlPause(const std::string& class_id, const std::string& id,
128 const tizen_base::Bundle& b);
129 void ControlChangePeriod(const std::string& class_id, const std::string& id,
130 const tizen_base::Bundle& b);
131 void ControlUpdate(const std::string& class_id, const std::string& id,
132 const tizen_base::Bundle& b);
133 void ControlResize(const std::string& class_id, const std::string& id,
134 const tizen_base::Bundle& b);
135 void GetContent(tizen_base::Bundle& b);
138 WidgetBase* __widget;
140 class WidgetContext::Impl {
142 explicit Impl(WidgetContext* parent) : parent_(parent) {}
145 UnsetPeriodicTimer();
149 void UpdateProcess(const tizen_base::Bundle& b);
150 void OnUpdate(bool force);
151 void SetPeriod(double period);
152 void SetPeriodicTimer();
153 void UnsetPeriodicTimer();
154 static gboolean TimedOutCb(gpointer user_data);
157 friend class WidgetContext;
158 friend class WidgetBase;
159 friend class WidgetBase::Impl;
161 WidgetContext* parent_;
163 std::unique_ptr<tizen_base::Bundle> args_;
164 std::string content_;
165 double period_ = 0.0f;
166 guint periodic_timer_ = 0;
167 bool pending_update_ = false;
168 std::string pending_content_;
171 void WidgetBase::Impl::ControlCreate(const std::string& class_id,
172 const std::string& id, const tizen_base::Bundle& b) {
173 if (parent_->FindById(id).get() != nullptr) {
174 _E("Already exists. ID(%s)", id.c_str());
178 std::shared_ptr<Context> context;
180 context = parent_->CreateContext(class_id, id);
181 } catch (std::runtime_error& e) {
182 _E("runtime_error exception occurs");
186 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
190 wc->impl_->args_.reset(new tizen_base::Bundle(b));
191 auto ctx = parent_->RunContext(context);
192 if (ctx.get() == nullptr)
195 wc->impl_->args_.reset();
196 wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
199 void WidgetBase::Impl::ControlDestroy(const std::string& class_id,
200 const std::string& id, const tizen_base::Bundle& b) {
201 auto context = parent_->FindById(id);
202 if (context.get() == nullptr) {
203 _E("Failed to find widget context. ID(%s)", id.c_str());
204 aul_widget_instance_del(class_id.c_str(), id.c_str());
208 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
212 wc->impl_->args_.reset(new tizen_base::Bundle(b));
213 parent_->ExitContext(context);
216 void WidgetBase::Impl::ControlResume(const std::string& class_id,
217 const std::string& id, const tizen_base::Bundle& b) {
218 auto context = parent_->FindById(id);
219 if (context.get() == nullptr) {
220 _E("Failed to find widget context. ID(%s)", id.c_str());
227 void WidgetBase::Impl::ControlPause(const std::string& class_id,
228 const std::string& id, const tizen_base::Bundle& b) {
229 auto context = parent_->FindById(id);
230 if (context.get() == nullptr) {
231 _E("Failed to find widget context. ID(%s)", id.c_str());
238 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
239 const std::string& id, const tizen_base::Bundle& b) {
241 for (auto& i : parent_->GetContexts()) {
242 if (i->GetContextId() == class_id) {
243 WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
247 cxt->impl_->UpdateProcess(b);
253 auto context = parent_->FindById(id);
254 if (context.get() == nullptr) {
255 _E("Failed to find widget context. ID(%s)", id.c_str());
259 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
263 wc->impl_->UpdateProcess(b);
266 void WidgetBase::Impl::ControlResize(const std::string& class_id,
267 const std::string& id, const tizen_base::Bundle& b) {
268 auto context = parent_->FindById(id);
269 if (context.get() == nullptr) {
270 _E("Failed to find widget context. ID(%s)", id.c_str());
275 std::string w_str = b.GetString(WIDGET_K_WIDTH);
276 if (!w_str.empty()) {
277 char* remain = nullptr;
278 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
282 std::string h_str = b.GetString(WIDGET_K_HEIGHT);
283 if (!h_str.empty()) {
284 char* remain = nullptr;
285 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
288 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
294 _D("WidgetContext(%s) is resized to %dx%d", id.c_str(), w, h);
295 __context.SendUpdateStatus(class_id, id,
296 WIDGET_INSTANCE_EVENT_SIZE_CHANGED, 0, nullptr);
299 void WidgetBase::Impl::ControlChangePeriod(const std::string& class_id,
300 const std::string& id, const tizen_base::Bundle& b) {
301 auto context = parent_->FindById(id);
302 if (context.get() == nullptr) {
303 _E("Failed to find widget context. ID(%s)", id.c_str());
307 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
311 wc->impl_->UnsetPeriodicTimer();
313 auto v = b.GetByte(WIDGET_K_PERIOD);
317 const double* period = reinterpret_cast<double*>(v.data());
318 wc->impl_->SetPeriod(*period);
319 wc->impl_->SetPeriodicTimer();
322 void WidgetBase::Impl::GetContent(tizen_base::Bundle& b) {
323 std::string instance_id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
324 if (instance_id.empty()) {
325 _E("Instance ID is nullptr");
329 auto context = parent_->FindById(instance_id);
330 if (context.get() == nullptr) {
331 _E("Failed to find widget context. ID(%s)", instance_id.c_str());
335 WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
339 if (!wc->impl_->content_.empty()) {
340 b.Add(AUL_K_WIDGET_CONTENT_INFO, wc->impl_->content_);
341 _D("Content info of %s found", instance_id.c_str());
343 b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
344 _D("Empty content info added");
348 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
351 WidgetBase::~WidgetBase() = default;
353 std::string WidgetBase::GetViewerEndpoint() {
354 return __context.GetViewerEndpoint();
357 void WidgetBase::Run(int argc, char** argv) {
358 bundle* kb = bundle_import_from_argv(argc, argv);
360 tizen_base::Bundle b(kb, false, true);
361 __context.SetViewerEndpoint(b.GetString(AUL_K_WIDGET_VIEWER));
362 if (!__context.GetViewerEndpoint().empty())
363 _D("Viewer endpoint :%s", __context.GetViewerEndpoint().c_str());
365 _E("Endpoint is missing");
367 _E("Failed to get launch argv");
368 throw std::runtime_error("Failed to get launch argv");
371 AppCoreMultiWindowBase::Run(argc, argv);
374 void WidgetBase::Dispose() {
375 AppCoreMultiWindowBase::Dispose();
376 if (getenv("AUL_LOADER_INIT")) {
377 unsetenv("AUL_LOADER_INIT");
382 void WidgetBase::ExitContext(
383 std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
384 AppCoreMultiWindowBase::ExitContext(context);
385 int cnt = GetContextCnt();
389 aul_widget_write_log(LOG_TAG,
390 "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
391 context->GetInstId().c_str());
394 void WidgetBase::Exit() {
395 AppCoreMultiWindowBase::Exit();
396 int cnt = GetContextCnt();
398 if (cnt == 0 && __context.IsPermanent())
399 ret = aul_notify_exit();
401 aul_widget_write_log(LOG_TAG,
402 "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
403 __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
406 int WidgetBase::OnCreate() {
407 AppCoreMultiWindowBase::OnCreate();
409 char appid[256] = { 0, };
410 int ret = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
411 if (ret != AUL_R_OK) {
412 _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
416 __context.SetAppId(appid);
418 char pkgid[256] = { 0, };
419 ret = aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid));
420 if (ret != AUL_R_OK) {
421 _E("aul_app_get_pkgid_bypid() is failed. error(%d)", ret);
425 __context.SetPackageId(pkgid);
427 screen_connector_provider_init();
428 _D("Widget base is created");
433 int WidgetBase::OnTerminate() {
434 screen_connector_provider_fini();
437 AppCoreMultiWindowBase::OnTerminate();
438 _D("Widget base is terminated");
442 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
443 AppCoreMultiWindowBase::OnReceive(type,
444 tizen_base::Bundle(b.GetHandle(), false, false));
448 for (auto& i : GetContexts())
455 case AUL_WIDGET_CONTENT:
456 impl_->GetContent(b);
465 int WidgetBase::OnControl(tizen_base::Bundle b) {
466 AppCoreMultiWindowBase::OnControl(
467 tizen_base::Bundle(b.GetHandle(), false, false));
469 std::string class_id = b.GetString(WIDGET_K_CLASS);
470 /* for previous version compatibility, use appid for default class id */
471 if (class_id.empty())
472 class_id = __context.GetAppId();
474 std::string id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
475 std::string operation = b.GetString(WIDGET_K_OPERATION);
476 if (operation.empty()) {
477 _E("Operation is empty");
481 _I("Operation(%s)", operation.c_str());
482 if (operation == "create")
483 impl_->ControlCreate(class_id, id, b);
484 else if (operation == "resize")
485 impl_->ControlResize(class_id, id, b);
486 else if (operation == "update")
487 impl_->ControlUpdate(class_id, id, b);
488 else if (operation == "destroy")
489 impl_->ControlDestroy(class_id, id, b);
490 else if (operation == "resume")
491 impl_->ControlResume(class_id, id, b);
492 else if (operation == "pause")
493 impl_->ControlPause(class_id, id, b);
494 else if (operation == "terminate")
495 impl_->ControlDestroy(class_id, id, b);
496 else if (operation == "period")
497 impl_->ControlChangePeriod(class_id, id, b);
502 void WidgetContext::Impl::OnUpdate(bool force) {
503 parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
504 tizen_base::Bundle(pending_content_), force);
506 std::string class_id = parent_->GetContextId();
507 std::string id = parent_->GetInstId();
508 __context.SendUpdateStatus(class_id, id,
509 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
510 _D("Updated: %s", id.c_str());
513 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
515 std::string force_str = b.GetString(WIDGET_K_FORCE);
516 if (force_str == "true")
521 std::string content_raw = b.GetString(WIDGET_K_CONTENT_INFO);
522 if (!parent_->IsResumed() && !force) {
523 pending_content_ = content_raw;
524 pending_update_ = true;
526 parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
527 tizen_base::Bundle(content_raw), force);
529 std::string class_id = parent_->GetContextId();
530 std::string id = parent_->GetInstId();
531 __context.SendUpdateStatus(class_id, id,
532 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
533 _D("Updated: %s", id.c_str());
537 void WidgetContext::Impl::SetPeriod(double period) {
541 void WidgetContext::Impl::SetPeriodicTimer() {
546 _D("Restart timer!");
547 periodic_timer_ = g_timeout_add_seconds(period_,
548 TimedOutCb, parent_);
552 void WidgetContext::Impl::UnsetPeriodicTimer() {
553 if (periodic_timer_) {
555 g_source_remove(periodic_timer_);
560 gboolean WidgetContext::Impl::TimedOutCb(gpointer user_data) {
561 WidgetContext* wc = reinterpret_cast<WidgetContext*>(user_data);
562 if (wc->IsResumed()) {
563 _D("Periodic update!");
564 wc->OnUpdate(tizen_base::Bundle(), false);
566 __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
567 WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
568 _D("Updated: %s", wc->GetInstId().c_str());
570 wc->impl_->pending_update_ = true;
571 wc->impl_->UnsetPeriodicTimer();
574 return G_SOURCE_CONTINUE;
577 WidgetContext::WidgetContext(std::string context_id, std::string inst_id,
578 AppCoreMultiWindowBase* app)
579 : AppCoreMultiWindowBase::Context(
580 std::move(context_id), std::move(inst_id), app),
581 impl_(std::make_unique<WidgetContext::Impl>(this)) {
584 WidgetContext::~WidgetContext() = default;
586 void WidgetContext::OnPause() {
587 std::string id = GetInstId();
588 _D("WidgetContext(%s) is paused", id.c_str());
589 std::string class_id = GetContextId();
590 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
591 WIDGET_INSTANCE_EVENT_PAUSE, 0, nullptr);
593 if (__context.IsFgSignal()) {
594 _D("Send bg signal to resourceD");
595 aul_widget_instance_change_status(class_id.c_str(), kStatusBackground);
596 __context.SetFgSignal(false);
600 void WidgetContext::OnResume() {
601 if (impl_->pending_update_) {
602 _D("Pending update!");
603 impl_->pending_update_ = false;
604 impl_->OnUpdate(false);
605 impl_->SetPeriodicTimer();
608 std::string id = GetInstId();
609 _D("WidgetContext(%s) is resumed", id.c_str());
610 std::string class_id = GetContextId();
611 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
612 WIDGET_INSTANCE_EVENT_RESUME, 0, nullptr);
614 if (!__context.IsFgSignal()) {
615 _D("Send fg signal to resourceD");
616 aul_widget_instance_change_status(class_id.c_str(), kStatusForeground);
617 __context.SetFgSignal(true);
621 void WidgetContext::OnResize(int w, int h) {
624 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
627 void WidgetContext::ExitAsync() {
628 tizen_base::Bundle b;
629 b.Add(WIDGET_K_OPERATION, "terminate");
631 impl_->args_.reset(new tizen_base::Bundle(std::move(b)));
632 g_idle_add([](gpointer user_data) -> gboolean {
633 if (__widget == nullptr)
634 return G_SOURCE_REMOVE;
636 WidgetContext* wc = static_cast<WidgetContext*>(user_data);
638 std::string id = wc->GetInstId();
639 auto context = __widget->FindById(id);
640 if (context.get() == nullptr) {
641 _E("Failed to find context. ID(%s)", id.c_str());
642 return G_SOURCE_REMOVE;
645 __widget->ExitContext(context);
646 return G_SOURCE_REMOVE;
650 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
651 std::string id = GetInstId();
653 return WIDGET_ERROR_FAULT;
655 std::string class_id = GetContextId();
656 if (class_id.empty())
657 return WIDGET_ERROR_FAULT;
659 int ret = __context.SendUpdateStatus(class_id, id,
660 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
662 _E("Failed to send content info: %s of %s (%d)",
663 id.c_str(), class_id.c_str(), ret);
664 return WIDGET_ERROR_FAULT;
668 auto contents_raw = const_cast<tizen_base::Bundle&>(contents).ToRaw();
669 impl_->content_ = std::string(
670 reinterpret_cast<char*>(contents_raw.first.get()));
671 } catch (const std::bad_alloc& e) {
672 impl_->content_ = "";
673 _E("Exception(%s) occurs", e.what());
676 return WIDGET_ERROR_NONE;
679 int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
680 wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
681 if (surface == nullptr) {
682 _E("Failed to get surface, wl_win(%p)", wl_win);
686 screen_connector_provider_remote_enable(id.c_str(), surface);
691 void WidgetContext::OnCreate() {
692 auto& b = impl_->args_;
693 std::string class_id = b->GetString(WIDGET_K_CLASS);
694 /* For previous version compatibility, use appid for default class id */
695 if (class_id.empty())
696 class_id = __context.GetAppId();
698 std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
699 std::string operation = b->GetString(WIDGET_K_OPERATION);
700 if (operation.empty()) {
701 _E("Operation is empty");
705 std::string w_str = b->GetString(WIDGET_K_WIDTH);
707 if (!w_str.empty()) {
708 char* remain = nullptr;
709 w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
712 std::string h_str = b->GetString(WIDGET_K_HEIGHT);
714 if (!h_str.empty()) {
715 char* remain = nullptr;
716 h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
719 tizen_base::Bundle content_info;
720 std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
722 if (!content.empty())
723 content_info = tizen_base::Bundle(content);
724 } catch (std::bad_alloc& e) {
725 _W("Failed to decode content info(%s)", content.c_str());
728 bool r = OnCreate(content_info, w, h);
730 _W("Create callback returns error");
731 int ret = __context.SendUpdateStatus(class_id, id,
732 WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
734 _E("Failed to send abrot status. error(%d)", ret);
736 throw std::runtime_error("Create callback returns error");
738 _D("WidgetContext(%s) is created", id.c_str());
739 aul_widget_instance_add(class_id.c_str(), id.c_str());
740 int ret = __context.SendUpdateStatus(class_id, id,
741 WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
743 _E("Failed to send create status. error(%d)", ret);
745 auto v = b->GetByte(WIDGET_K_PERIOD);
749 const double* period = reinterpret_cast<double*>(v.data());
751 _I("Set periodic update timer. period(%lf)", *period);
752 impl_->SetPeriod(*period);
753 impl_->SetPeriodicTimer();
758 void WidgetContext::OnTerminate() {
759 DestroyType reason = DestroyType::TEMPORARY;
760 int event = WIDGET_INSTANCE_EVENT_TERMINATE;
761 std::string id = GetInstId();
762 std::string class_id = GetContextId();
763 auto& b = impl_->args_;
765 std::string operation = b->GetString(WIDGET_K_OPERATION);
766 if (operation == "destroy")
767 reason = DestroyType::PERMANENT;
770 tizen_base::Bundle content_info;
772 if (!impl_->content_.empty())
773 content_info = tizen_base::Bundle(impl_->content_);
774 } catch (const std::bad_alloc& e) {
775 _E("Exception(%s) occurs", e.what());
778 OnDestroy(reason, content_info);
780 _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
781 reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
782 if (reason == DestroyType::PERMANENT) {
783 __context.SetPermanent(true);
784 event = WIDGET_INSTANCE_EVENT_DESTROY;
785 aul_widget_instance_del(class_id.c_str(), id.c_str());
787 __context.SetPermanent(false);
788 __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
789 WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
792 impl_->UnsetPeriodicTimer();
794 __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
797 } // namespace tizen_cpp