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