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