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