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