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