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