26219b39f9b802d9575fba3c8b6c3078b0b25b59
[platform/core/appfw/appcore-widget.git] / src / base / widget_base.cc
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <aul.h>
18 #include <aul_app_com.h>
19 #include <aul_widget.h>
20 #include <bundle_internal.h>
21 #include <dlog.h>
22 #include <glib.h>
23 #include <screen_connector_provider.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <widget_errno.h>
27 #include <widget_instance.h>
28
29 #include <stdexcept>
30
31 #include <app_core_multi_window_base.hh>
32
33 #include "common/log_private.hh"
34 #include "include/widget_base.hh"
35
36 namespace tizen_cpp {
37 namespace {
38
39 constexpr const char kStatusForeground[] = "fg";
40 constexpr const char kStatusBackground[] = "bg";
41
42 class AppContext {
43  public:
44   void SetAppId(const std::string& app_id) {
45     app_id_ = app_id;
46   }
47
48   const std::string& GetAppId() const {
49     return app_id_;
50   }
51
52   void SetPackageId(const std::string& package_id) {
53     package_id_ = package_id;
54   }
55
56   const std::string& GetPackageId() const {
57     return package_id_;
58   }
59
60   void SetViewerEndpoint(const std::string& viewer_endpoint) {
61     viewer_endpoint_ = viewer_endpoint;
62   }
63
64   const std::string& GetViewerEndpoint() const {
65     return viewer_endpoint_;
66   }
67
68   void SetPermanent(bool permanent) {
69     permanent_ = permanent;
70   }
71
72   bool IsPermanent() const {
73     return permanent_;
74   }
75
76   void SetFgSignal(bool fg_signal) {
77     fg_signal_ = fg_signal;
78   }
79
80   bool IsFgSignal() const {
81     return fg_signal_;
82   }
83
84   int SendUpdateStatus(const std::string& class_id,
85       const std::string& instance_id, int status, int err,
86       bundle* extra) {
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);
89     if (ret != AUL_R_OK)
90       return ret;
91
92     int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
93     if (lifecycle > -1) {
94       ret = aul_widget_send_status_to_service(class_id.c_str(),
95           instance_id.c_str(), package_id_.c_str(), lifecycle);
96     }
97
98     return ret;
99   }
100
101  private:
102   std::string app_id_;
103   std::string package_id_;
104   std::string viewer_endpoint_;
105   bool permanent_ = false;
106   bool fg_signal_ = false;
107 };
108
109 AppContext __context;
110
111 }  // namespace
112
113 class WidgetBase::Impl {
114  public:
115   explicit Impl(WidgetBase* parent) : parent_(parent) {}
116
117  private:
118   friend class WidgetBase;
119   WidgetBase* parent_;
120
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);
136 };
137
138 WidgetBase* __widget;
139
140 class WidgetContext::Impl {
141  public:
142   explicit Impl(WidgetContext* parent) : parent_(parent) {}
143
144   ~Impl() {
145     UnsetPeriodicTimer();
146   }
147
148  private:
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);
155
156  private:
157   friend class WidgetContext;
158   friend class WidgetBase;
159   friend class WidgetBase::Impl;
160
161   WidgetContext* parent_;
162
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_;
169 };
170
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());
175     return;
176   }
177
178   std::shared_ptr<Context> context;
179   try {
180     context = parent_->CreateContext(class_id, id);
181   } catch (std::runtime_error& e) {
182     _E("runtime_error exception occurs");
183     return;
184   }
185
186   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
187   if (wc == nullptr)
188     return;
189
190   wc->impl_->args_.reset(new tizen_base::Bundle(b));
191   auto ctx = parent_->RunContext(context);
192   if (ctx.get() == nullptr)
193     return;
194
195   wc->impl_->args_.reset();
196   wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
197 }
198
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());
205     return;
206   }
207
208   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
209   if (wc == nullptr)
210     return;
211
212   wc->impl_->args_.reset(new tizen_base::Bundle(b));
213   parent_->ExitContext(context);
214 }
215
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());
221     return;
222   }
223
224   context->Resume();
225 }
226
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());
232     return;
233   }
234
235   context->Pause();
236 }
237
238 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
239     const std::string& id, const tizen_base::Bundle& b) {
240   if (id.empty()) {
241     for (auto& i : parent_->GetContexts()) {
242       if (i->GetContextId() == class_id) {
243         WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
244         if (cxt == nullptr)
245           continue;
246
247         cxt->impl_->UpdateProcess(b);
248       }
249     }
250     return;
251   }
252
253   auto context = parent_->FindById(id);
254   if (context.get() == nullptr) {
255     _E("Failed to find widget context. ID(%s)", id.c_str());
256     return;
257   }
258
259   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
260   if (wc == nullptr)
261     return;
262
263   wc->impl_->UpdateProcess(b);
264 }
265
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());
271     return;
272   }
273
274   int w = 0;
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));
279   }
280
281   int h = 0;
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));
286   }
287
288   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
289   if (wc == nullptr)
290     return;
291
292   wc->OnResize(w, h);
293
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);
297 }
298
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());
304     return;
305   }
306
307   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
308   if (wc == nullptr)
309     return;
310
311   wc->impl_->UnsetPeriodicTimer();
312
313   auto v = b.GetByte(WIDGET_K_PERIOD);
314   if (v.empty())
315     return;
316
317   const double* period = reinterpret_cast<double*>(v.data());
318   wc->impl_->SetPeriod(*period);
319   wc->impl_->SetPeriodicTimer();
320 }
321
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");
326     return;
327   }
328
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());
332     return;
333   }
334
335   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
336   if (wc == nullptr)
337     return;
338
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());
342   } else {
343     b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
344     _D("Empty content info added");
345   }
346 }
347
348 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
349 }
350
351 WidgetBase::~WidgetBase() = default;
352
353 std::string WidgetBase::GetViewerEndpoint() {
354   return __context.GetViewerEndpoint();
355 }
356
357 void WidgetBase::Run(int argc, char** argv) {
358   bundle* kb = bundle_import_from_argv(argc, argv);
359   if (kb) {
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());
364     else
365       _E("Endpoint is missing");
366   } else {
367     _E("Failed to get launch argv");
368     throw std::runtime_error("Failed to get launch argv");
369   }
370
371   AppCoreMultiWindowBase::Run(argc, argv);
372 }
373
374 void WidgetBase::Dispose() {
375   AppCoreMultiWindowBase::Dispose();
376   if (getenv("AUL_LOADER_INIT")) {
377       unsetenv("AUL_LOADER_INIT");
378       OnLoopFinish();
379   }
380 }
381
382 void WidgetBase::ExitContext(
383     std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
384   AppCoreMultiWindowBase::ExitContext(context);
385   int cnt = GetContextCnt();
386   if (cnt == 0)
387     Exit();
388
389   aul_widget_write_log(LOG_TAG,
390       "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
391       context->GetInstId().c_str());
392 }
393
394 void WidgetBase::Exit() {
395   AppCoreMultiWindowBase::Exit();
396   int cnt = GetContextCnt();
397   int ret = 0;
398   if (cnt == 0 && __context.IsPermanent())
399     ret = aul_notify_exit();
400
401   aul_widget_write_log(LOG_TAG,
402       "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
403       __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
404 }
405
406 int WidgetBase::OnCreate() {
407   AppCoreMultiWindowBase::OnCreate();
408
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);
413     return -1;
414   }
415
416   __context.SetAppId(appid);
417
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);
422     return -1;
423   }
424
425   __context.SetPackageId(pkgid);
426
427   screen_connector_provider_init();
428   _D("Widget base is created");
429   __widget = this;
430   return 0;
431 }
432
433 int WidgetBase::OnTerminate() {
434   screen_connector_provider_fini();
435   __widget = nullptr;
436
437   AppCoreMultiWindowBase::OnTerminate();
438   _D("Widget base is terminated");
439   return 0;
440 }
441
442 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
443   AppCoreMultiWindowBase::OnReceive(type,
444       tizen_base::Bundle(b.GetHandle(), false, false));
445
446   switch (type) {
447     case AUL_RESUME: {
448         for (auto& i : GetContexts())
449           i->Resume();
450       }
451       break;
452     case AUL_TERMINATE:
453       Exit();
454       break;
455     case AUL_WIDGET_CONTENT:
456       impl_->GetContent(b);
457       break;
458     default:
459       break;
460   }
461
462   return 0;
463 }
464
465 int WidgetBase::OnControl(tizen_base::Bundle b) {
466   AppCoreMultiWindowBase::OnControl(
467       tizen_base::Bundle(b.GetHandle(), false, false));
468
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();
473
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");
478     return 0;
479   }
480
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);
498
499   return 0;
500 }
501
502 void WidgetContext::Impl::OnUpdate(bool force) {
503   parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
504       tizen_base::Bundle(pending_content_), force);
505
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());
511 }
512
513 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
514   bool force;
515   std::string force_str = b.GetString(WIDGET_K_FORCE);
516   if (force_str == "true")
517     force = true;
518   else
519     force = false;
520
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;
525   } else {
526     parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
527         tizen_base::Bundle(content_raw), force);
528
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());
534   }
535 }
536
537 void WidgetContext::Impl::SetPeriod(double period) {
538   period_ = period;
539 }
540
541 void WidgetContext::Impl::SetPeriodicTimer() {
542   if (periodic_timer_)
543     return;
544
545   if (period_ > 0) {
546     _D("Restart timer!");
547     periodic_timer_ = g_timeout_add_seconds(period_,
548         TimedOutCb, parent_);
549   }
550 }
551
552 void WidgetContext::Impl::UnsetPeriodicTimer() {
553   if (periodic_timer_) {
554     _D("Remove timer!");
555     g_source_remove(periodic_timer_);
556     periodic_timer_ = 0;
557   }
558 }
559
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);
565
566     __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
567         WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
568     _D("Updated: %s", wc->GetInstId().c_str());
569   } else {
570     wc->impl_->pending_update_ = true;
571     wc->impl_->UnsetPeriodicTimer();
572   }
573
574   return G_SOURCE_CONTINUE;
575 }
576
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)) {
582 }
583
584 WidgetContext::~WidgetContext() = default;
585
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);
592
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);
597   }
598 }
599
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();
606   }
607
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);
613
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);
618   }
619 }
620
621 void WidgetContext::OnResize(int w, int h) {
622 }
623
624 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
625 }
626
627 void WidgetContext::ExitAsync() {
628   tizen_base::Bundle b;
629   b.Add(WIDGET_K_OPERATION, "terminate");
630
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;
635
636     WidgetContext* wc = static_cast<WidgetContext*>(user_data);
637
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;
643     }
644
645     __widget->ExitContext(context);
646     return G_SOURCE_REMOVE;
647   }, this);
648 }
649
650 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
651   std::string id = GetInstId();
652   if (id.empty())
653     return WIDGET_ERROR_FAULT;
654
655   std::string class_id = GetContextId();
656   if (class_id.empty())
657     return WIDGET_ERROR_FAULT;
658
659   int ret = __context.SendUpdateStatus(class_id, id,
660       WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
661   if (ret < 0) {
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;
665   }
666
667   try {
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());
674   }
675
676   return WIDGET_ERROR_NONE;
677 }
678
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);
683     return -1;
684   }
685
686   screen_connector_provider_remote_enable(id.c_str(), surface);
687   WindowBind(wl_win);
688   return 0;
689 }
690
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();
697
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");
702     return;
703   }
704
705   std::string w_str = b->GetString(WIDGET_K_WIDTH);
706   int w = 0;
707   if (!w_str.empty()) {
708     char* remain = nullptr;
709     w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
710   }
711
712   std::string h_str = b->GetString(WIDGET_K_HEIGHT);
713   int h = 0;
714   if (!h_str.empty()) {
715     char* remain = nullptr;
716     h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
717   }
718
719   tizen_base::Bundle content_info;
720   std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
721   try {
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());
726   }
727
728   bool r = OnCreate(content_info, w, h);
729   if (!r) {
730     _W("Create callback returns error");
731     int ret = __context.SendUpdateStatus(class_id, id,
732         WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
733     if (ret != 0)
734       _E("Failed to send abrot status. error(%d)", ret);
735
736     throw std::runtime_error("Create callback returns error");
737   } else {
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);
742     if (ret != 0)
743       _E("Failed to send create status. error(%d)", ret);
744
745     auto v = b->GetByte(WIDGET_K_PERIOD);
746     if (v.empty())
747       return;
748
749     const double* period = reinterpret_cast<double*>(v.data());
750     if (*period > 0) {
751       _I("Set periodic update timer. period(%lf)", *period);
752       impl_->SetPeriod(*period);
753       impl_->SetPeriodicTimer();
754     }
755   }
756 }
757
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_;
764   if (b.get()) {
765     std::string operation = b->GetString(WIDGET_K_OPERATION);
766     if (operation == "destroy")
767       reason = DestroyType::PERMANENT;
768   }
769
770   tizen_base::Bundle content_info;
771   try {
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());
776   }
777
778   OnDestroy(reason, content_info);
779
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());
786   } else {
787     __context.SetPermanent(false);
788     __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
789         WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
790   }
791
792   impl_->UnsetPeriodicTimer();
793
794   __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
795 }
796
797 }  // namespace tizen_cpp