Fix crash issue
[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 <app_core_multi_window_base.hh>
30
31 #include "common/log_private.hh"
32 #include "include/widget_base.hh"
33
34 namespace tizen_cpp {
35 namespace {
36
37 constexpr const char kStatusForeground[] = "fg";
38 constexpr const char kStatusBackground[] = "bg";
39
40 class AppContext {
41  public:
42   void SetAppId(const std::string& app_id) {
43     app_id_ = app_id;
44   }
45
46   const std::string& GetAppId() const {
47     return app_id_;
48   }
49
50   void SetPackageId(const std::string& package_id) {
51     package_id_ = package_id;
52   }
53
54   const std::string& GetPackageId() const {
55     return package_id_;
56   }
57
58   void SetViewerEndpoint(const std::string& viewer_endpoint) {
59     viewer_endpoint_ = viewer_endpoint;
60   }
61
62   const std::string& GetViewerEndpoint() const {
63     return viewer_endpoint_;
64   }
65
66   void SetPermanent(bool permanent) {
67     permanent_ = permanent;
68   }
69
70   bool IsPermanent() const {
71     return permanent_;
72   }
73
74   void SetFgSignal(bool fg_signal) {
75     fg_signal_ = fg_signal;
76   }
77
78   bool IsFgSignal() const {
79     return fg_signal_;
80   }
81
82   int SendUpdateStatus(const std::string& class_id,
83       const std::string& instance_id, int status, int err,
84       bundle* extra) {
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);
87     if (ret != AUL_R_OK)
88       return ret;
89
90     int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
91     if (lifecycle > -1) {
92       ret = aul_widget_send_status_to_service(class_id.c_str(),
93           instance_id.c_str(), package_id_.c_str(), lifecycle);
94     }
95
96     return ret;
97   }
98
99  private:
100   std::string app_id_;
101   std::string package_id_;
102   std::string viewer_endpoint_;
103   bool permanent_ = false;
104   bool fg_signal_ = false;
105 };
106
107 AppContext __context;
108
109 }  // namespace
110
111 class WidgetBase::Impl {
112  public:
113   explicit Impl(WidgetBase* parent) : parent_(parent) {}
114
115  private:
116   friend class WidgetBase;
117   WidgetBase* parent_;
118
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);
134 };
135
136 WidgetBase* __widget;
137
138 class WidgetContext::Impl {
139  public:
140   explicit Impl(WidgetContext* parent) : parent_(parent) {}
141
142   ~Impl() {
143     UnsetPeriodicTimer();
144   }
145
146  private:
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);
153
154  private:
155   friend class WidgetContext;
156   friend class WidgetBase;
157   friend class WidgetBase::Impl;
158
159   WidgetContext* parent_;
160
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_;
167 };
168
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());
173     return;
174   }
175
176   std::shared_ptr<Context> context;
177   try {
178     context = parent_->CreateContext(class_id, id);
179   } catch (std::runtime_error& e) {
180     _E("runtime_error exception occurs");
181     return;
182   }
183
184   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
185   if (wc == nullptr)
186     return;
187
188   wc->impl_->args_.reset(new tizen_base::Bundle(b));
189   auto ctx = parent_->RunContext(context);
190   if (ctx.get() == nullptr)
191     return;
192
193   wc->impl_->args_.reset();
194   wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
195 }
196
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());
203     return;
204   }
205
206   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
207   if (wc == nullptr)
208     return;
209
210   wc->impl_->args_.reset(new tizen_base::Bundle(b));
211   parent_->ExitContext(context);
212 }
213
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());
219     return;
220   }
221
222   context->Resume();
223 }
224
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());
230     return;
231   }
232
233   context->Pause();
234 }
235
236 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
237     const std::string& id, const tizen_base::Bundle& b) {
238   if (id.empty()) {
239     for (auto& i : parent_->GetContexts()) {
240       if (i->GetContextId() == class_id) {
241         WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
242         if (cxt == nullptr)
243           continue;
244
245         cxt->impl_->UpdateProcess(b);
246       }
247     }
248     return;
249   }
250
251   auto context = parent_->FindById(id);
252   if (context.get() == nullptr) {
253     _E("Failed to find widget context. ID(%s)", id.c_str());
254     return;
255   }
256
257   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
258   if (wc == nullptr)
259     return;
260
261   wc->impl_->UpdateProcess(b);
262 }
263
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());
269     return;
270   }
271
272   int w = 0;
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));
277   }
278
279   int h = 0;
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));
284   }
285
286   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
287   if (wc == nullptr)
288     return;
289
290   wc->OnResize(w, h);
291
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);
295 }
296
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());
302     return;
303   }
304
305   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
306   if (wc == nullptr)
307     return;
308
309   wc->impl_->UnsetPeriodicTimer();
310
311   auto v = b.GetByte(WIDGET_K_PERIOD);
312   if (v.empty())
313     return;
314
315   const double* period = reinterpret_cast<double*>(v.data());
316   wc->impl_->SetPeriod(*period);
317   wc->impl_->SetPeriodicTimer();
318 }
319
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");
324     return;
325   }
326
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());
330     return;
331   }
332
333   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
334   if (wc == nullptr)
335     return;
336
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());
340   } else {
341     b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
342     _D("Empty content info added");
343   }
344 }
345
346 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
347 }
348
349 WidgetBase::~WidgetBase() = default;
350
351 std::string WidgetBase::GetViewerEndpoint() {
352   return __context.GetViewerEndpoint();
353 }
354
355 void WidgetBase::Run(int argc, char** argv) {
356   bundle* kb = bundle_import_from_argv(argc, argv);
357   if (kb) {
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());
362     else
363       _E("Endpoint is missing");
364   } else {
365     _E("Failed to get launch argv");
366     throw std::runtime_error("Failed to get launch argv");
367   }
368
369   AppCoreMultiWindowBase::Run(argc, argv);
370 }
371
372 void WidgetBase::Dispose() {
373   AppCoreMultiWindowBase::Dispose();
374   if (getenv("AUL_LOADER_INIT")) {
375       unsetenv("AUL_LOADER_INIT");
376       OnLoopFinish();
377   }
378 }
379
380 void WidgetBase::ExitContext(
381     std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
382   AppCoreMultiWindowBase::ExitContext(context);
383   int cnt = GetContextCnt();
384   if (cnt == 0)
385     Exit();
386
387   aul_widget_write_log(LOG_TAG,
388       "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
389       context->GetInstId().c_str());
390 }
391
392 void WidgetBase::Exit() {
393   AppCoreMultiWindowBase::Exit();
394   int cnt = GetContextCnt();
395   int ret = 0;
396   if (cnt == 0 && __context.IsPermanent())
397     ret = aul_notify_exit();
398
399   aul_widget_write_log(LOG_TAG,
400       "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
401       __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
402 }
403
404 int WidgetBase::OnCreate() {
405   AppCoreMultiWindowBase::OnCreate();
406
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);
411     return -1;
412   }
413
414   __context.SetAppId(appid);
415
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);
420     return -1;
421   }
422
423   __context.SetPackageId(pkgid);
424
425   screen_connector_provider_init();
426   _D("Widget base is created");
427   __widget = this;
428   return 0;
429 }
430
431 int WidgetBase::OnTerminate() {
432   screen_connector_provider_fini();
433   __widget = nullptr;
434
435   AppCoreMultiWindowBase::OnTerminate();
436   _D("Widget base is terminated");
437   return 0;
438 }
439
440 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
441   AppCoreMultiWindowBase::OnReceive(type,
442       tizen_base::Bundle(b.GetHandle(), false, false));
443
444   switch (type) {
445     case AUL_RESUME: {
446         for (auto& i : GetContexts())
447           i->Resume();
448       }
449       break;
450     case AUL_TERMINATE:
451       Exit();
452       break;
453     case AUL_WIDGET_CONTENT:
454       impl_->GetContent(b);
455       break;
456     default:
457       break;
458   }
459
460   return 0;
461 }
462
463 int WidgetBase::OnControl(tizen_base::Bundle b) {
464   AppCoreMultiWindowBase::OnControl(
465       tizen_base::Bundle(b.GetHandle(), false, false));
466
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();
471
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");
476     return 0;
477   }
478
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);
496
497   return 0;
498 }
499
500 void WidgetContext::Impl::OnUpdate(bool force) {
501   parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
502       tizen_base::Bundle(pending_content_), force);
503
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());
509 }
510
511 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
512   bool force;
513   std::string force_str = b.GetString(WIDGET_K_FORCE);
514   if (force_str == "true")
515     force = true;
516   else
517     force = false;
518
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;
523   } else {
524     parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
525         tizen_base::Bundle(content_raw), force);
526
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());
532   }
533 }
534
535 void WidgetContext::Impl::SetPeriod(double period) {
536   period_ = period;
537 }
538
539 void WidgetContext::Impl::SetPeriodicTimer() {
540   if (periodic_timer_)
541     return;
542
543   if (period_ > 0) {
544     _D("Restart timer!");
545     periodic_timer_ = g_timeout_add_seconds(period_,
546         TimedOutCb, parent_);
547   }
548 }
549
550 void WidgetContext::Impl::UnsetPeriodicTimer() {
551   if (periodic_timer_) {
552     _D("Remove timer!");
553     g_source_remove(periodic_timer_);
554     periodic_timer_ = 0;
555   }
556 }
557
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);
563
564     __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
565         WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
566     _D("Updated: %s", wc->GetInstId().c_str());
567   } else {
568     wc->impl_->pending_update_ = true;
569     wc->impl_->UnsetPeriodicTimer();
570   }
571
572   return G_SOURCE_CONTINUE;
573 }
574
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)) {
580 }
581
582 WidgetContext::~WidgetContext() = default;
583
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);
590
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);
595   }
596 }
597
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();
604   }
605
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);
611
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);
616   }
617 }
618
619 void WidgetContext::OnResize(int w, int h) {
620 }
621
622 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
623 }
624
625 void WidgetContext::ExitAsync() {
626   tizen_base::Bundle b;
627   b.Add(WIDGET_K_OPERATION, "terminate");
628
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;
633
634     WidgetContext* wc = static_cast<WidgetContext*>(user_data);
635
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;
641     }
642
643     __widget->ExitContext(context);
644     return G_SOURCE_REMOVE;
645   }, this);
646 }
647
648 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
649   std::string id = GetInstId();
650   if (id.empty())
651     return WIDGET_ERROR_FAULT;
652
653   std::string class_id = GetContextId();
654   if (class_id.empty())
655     return WIDGET_ERROR_FAULT;
656
657   int ret = __context.SendUpdateStatus(class_id, id,
658       WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
659   if (ret < 0) {
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;
663   }
664
665   try {
666     auto contents_raw = const_cast<tizen_base::Bundle&>(contents).ToRaw();
667     impl_->content_ = std::string(
668         reinterpret_cast<char*>(contents_raw.first.get()));
669   } catch (const std::bad_alloc& e) {
670     impl_->content_ = "";
671     _E("Exception(%s) occurs", e.what());
672   }
673
674   return WIDGET_ERROR_NONE;
675 }
676
677 int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
678   wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
679   if (surface == nullptr) {
680     _E("Failed to get surface, wl_win(%p)", wl_win);
681     return -1;
682   }
683
684   screen_connector_provider_remote_enable(id.c_str(), surface);
685   WindowBind(wl_win);
686   return 0;
687 }
688
689 void WidgetContext::OnCreate() {
690   auto& b = impl_->args_;
691   std::string class_id = b->GetString(WIDGET_K_CLASS);
692   /* For previous version compatibility, use appid for default class id */
693   if (class_id.empty())
694     class_id = __context.GetAppId();
695
696   std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
697   std::string operation = b->GetString(WIDGET_K_OPERATION);
698   if (operation.empty()) {
699     _E("Operation is empty");
700     return;
701   }
702
703   std::string w_str = b->GetString(WIDGET_K_WIDTH);
704   int w = 0;
705   if (!w_str.empty()) {
706     char* remain = nullptr;
707     w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
708   }
709
710   std::string h_str = b->GetString(WIDGET_K_HEIGHT);
711   int h = 0;
712   if (!h_str.empty()) {
713     char* remain = nullptr;
714     h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
715   }
716
717   tizen_base::Bundle content_info;
718   std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
719   try {
720     if (!content.empty())
721       content_info = tizen_base::Bundle(content);
722   } catch (std::bad_alloc& e) {
723     _W("Failed to decode content info(%s)", content.c_str());
724   }
725
726   bool r = OnCreate(content_info, w, h);
727   if (!r) {
728     _W("Create callback returns error");
729     int ret = __context.SendUpdateStatus(class_id, id,
730         WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
731     if (ret != 0)
732       _E("Failed to send abrot status. error(%d)", ret);
733
734     throw std::runtime_error("Create callback returns error");
735   } else {
736     _D("WidgetContext(%s) is created", id.c_str());
737     aul_widget_instance_add(class_id.c_str(), id.c_str());
738     int ret = __context.SendUpdateStatus(class_id, id,
739         WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
740     if (ret != 0)
741       _E("Failed to send create status. error(%d)", ret);
742
743     auto v = b->GetByte(WIDGET_K_PERIOD);
744     if (v.empty())
745       return;
746
747     const double* period = reinterpret_cast<double*>(v.data());
748     if (*period > 0) {
749       _I("Set periodic update timer. period(%lf)", *period);
750       impl_->SetPeriod(*period);
751       impl_->SetPeriodicTimer();
752     }
753   }
754 }
755
756 void WidgetContext::OnTerminate() {
757   DestroyType reason = DestroyType::TEMPORARY;
758   int event = WIDGET_INSTANCE_EVENT_TERMINATE;
759   std::string id = GetInstId();
760   std::string class_id = GetContextId();
761   auto& b = impl_->args_;
762   if (b.get()) {
763     std::string operation = b->GetString(WIDGET_K_OPERATION);
764     if (operation == "destroy")
765       reason = DestroyType::PERMANENT;
766   }
767
768   tizen_base::Bundle content_info;
769   try {
770     if (!impl_->content_.empty())
771       content_info = tizen_base::Bundle(impl_->content_);
772   } catch (const std::bad_alloc& e) {
773     _E("Exception(%s) occurs", e.what());
774   }
775
776   OnDestroy(reason, content_info);
777
778   _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
779       reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
780   if (reason == DestroyType::PERMANENT) {
781     __context.SetPermanent(true);
782     event = WIDGET_INSTANCE_EVENT_DESTROY;
783     aul_widget_instance_del(class_id.c_str(), id.c_str());
784   } else {
785     __context.SetPermanent(false);
786     __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
787         WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
788   }
789
790   impl_->UnsetPeriodicTimer();
791
792   __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
793 }
794
795 }  // namespace tizen_cpp