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