aa55b1d3ab319f49f94d80c0d908375040eef012
[platform/core/appfw/app-core.git] / tizen-cpp / app-core-cpp / app_core_base.cc
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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 "app-core-cpp/app_core_base.hh"
18
19 #include <aul.h>
20 #include <aul_app_lifecycle.h>
21 #include <aul_watchdog.h>
22 #include <bundle_internal.h>
23 #include <dlfcn.h>
24 #include <errno.h>
25 #include <gio/gio.h>
26 #include <glib-unix.h>
27 #include <glib.h>
28 #include <libintl.h>
29 #include <linux/limits.h>
30 #include <locale.h>
31 #include <malloc.h>
32 #include <sensor_internal.h>
33 #include <signal.h>
34 #include <stdbool.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <system_info.h>
39 #include <ttrace.h>
40 #include <unistd.h>
41 #include <vconf.h>
42
43 #include <algorithm>
44 #include <cstring>
45 #include <list>
46 #include <map>
47 #include <memory>
48 #include <set>
49 #include <sstream>
50 #include <utility>
51 #include <vector>
52
53 #include "app-core-cpp/app_core_plugin_private.hh"
54 #include "app-core-cpp/exit_handler_private.hh"
55 #include "app-core-cpp/sigterm_handler_private.hh"
56 #include "common/glib_private.hh"
57 #include "common/log_private.hh"
58 #include "common/log_tracer.hh"
59
60 extern "C" void aul_finalize();
61 namespace tizen_cpp {
62
63 AppCoreBase* AppCoreBase::context_ = nullptr;
64
65 namespace {
66
67 internal::ExitHandler exit_handler;
68 internal::SigtermHandler sigterm_handler;
69
70 enum TizenProfile {
71   Unknown = 0x00,
72   Mobile = 0x01,
73   Wearable = 0x02,
74   Tv = 0x04,
75   Ivi = 0x08,
76   Common = 0x10,
77 };
78
79 TizenProfile TizenProfileGet() {
80   static TizenProfile profile = TizenProfile::Unknown;
81   if (__builtin_expect(profile != TizenProfile::Unknown, 1))
82     return profile;
83
84   char* profile_name = nullptr;
85   system_info_get_platform_string("http://tizen.org/feature/profile",
86       &profile_name);
87   if (profile_name == nullptr)
88     return profile;
89
90   switch (*profile_name) {
91   case 'm':
92   case 'M':
93     profile = TizenProfile::Mobile;
94     break;
95   case 'w':
96   case 'W':
97     profile = TizenProfile::Wearable;
98     break;
99   case 't':
100   case 'T':
101     profile = TizenProfile::Tv;
102     break;
103   case 'i':
104   case 'I':
105     profile = TizenProfile::Ivi;
106     break;
107   default:
108     profile = TizenProfile::Common;
109     break;
110   }
111   free(profile_name);
112
113   return profile;
114 }
115
116 constexpr const char PATH_LOCALE[] = "locale";
117 constexpr int SQLITE_FLUSH_MAX = 1024 * 1024;
118 constexpr const char RESOURCED_FREEZER_PATH[] =
119     "/Org/Tizen/ResourceD/Freezer";
120 constexpr const char RESOURCED_FREEZER_INTERFACE[] =
121     "org.tizen.resourced.freezer";
122 constexpr const char RESOURCED_FREEZER_SIGNAL[] =
123     "FreezerState";
124
125 struct Rotation {
126   int conn;
127   int lock;
128   int ref;
129   tizen_cpp::AppCoreBase::RotationState rm;
130   int charger_status;
131   bool initialized;
132 };
133
134 Rotation __rotation;
135 GDBusConnection* __bus;
136 guint __suspend_dbus_handler_initialized;
137 AppCoreBase::DisplayState __display_state = AppCoreBase::DISPLAY_STATE_UNKNOWN;
138
139 }  // namespace
140
141 class AppCoreBase::EventBase::Impl {
142  private:
143   friend class AppCoreBase;
144   Type type_ = IEvent::Type::START;
145   std::string str_val_;
146   int val_ = -1;
147 };
148
149 class AppCoreBase::Impl {
150  public:
151   explicit Impl(AppCoreBase* parent) : parent_(parent) {
152     if (TizenProfileGet() & TizenProfile::Wearable)
153       feature_ |= FEATURE_CHARGER_STATUS;
154     if (!(TizenProfileGet() & TizenProfile::Tv))
155       feature_ |= FEATURE_BACKGROUND_MANAGEMENT;
156   }
157
158  private:
159   void UnregisterRotationChangedEvent();
160   void RegisterRotationChangedEvent();
161   std::string GetAppName(const char* appid);
162   std::string GetLocaleResourceDir();
163   void UpdateLang();
164   void UpdateRegion();
165   std::list<std::string> SplitLanguage(const std::string& lang);
166   std::string GetLanguage(std::string lang);
167   void AppendDefaultLangs(std::vector<std::string>& lang_set);
168   std::string GetStringBefore(const char* str, const char* delim);
169   std::map<std::string, std::set<std::string>> GetLangTable();
170   void AppendLangs(const std::string& lang, std::vector<std::string>& lang_set,
171       std::map<std::string, std::set<std::string>>& table);
172   void ChangeLang();
173   void OnFreezerSignal();
174
175   template <class T>
176   void InvokeCallback(T event, IEvent::Type type) {
177     for (auto& i : events_) {
178       if (i->GetType() != type)
179         continue;
180
181       if (i->GetVal(event) != event ||
182           type == IEvent::Type::START ||
183           type == IEvent::Type::UPDATE_REQUESTED) {
184         i->SetVal(event);
185         i->OnEvent(event);
186       }
187     }
188   }
189
190   static void InitSuspendDbusHandler(gpointer data);
191   static gboolean InitSuspendCb(gpointer data);
192   static gboolean InvokeLangChangeCb(gpointer data);
193   static void ReceiveSuspendSignalCb(GDBusConnection*, const gchar*,
194       const gchar*, const gchar*, const gchar*, GVariant*, gpointer);
195   static void OnLowBatteryCb(keynode_t* key, void* data);
196   static void OnTimeZoneChangedCb(keynode_t* key, void* data);
197   static void LockCb(keynode_t* node, void* user_data);
198   static void AutoRotationChangedCb(sensor_t sensor, unsigned int event_type,
199       sensor_data_t* data, void* user_data);
200   static void ChargerStatusChangedCb(keynode_t* keynode, void* user_data);
201   static void LanguageChangeCb(keynode_t* key, void* data);
202   static void RegionChangeCb(keynode_t* key, void* data);
203   static void LowMemoryCb(keynode_t* key, void* data);
204   void InitRotation();
205   void FiniRotation();
206   RotationState GetRm(sensor_data_t data);
207   void VerifyLanguage();
208   void SetDefaultEvents();
209   void UnsetDefaultEvents();
210   void PluginInit(int argc, char** argv);
211   void PluginFini();
212
213  private:
214   friend class AppCoreBase;
215   AppCoreBase* parent_ = nullptr;
216
217   int argc_ = 0;
218   char** argv_ = nullptr;
219   bool suspended_state_ = false;
220   bool allowed_bg_ = false;
221   bool dirty_ = false;
222   unsigned int tid_ = 0;
223   std::list<std::shared_ptr<EventBase>> events_;
224   std::string locale_dir_;
225   guint sid_ = 0;
226   int feature_ = 0;
227   IAppCore* core_delegator_ = nullptr;
228   IMainLoop* loop_delegator_ = nullptr;
229   guint signal_handler_source_ = 0;
230   std::unique_ptr<AppCorePlugin> plugin_;
231 };
232
233 AppCoreBase::EventBase::EventBase(Type type)
234     : impl_(std::make_unique<EventBase::Impl>()) {
235   impl_->type_ = type;
236 }
237
238 AppCoreBase::EventBase::~EventBase() = default;
239
240 IAppCore::IEvent::Type AppCoreBase::EventBase::GetType() const {
241   return impl_->type_;
242 }
243
244 std::string AppCoreBase::EventBase::GetVal(std::string cur) const {
245   return impl_->str_val_;
246 }
247
248 int AppCoreBase::EventBase::GetVal(int cur) const {
249   return impl_->val_;
250 }
251
252 void AppCoreBase::EventBase::SetVal(std::string val) {
253   impl_->str_val_ = std::move(val);
254 }
255
256 void AppCoreBase::EventBase::SetVal(int val) {
257   impl_->val_ = std::move(val);
258 }
259
260 AppCoreBase::AppCoreBase()
261     : impl_(std::make_unique<AppCoreBase::Impl>(this)) {
262   if (context_ != nullptr) {
263     _E("Context is already initialized");
264   }
265
266   context_ = this;
267 }
268
269 AppCoreBase::~AppCoreBase() {
270   _I("");
271   context_ = nullptr;
272 }
273
274 AppCoreBase* AppCoreBase::GetContext() {
275   if (context_ == nullptr) {
276     _E("Context is not initialized.");
277     return nullptr;
278   }
279
280   return context_;
281 }
282
283 void AppCoreBase::Impl::ChangeLang() {
284   const char* lang = getenv("LANG");
285   if (lang == nullptr)
286     return;
287
288   InvokeCallback(lang, IEvent::Type::LANG_CHANGE);
289 }
290
291 void AppCoreBase::RaiseEvent(int event, IEvent::Type type) {
292   impl_->InvokeCallback(event, type);
293 }
294
295 void AppCoreBase::RaiseEvent(const std::string& event, IEvent::Type type) {
296   impl_->InvokeCallback(event, type);
297 }
298
299 void AppCoreBase::Impl::OnFreezerSignal() {
300   if (!allowed_bg_ && suspended_state_) {
301     parent_->RemoveSuspendTimer();
302     InvokeCallback(SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND,
303         IEvent::Type::SUSPENDED_STATE_CHANGE);
304     suspended_state_ = false;
305     parent_->AddSuspendTimer();
306   }
307 }
308
309 AppCoreBase::RotationState AppCoreBase::Impl::GetRm(sensor_data_t data) {
310   if (data.value_count <= 0) {
311     _E("Failed to get sensor data");
312     return ROTATION_UNKNOWN;
313   }
314
315   int event = data.values[0];
316   switch (event) {
317   case AUTO_ROTATION_DEGREE_0:
318     return ROTATION_PORTRAIT_NORMAL;
319   case AUTO_ROTATION_DEGREE_90:
320     return ROTATION_LANDSCAPE_NORMAL;
321   case AUTO_ROTATION_DEGREE_180:
322     return ROTATION_PORTRAIT_REVERSE;
323   case AUTO_ROTATION_DEGREE_270:
324     return ROTATION_LANDSCAPE_REVERSE;
325   default:
326     return ROTATION_UNKNOWN;
327   }
328 }
329
330 void AppCoreBase::Impl::InitSuspendDbusHandler(gpointer data) {
331   if (__suspend_dbus_handler_initialized)
332     return;
333
334   if (__bus == nullptr) {
335     GError* err = nullptr;
336     __bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &err);
337     if (__bus == nullptr) {
338       _E("Failed to connect to the D-BUS daemon: %s", err ? err->message : "");
339       if (err)
340         g_error_free(err);
341
342       return;
343     }
344   }
345
346   __suspend_dbus_handler_initialized = g_dbus_connection_signal_subscribe(
347     __bus, nullptr, RESOURCED_FREEZER_INTERFACE, RESOURCED_FREEZER_SIGNAL,
348     RESOURCED_FREEZER_PATH, nullptr, G_DBUS_SIGNAL_FLAGS_NONE,
349     ReceiveSuspendSignalCb, data, nullptr);
350
351   if (__suspend_dbus_handler_initialized == 0) {
352     _E("g_dbus_connection_signal_subscribe() is failed.");
353     return;
354   }
355
356   _D("[__SUSPEND__] suspend signal initialized");
357 }
358
359 gboolean AppCoreBase::Impl::InitSuspendCb(gpointer data) {
360   InitSuspendDbusHandler(data);
361   return G_SOURCE_REMOVE;
362 }
363
364 gboolean AppCoreBase::Impl::InvokeLangChangeCb(gpointer data) {
365   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
366   base->impl_->sid_ = 0;
367   base->impl_->ChangeLang();
368   return G_SOURCE_REMOVE;
369 }
370
371 void AppCoreBase::Impl::ReceiveSuspendSignalCb(GDBusConnection*, const gchar*,
372     const gchar*, const gchar*, const gchar* signal_name, GVariant* parameters,
373     gpointer user_data) {
374   if (g_strcmp0(signal_name, RESOURCED_FREEZER_SIGNAL) == 0) {
375     gint pid = -1;
376     gint status = 0;
377     g_variant_get(parameters, "(ii)", &status, &pid);
378     if (pid == getpid() && status == 0) {
379       AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
380       base->impl_->OnFreezerSignal();
381     }
382   }
383 }
384
385 void AppCoreBase::Impl::LanguageChangeCb(keynode_t* key, void* user_data) {
386   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
387   if (base->impl_->sid_) {
388     GLib::SourceRemove(base->impl_->sid_);
389     base->impl_->sid_ = 0;
390   }
391
392   char* val = vconf_keynode_get_str(key);
393   if (val == nullptr)
394     return;
395
396   base->impl_->UpdateLang();
397   base->impl_->InvokeCallback(val, IEvent::Type::LANG_CHANGE);
398 }
399
400 void AppCoreBase::Impl::RegionChangeCb(keynode_t* key, void* user_data) {
401   const char* name = vconf_keynode_get_name(key);
402   if (name == nullptr)
403     return;
404
405   if (strcmp(name, VCONFKEY_REGIONFORMAT) &&
406       strcmp(name, VCONFKEY_REGIONFORMAT_TIME1224))
407     return;
408
409   char* val = vconf_get_str(VCONFKEY_REGIONFORMAT);
410   if (val == nullptr)
411     return;
412   std::unique_ptr<char, decltype(std::free)*> region_auto(val, std::free);
413
414   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
415   base->impl_->UpdateRegion();
416   base->impl_->InvokeCallback(val, IEvent::Type::REGION_CHANGE);
417 }
418
419 void AppCoreBase::Impl::LowMemoryCb(keynode_t* key, void* user_data) {
420   int val = vconf_keynode_get_int(key);
421   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
422   base->impl_->InvokeCallback(val, IEvent::Type::LOW_MEMORY);
423   if (val >= VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING)
424     malloc_trim(0);
425 }
426
427 void AppCoreBase::Impl::ChargerStatusChangedCb(keynode_t* keynode,
428     void* user_data) {
429   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
430   if (base->impl_->feature_ & FEATURE_CHARGER_STATUS) {
431     __rotation.charger_status = vconf_keynode_get_int(keynode);
432     if (__rotation.ref) {
433       if (__rotation.charger_status) {
434         base->impl_->InitRotation();
435       } else {
436         base->impl_->FiniRotation();
437       }
438     }
439
440     _D("charger status(%d)", __rotation.charger_status);
441   }
442 }
443
444 void AppCoreBase::Impl::LockCb(keynode_t* node, void* user_data) {
445   AppCoreBase::RotationState rm;
446   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
447
448   __rotation.lock = !vconf_keynode_get_bool(node);
449   if (__rotation.lock) {
450     _D("Rotation locked");
451     rm = ROTATION_PORTRAIT_NORMAL;
452   } else {
453     _D("Rotation unlocked");
454     sensor_data_t data;
455     bool r = sensord_get_data(__rotation.conn, AUTO_ROTATION_SENSOR, &data);
456     if (!r) {
457       _E("Failed to get sensor data");
458       return;
459     }
460
461     rm = base->impl_->GetRm(data);
462     if (rm == ROTATION_UNKNOWN) {
463       _E("Unknown mode");
464       return;
465     }
466   }
467
468   if (__rotation.rm == rm)
469     return;
470
471   _D("Rotation: %d -> %d", __rotation.rm, rm);
472   __rotation.rm = rm;
473   base->impl_->InvokeCallback(__rotation.rm,
474       IEvent::Type::DEVICE_ORIENTATION_CHANGED);
475 }
476
477 void AppCoreBase::Impl::AutoRotationChangedCb(sensor_t sensor,
478     unsigned int event_type, sensor_data_t* data, void* user_data) {
479   if (data == nullptr)
480     return;
481
482   if (__rotation.lock)
483     return;
484
485   if (event_type != AUTO_ROTATION_CHANGE_STATE_EVENT)
486     return;
487
488   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
489   AppCoreBase::RotationState rm = base->impl_->GetRm(*data);
490   if (rm == ROTATION_UNKNOWN) {
491     _E("Unknown mode");
492     return;
493   }
494
495   _D("Rotation: %d -> %d", __rotation.rm, rm);
496   __rotation.rm = rm;
497   base->impl_->InvokeCallback(__rotation.rm,
498       IEvent::Type::DEVICE_ORIENTATION_CHANGED);
499 }
500
501 void AppCoreBase::Impl::InitRotation() {
502   if (__rotation.initialized)
503     return;
504
505   sensor_t sensor = sensord_get_sensor(AUTO_ROTATION_SENSOR);
506   __rotation.conn = sensord_connect(sensor);
507   if (__rotation.conn < 0) {
508     _E("Failed to connect sensord");
509     return;
510   }
511
512   bool r = sensord_register_event(__rotation.conn,
513       AUTO_ROTATION_CHANGE_STATE_EVENT, SENSOR_INTERVAL_NORMAL, 0,
514       AutoRotationChangedCb, parent_);
515   if (!r) {
516     _E("Failed to register auto rotation change event");
517     sensord_disconnect(__rotation.conn);
518     return;
519   }
520
521   r = sensord_start(__rotation.conn, 0);
522   if (!r) {
523     _E("Failed to start sensord");
524     sensord_unregister_event(__rotation.conn, AUTO_ROTATION_CHANGE_STATE_EVENT);
525     sensord_disconnect(__rotation.conn);
526     return;
527   }
528
529   int lock = 0;
530   vconf_get_bool(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, &lock);
531   vconf_notify_key_changed(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, LockCb,
532       parent_);
533
534   __rotation.lock = !lock;
535   __rotation.initialized = true;
536 }
537
538 void AppCoreBase::Impl::FiniRotation() {
539   if (!__rotation.initialized)
540     return;
541
542   vconf_ignore_key_changed(VCONFKEY_SETAPPL_AUTO_ROTATE_SCREEN_BOOL, LockCb);
543   sensord_unregister_event(__rotation.conn, AUTO_ROTATION_CHANGE_STATE_EVENT);
544   sensord_stop(__rotation.conn);
545   sensord_disconnect(__rotation.conn);
546
547   __rotation.lock = 0;
548   __rotation.initialized = false;
549 }
550
551 void AppCoreBase::Impl::VerifyLanguage() {
552   const char* env_lang = getenv("LANG");
553   if (env_lang == nullptr)
554     return;
555
556   char* lang = vconf_get_str(VCONFKEY_LANGSET);
557   if (lang == nullptr)
558     return;
559
560   std::unique_ptr<char, decltype(std::free)*> lang_auto(lang, std::free);
561
562   if (strcmp(env_lang, lang) != 0) {
563     _I("LANG(%s), LANGSET(%s)", env_lang, lang);
564     sid_ = GLib::IdleAdd(InvokeLangChangeCb, parent_);
565   }
566 }
567
568 void AppCoreBase::Impl::SetDefaultEvents() {
569   vconf_notify_key_changed(VCONFKEY_LANGSET, LanguageChangeCb, parent_);
570   int r = vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, RegionChangeCb,
571       parent_);
572   if (r == 0) {
573     vconf_notify_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, RegionChangeCb,
574         parent_);
575   }
576
577   vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, LowMemoryCb, parent_);
578 }
579
580 void AppCoreBase::Impl::UnsetDefaultEvents() {
581   vconf_ignore_key_changed(VCONFKEY_LANGSET, LanguageChangeCb);
582   int r = vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, RegionChangeCb);
583   if (r == 0)
584     vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT_TIME1224, RegionChangeCb);
585
586   vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, LowMemoryCb);
587 }
588
589 int AppCoreBase::OnReceive(aul_type type, tizen_base::Bundle b) {
590   switch (type) {
591   case AUL_START:
592     _D("[APP %d]     AUL event: AUL_START", getpid());
593     if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT) {
594       std::string bg = b.GetString(AUL_K_ALLOWED_BG);
595       if (bg == "ALLOWED_BG") {
596         _D("[__SUSPEND__] allowed background");
597         impl_->allowed_bg_ = true;
598         RemoveSuspendTimer();
599       }
600     }
601
602     traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:RESET");
603     if (impl_->core_delegator_)
604       impl_->core_delegator_->OnControl(std::move(b));
605     else
606       OnControl(std::move(b));
607     traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
608     break;
609   case AUL_RESUME:
610     _D("[APP %d]     AUL event: AUL_RESUME", getpid());
611     if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT) {
612       std::string bg = b.GetString(AUL_K_ALLOWED_BG);
613       if (bg == "ALLOWED_BG") {
614         _D("[__SUSPEND__] allowed background");
615         impl_->allowed_bg_ = true;
616         RemoveSuspendTimer();
617       }
618     }
619     break;
620   case AUL_TERMINATE:
621     _D("[APP %d]     AUL event: AUL_TERMINATE", getpid());
622     aul_status_update(STATUS_DYING);
623     if (!impl_->allowed_bg_)
624       RemoveSuspendTimer();
625
626     Exit();
627     break;
628   case AUL_TERMINATE_INST:
629   case AUL_TERMINATE_BG_INST:
630   case AUL_TERMINATE_BGAPP:
631     _D("[APP %d]     AUL event: %d", getpid(), type);
632     if (!impl_->allowed_bg_)
633       RemoveSuspendTimer();
634     break;
635   case AUL_WAKE:
636     _D("[APP %d]     AUL event: AUL_WAKE", getpid());
637     if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT) {
638       if (!impl_->allowed_bg_ && impl_->suspended_state_) {
639         RemoveSuspendTimer();
640         int suspend = SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
641         impl_->InvokeCallback(suspend, IEvent::Type::SUSPENDED_STATE_CHANGE);
642         impl_->suspended_state_ = false;
643       }
644     }
645     break;
646   case AUL_SUSPEND:
647     _D("[APP %d]     AUL event: AUL_SUSPEND", getpid());
648     if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT) {
649       if (!impl_->allowed_bg_ && !impl_->suspended_state_) {
650         RemoveSuspendTimer();
651         FlushMemory();
652       }
653     }
654     break;
655   case AUL_UPDATE_REQUESTED:
656     _D("[APP %d]     AUL event: AUL_UPDATE_REQUESTED", getpid());
657     impl_->InvokeCallback(0, IEvent::Type::UPDATE_REQUESTED);
658     break;
659   default:
660     _D("[APP %d]     AUL event: %d", getpid(), type);
661     /* do nothing */
662     break;
663   }
664
665   return 0;
666 }
667
668 int AppCoreBase::OnCreate() {
669   int ret = aul_launch_init([](aul_type type, bundle* b, void* data) -> int {
670         AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
671         if (base->impl_->core_delegator_) {
672           return base->impl_->core_delegator_->OnReceive(type,
673               b ? tizen_base::Bundle(b, false, false) : tizen_base::Bundle());
674         }
675         return base->OnReceive(type,
676             b ? tizen_base::Bundle(b, false, false) : tizen_base::Bundle());
677       }, this);
678   if (ret < 0 && ret != AUL_R_ECANCELED) {
679     _E("aul_launch_init() is failed. error(%d)", ret);
680     return -1;
681   }
682
683   ret = aul_launch_argv_handler(impl_->argc_, impl_->argv_);
684   if (ret < 0) {
685     _E("aul_launch_argv_handler() is failed. error(%d)", ret);
686     return -1;
687   }
688
689   return 0;
690 }
691
692 int AppCoreBase::OnControl(tizen_base::Bundle b) {
693   return 0;
694 }
695
696 int AppCoreBase::OnTerminate() {
697   aul_finalize();
698   return 0;
699 }
700
701 void AppCoreBase::SetCoreDelegator(IAppCore* delegator) {
702   impl_->core_delegator_ = delegator;
703 }
704
705 void AppCoreBase::SetLoopDelegator(IMainLoop* delegator) {
706   impl_->loop_delegator_ = delegator;
707 }
708
709 std::string AppCoreBase::Impl::GetAppName(const char* appid) {
710   if (appid == nullptr)
711     return "";
712
713   /* com.vendor.name -> name */
714   const char* name_token = strrchr(appid, '.');
715   if (name_token == nullptr)
716     return appid;
717
718   name_token++;
719   return name_token;
720 }
721
722 std::string AppCoreBase::Impl::GetLocaleResourceDir() {
723   const char* res_path = aul_get_app_resource_path();
724   if (res_path == nullptr) {
725     _E("Failed to get resource path");
726     return "";
727   }
728
729   return std::string(res_path) + PATH_LOCALE;
730 }
731
732 int AppCoreBase::OnSetI18n() {
733   char appid[PATH_MAX];
734   int ret = aul_app_get_appid_bypid(getpid(), appid, PATH_MAX);
735   if (ret < 0) {
736     _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
737     return -1;
738   }
739
740   std::string name = impl_->GetAppName(appid);
741   if (name.empty())
742     return -1;
743
744   std::string locale_dir = impl_->GetLocaleResourceDir();
745   if (locale_dir.empty())
746     return -1;
747
748   return SetI18n(move(name), move(locale_dir));
749 }
750
751 void AppCoreBase::Impl::OnLowBatteryCb(keynode_t* key, void* data) {
752   int val = vconf_keynode_get_int(key);
753   if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW) {
754     AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
755     base->impl_->InvokeCallback(val, IEvent::Type::LOW_BATTERY);
756   }
757 }
758
759 void AppCoreBase::Impl::OnTimeZoneChangedCb(keynode_t* key, void* data) {
760   char* time_zone_id = vconf_keynode_get_str(key);
761   if (time_zone_id == nullptr) {
762     return;
763   }
764
765   char* time_zone = vconf_get_str(VCONFKEY_SETAPPL_TIMEZONE_INT);
766   if (time_zone != nullptr) {
767     AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
768     base->impl_->InvokeCallback(std::string(time_zone) + "|" + time_zone_id,
769         IEvent::Type::TIME_ZONE_CHANGED);
770     free(time_zone);
771   }
772 }
773
774 void AppCoreBase::Impl::UnregisterRotationChangedEvent() {
775   if (!__rotation.ref)
776     return;
777
778   __rotation.ref--;
779   if (__rotation.ref == 0) {
780     FiniRotation();
781     if (feature_ & FEATURE_CHARGER_STATUS) {
782       vconf_ignore_key_changed(VCONFKEY_SYSMAN_CHARGER_STATUS,
783           ChargerStatusChangedCb);
784     }
785   }
786 }
787
788 void AppCoreBase::Impl::RegisterRotationChangedEvent() {
789   if (__rotation.ref == 0) {
790     if (feature_ & FEATURE_CHARGER_STATUS) {
791       vconf_get_int(VCONFKEY_SYSMAN_CHARGER_STATUS, &__rotation.charger_status);
792       vconf_notify_key_changed(VCONFKEY_SYSMAN_CHARGER_STATUS,
793           ChargerStatusChangedCb, parent_);
794       if (__rotation.charger_status)
795         InitRotation();
796     } else {
797       InitRotation();
798     }
799   }
800
801   __rotation.ref++;
802 }
803
804 int AppCoreBase::OnSetEvent(IEvent::Type event) {
805   switch (event) {
806   case IEvent::Type::LOW_BATTERY:
807     vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW,
808         impl_->OnLowBatteryCb, this);
809     break;
810   case IEvent::Type::DEVICE_ORIENTATION_CHANGED:
811     impl_->RegisterRotationChangedEvent();
812     break;
813   case IEvent::Type::SUSPENDED_STATE_CHANGE:
814     break;
815   case IEvent::Type::TIME_ZONE_CHANGED:
816     vconf_notify_key_changed(VCONFKEY_SETAPPL_TIMEZONE_ID,
817         impl_->OnTimeZoneChangedCb, this);
818     break;
819   default:
820     break;
821   }
822
823   return 0;
824 }
825
826 int AppCoreBase::OnUnsetEvent(IEvent::Type event) {
827   switch (event) {
828   case IEvent::Type::LOW_BATTERY:
829     vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW,
830         impl_->OnLowBatteryCb);
831     break;
832   case IEvent::Type::DEVICE_ORIENTATION_CHANGED:
833     impl_->UnregisterRotationChangedEvent();
834     break;
835   case IEvent::Type::SUSPENDED_STATE_CHANGE:
836     break;
837   default:
838     break;
839   }
840
841   return 0;
842 }
843
844 int AppCoreBase::OnTrimMemory() {
845   int (*sqlite3_free_heap_memory)(int);
846
847   sqlite3_free_heap_memory = reinterpret_cast<
848       decltype(sqlite3_free_heap_memory)>(
849           dlsym(RTLD_DEFAULT, "sqlite3_release_memory"));
850   if (sqlite3_free_heap_memory)
851     sqlite3_free_heap_memory(SQLITE_FLUSH_MAX);
852
853   malloc_trim(0);
854   return 0;
855 }
856
857 void AppCoreBase::AddEvent(std::shared_ptr<EventBase> event) {
858   bool found = false;
859   for (auto& ev : impl_->events_) {
860     if (ev->GetType() == event->GetType()) {
861       found = true;
862       break;
863     }
864   }
865
866   if (!found) {
867     if (impl_->core_delegator_)
868       impl_->core_delegator_->OnSetEvent(event->GetType());
869     else
870       OnSetEvent(event->GetType());
871   }
872
873   impl_->events_.push_back(move(event));
874 }
875
876 bool AppCoreBase::RemoveEvent(std::shared_ptr<EventBase> event) {
877   bool found = false;
878   impl_->events_.remove_if([&](const std::shared_ptr<EventBase>& ptr)->bool {
879     if (event.get() == ptr.get()) {
880       found = true;
881       return true;
882     }
883     return false;
884   });
885
886   return found;
887 }
888
889 void AppCoreBase::FlushMemory() {
890   if (impl_->core_delegator_)
891     impl_->core_delegator_->OnTrimMemory();
892   else
893     OnTrimMemory();
894
895   impl_->tid_ = 0;
896   if (!impl_->allowed_bg_ && !impl_->suspended_state_) {
897     _D("[__SUSPEND__] flush case");
898     int suspend = SUSPENDED_STATE_WILL_ENTER_SUSPEND;
899     impl_->InvokeCallback(suspend, IEvent::Type::SUSPENDED_STATE_CHANGE);
900     impl_->suspended_state_ = true;
901   }
902 }
903
904 AppCoreBase::RotationState AppCoreBase::GetRotationState() {
905   if (!__rotation.ref)
906     throw std::runtime_error("invalid rotation state");
907
908   return __rotation.rm;
909 }
910
911 bool AppCoreBase::IsBgAllowed() {
912   return impl_->allowed_bg_;
913 }
914
915 bool AppCoreBase::IsSuspended() {
916   return impl_->suspended_state_;
917 }
918
919 void AppCoreBase::ToggleSuspendedState() {
920   impl_->suspended_state_ = !impl_->suspended_state_;
921 }
922
923 std::list<std::string> AppCoreBase::Impl::SplitLanguage(
924     const std::string& lang) {
925   std::istringstream ss(lang);
926   std::list<std::string> li;
927   std::string token;
928
929   while (std::getline(ss, token, ':'))
930     li.push_back(token);
931
932   return li;
933 }
934
935 void AppCoreBase::Impl::AppendDefaultLangs(std::vector<std::string>& langs) {
936   langs.push_back("en_US");
937   langs.push_back("en_GB");
938   langs.push_back("en");
939 }
940
941 std::string AppCoreBase::Impl::GetStringBefore(const char* cstr,
942     const char* delim) {
943   if (cstr == nullptr || delim == nullptr)
944     return "";
945
946   auto str = std::string(cstr);
947   auto idx = str.find(delim);
948   return str.substr(0, idx);
949 }
950
951 std::map<std::string, std::set<std::string>> AppCoreBase::Impl::GetLangTable() {
952   if (locale_dir_.empty())
953     return {};
954
955   DIR* dp = opendir(locale_dir_.c_str());
956   if (dp == nullptr)
957     return {};
958
959   std::map<std::string, std::set<std::string>> table;
960   struct dirent *dentry;
961   while ((dentry = readdir(dp)) != nullptr) {
962     if (!strcmp(dentry->d_name, ".") ||
963         !strcmp(dentry->d_name, ".."))
964       continue;
965
966     std::string buf = locale_dir_ + "/" + dentry->d_name;
967     struct stat stat_buf;
968     int ret = stat(buf.c_str(), &stat_buf);
969     if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
970       continue;
971
972     std::string parent_lang = GetStringBefore(dentry->d_name, "_");
973     if (parent_lang.empty()) {
974       _E("Out of memory");
975       break;
976     }
977
978     table[parent_lang].insert(dentry->d_name);
979   }
980
981   closedir(dp);
982   return table;
983 }
984
985 void AppCoreBase::Impl::AppendLangs(const std::string& lang,
986     std::vector<std::string>& langs,
987     std::map<std::string, std::set<std::string>>& table) {
988   if (lang.empty())
989     return;
990
991   langs.push_back(lang);
992   std::string extract_lang = GetStringBefore(lang.c_str(), ".");
993   if (extract_lang.empty())
994     return;
995
996   if (std::find(langs.begin(), langs.end(), extract_lang) != langs.end())
997     return;
998
999   std::string parent_lang = GetStringBefore(extract_lang.c_str(), "_");
1000   if (parent_lang.empty())
1001     return;
1002
1003   if (table.find(parent_lang) == table.end())
1004     return;
1005
1006   auto it = table[parent_lang].find(extract_lang);
1007   if (it != table[parent_lang].end()) {
1008     std::string value = *it;
1009     if (std::find(langs.begin(), langs.end(), value) == langs.end())
1010       langs.push_back(std::move(value));
1011
1012     table[parent_lang].erase(it);
1013     return;
1014   }
1015
1016   it = table[parent_lang].find(parent_lang);
1017   if (it != table[parent_lang].end()) {
1018     std::string value = *it;
1019     if (std::find(langs.begin(), langs.end(), value) == langs.end())
1020       langs.push_back(std::move(value));
1021
1022     table[parent_lang].erase(parent_lang);
1023     return;
1024   }
1025
1026   if (!table[parent_lang].empty()) {
1027     auto i = table[parent_lang].begin();
1028     std::string value = *i;
1029     if (std::find(langs.begin(), langs.end(), value) == langs.end())
1030       langs.push_back(std::move(value));
1031
1032     table[parent_lang].erase(i);
1033   }
1034 }
1035
1036 std::string AppCoreBase::Impl::GetLanguage(std::string lang) {
1037   std::list<std::string> l = SplitLanguage(lang);
1038   if (l.empty())
1039     return "";
1040
1041   auto table = GetLangTable();
1042   if (table.empty())
1043     return "";
1044
1045   std::vector<std::string> langs;
1046   for (auto& i : l)
1047     AppendLangs(i, langs, table);
1048
1049   AppendDefaultLangs(langs);
1050   std::string ret;
1051   for (auto& i : langs) {
1052     if (ret.empty())
1053       ret = i;
1054     else
1055       ret += ":" + i;
1056   }
1057
1058   return ret;
1059 }
1060
1061 void AppCoreBase::Impl::UpdateLang() {
1062   char* lang = vconf_get_str(VCONFKEY_LANGSET);
1063   if (lang == nullptr)
1064     return;
1065
1066   std::unique_ptr<char, decltype(std::free)*> lang_auto(lang, std::free);
1067   std::string language = GetLanguage(lang);
1068   if (!language.empty()) {
1069     _D("*****language(%s)", language.c_str());
1070     setenv("LANGUAGE", language.c_str(), 1);
1071   } else {
1072     setenv("LANGUAGE", lang, 1);
1073   }
1074   setenv("LANG", lang, 1);
1075   setenv("LC_MESSAGES", lang, 1);
1076   setenv("LC_ALL", lang, 1);
1077   char* r = setlocale(LC_ALL, "");
1078   if (r == nullptr) {
1079     r = setlocale(LC_ALL, "en_US.UTF-8");
1080     if (r != nullptr) {
1081       _D("*****appcore setlocale=%s\n", r);
1082     } else {
1083       _D("*****appcore setlocale=\"C\"");
1084       setenv("LC_ALL", "C", 1);
1085       r = setlocale(LC_ALL, "");
1086       if (r == nullptr)
1087         _E("failed to setlocale");
1088     }
1089   }
1090 }
1091
1092 void AppCoreBase::Impl::UpdateRegion() {
1093   char* region = vconf_get_str(VCONFKEY_REGIONFORMAT);
1094   if (region == nullptr)
1095     return;
1096
1097   std::unique_ptr<char, decltype(std::free)*> region_auto(region, std::free);
1098   setenv("LC_CTYPE", region, 1);
1099   setenv("LC_NUMERIC", region, 1);
1100   setenv("LC_TIME", region, 1);
1101   setenv("LC_COLLATE", region, 1);
1102   setenv("LC_MONETARY", region, 1);
1103   setenv("LC_PAPER", region, 1);
1104   setenv("LC_NAME", region, 1);
1105   setenv("LC_ADDRESS", region, 1);
1106   setenv("LC_TELEPHONE", region, 1);
1107   setenv("LC_MEASUREMENT", region, 1);
1108   setenv("LC_IDENTIFICATION", region, 1);
1109   char* r = setlocale(LC_ALL, "");
1110   if (r != nullptr) {
1111     _D("*****appcore setlocale=%s\n", r);
1112   } else {
1113     _D("*****appcore setlocale=\"C\"");
1114     setenv("LC_ALL", "C", 1);
1115     r = setlocale(LC_ALL, "");
1116     if (r == nullptr)
1117       _E("failed to setlocale");
1118   }
1119 }
1120
1121 int AppCoreBase::SetI18n(std::string domain_name, std::string dir_name) {
1122   if (domain_name.empty()) {
1123     errno = EINVAL;
1124     return -1;
1125   }
1126
1127   if (!dir_name.empty())
1128     impl_->locale_dir_ = dir_name;
1129
1130   impl_->UpdateLang();
1131   impl_->UpdateRegion();
1132
1133   char* r = setlocale(LC_ALL, "");
1134   /* if locale is not set properly, try to set "en_US" again */
1135   if (r == nullptr) {
1136     r = setlocale(LC_ALL, "en_US.UTF-8");
1137     if (r == nullptr) {
1138       _E("appcore: setlocale() error");
1139       _D("*****appcore setlocale=\"C\"");
1140       setenv("LC_ALL", "C", 1);
1141       r = setlocale(LC_ALL, "");
1142       if (r == nullptr)
1143         _E("failed to setlocale");
1144     }
1145   }
1146
1147   if (r != nullptr)
1148     _D("*****appcore setlocale=%s\n", r);
1149
1150   r = bindtextdomain(domain_name.c_str(), dir_name.c_str());
1151   if (r == nullptr)
1152     _E("appcore: bindtextdomain() error");
1153
1154   r = textdomain(domain_name.c_str());
1155   if (r == nullptr)
1156     _E("appcore: textdomain() error");
1157
1158   return 0;
1159 }
1160
1161 void AppCoreBase::Exit() {
1162   aul_status_update(STATUS_DYING);
1163   if (impl_->loop_delegator_)
1164     impl_->loop_delegator_->OnLoopExit();
1165   else
1166     OnLoopExit();
1167 }
1168
1169 void AppCoreBase::AddSuspendTimer() {
1170   impl_->tid_ = GLib::TimeoutAdd(5000, [](gpointer data) -> gboolean {
1171         AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
1172         base->FlushMemory();
1173         return FALSE;
1174       }, this);
1175 }
1176
1177 void AppCoreBase::RemoveSuspendTimer() {
1178   if (impl_->tid_ > 0) {
1179     GLib::SourceRemove(impl_->tid_);
1180     impl_->tid_ = 0;
1181   }
1182 }
1183
1184 void AppCoreBase::SetDisplayState(DisplayState state) {
1185   __display_state = state;
1186 }
1187
1188 AppCoreBase::DisplayState AppCoreBase::GetDisplayState() {
1189   return __display_state;
1190 }
1191
1192 int AppCoreBase::EnableWatchdog() {
1193   _I("[__APPCORE_WATCHDOG__] enable");
1194   return aul_watchdog_enable();
1195 }
1196
1197 int AppCoreBase::DisableWatchdog() {
1198   _I("[__APPCORE_WATCHDOG__] disable");
1199   return aul_watchdog_disable();
1200 }
1201
1202 int AppCoreBase::KickWatchdog() {
1203   _I("[__APPCORE_WATCHDOG__] kick");
1204   return aul_watchdog_kick();
1205 }
1206
1207 void AppCoreBase::Run(int argc, char** argv) {
1208   Init(argc, argv);
1209   Fini();
1210 }
1211
1212 void AppCoreBase::Init(int argc, char** argv) {
1213   LogTracer tracer("AppCoreBase::Init()");
1214   impl_->tid_ = 0;
1215   impl_->suspended_state_ = false;
1216   impl_->allowed_bg_ = false;
1217   impl_->argc_ = argc;
1218   impl_->argv_ = argv;
1219   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:OPS_INIT");
1220   sigterm_handler.Restore();
1221   if (impl_->loop_delegator_)
1222     impl_->loop_delegator_->OnLoopInit(argc, argv);
1223   else
1224     OnLoopInit(argc, argv);
1225
1226   impl_->signal_handler_source_ = g_unix_signal_add(SIGTERM,
1227       [](gpointer data) -> gboolean {
1228         _W("sigterm handler");
1229         if (context_ != nullptr) {
1230           context_->Exit();
1231           context_->impl_->signal_handler_source_ = 0;
1232         }
1233
1234         return G_SOURCE_REMOVE;
1235       }, nullptr);
1236
1237   if (impl_->signal_handler_source_ == 0)
1238     _E("Failed to add sigterm handler.");
1239
1240   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1241
1242   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:PLUGIN_INIT");
1243   impl_->PluginInit(argc, argv);
1244   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1245
1246   if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT)
1247     GLib::IdleAdd(Impl::InitSuspendCb, this);
1248
1249   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:SET_SYSTEM_EVENT");
1250   if (!impl_->dirty_) {
1251     impl_->dirty_ = true;
1252
1253     for (auto& e : impl_->events_) {
1254       if (impl_->core_delegator_)
1255         impl_->core_delegator_->OnSetEvent(e->GetType());
1256       else
1257         OnSetEvent(e->GetType());
1258     }
1259   }
1260   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1261
1262   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:VERIFY_LANG");
1263   impl_->VerifyLanguage();
1264   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1265
1266   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:SET_DEFAULT_EVENTS");
1267   impl_->SetDefaultEvents();
1268   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1269
1270   if (impl_->core_delegator_)
1271     impl_->core_delegator_->OnSetI18n();
1272   else
1273     OnSetI18n();
1274
1275   aul_app_lifecycle_update_state(AUL_APP_LIFECYCLE_STATE_INITIALIZED);
1276
1277   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:CREATE");
1278   int create = 0;
1279   if (impl_->core_delegator_)
1280     create = impl_->core_delegator_->OnCreate();
1281   else
1282     create = OnCreate();
1283   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1284
1285   aul_app_lifecycle_update_state(AUL_APP_LIFECYCLE_STATE_CREATED);
1286   if (create < 0)
1287     return;
1288
1289   if (impl_->loop_delegator_)
1290     impl_->loop_delegator_->OnLoopRun();
1291   else
1292     OnLoopRun();
1293 }
1294
1295 void AppCoreBase::Fini() {
1296   LogTracer tracer("AppCoreBase::Fini()");
1297   if (impl_->signal_handler_source_) {
1298     g_source_remove(impl_->signal_handler_source_);
1299     impl_->signal_handler_source_ = 0;
1300   }
1301
1302   Dispose();
1303 }
1304
1305 void AppCoreBase::Dispose() {
1306   aul_status_update(STATUS_DYING);
1307   DisableWatchdog();
1308   aul_app_lifecycle_update_state(AUL_APP_LIFECYCLE_STATE_DESTROYED);
1309
1310   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:TERMINATE");
1311   if (impl_->core_delegator_)
1312     impl_->core_delegator_->OnTerminate();
1313   else
1314     OnTerminate();
1315   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
1316
1317   for (auto& e : impl_->events_) {
1318     if (impl_->core_delegator_)
1319       impl_->core_delegator_->OnUnsetEvent(e->GetType());
1320     else
1321       OnUnsetEvent(e->GetType());
1322   }
1323
1324   impl_->UnsetDefaultEvents();
1325   if (impl_->sid_) {
1326     GLib::SourceRemove(impl_->sid_);
1327     impl_->sid_ = 0;
1328   }
1329
1330   RemoveSuspendTimer();
1331   impl_->PluginFini();
1332   impl_->dirty_ = false;
1333   if (impl_->loop_delegator_)
1334     impl_->loop_delegator_->OnLoopFinish();
1335   else
1336     OnLoopFinish();
1337 }
1338
1339 void AppCoreBase::SetFeature(int feature) {
1340   impl_->feature_ = feature;
1341 }
1342
1343 int AppCoreBase::GetFeature() const {
1344   return impl_->feature_;
1345 }
1346
1347 void AppCoreBase::Impl::PluginInit(int argc, char** argv) {
1348   plugin_.reset(AppCorePlugin::Load());
1349   if (!plugin_)
1350     return;
1351
1352   plugin_->Init(argc, argv);
1353 }
1354
1355 void AppCoreBase::Impl::PluginFini() {
1356   if (!plugin_)
1357     return;
1358
1359   plugin_->Fini();
1360 }
1361
1362 }  // namespace tizen_cpp