Refactor appcore-agent 42/254242/14
authorjh9216.park <jh9216.park@samsung.com>
Thu, 25 Feb 2021 04:02:51 +0000 (23:02 -0500)
committerChanggyu Choi <changyu.choi@samsung.com>
Fri, 7 Jan 2022 07:22:54 +0000 (16:22 +0900)
- Use app-core for C++

Requires:
 - https://review.tizen.org/gerrit/#/c/platform/core/appfw/app-core/+/247812/
 - https://review.tizen.org/gerrit/#/c/platform/core/api/app-common/+/253882/

Change-Id: I5a408b66cc15a993a538c620125087fe0399eb3c
Signed-off-by: jh9216.park <jh9216.park@samsung.com>
16 files changed:
CMakeLists.txt
include/service_app_internal.h
packaging/appcore-agent.spec
src/job-handler.cc [new file with mode: 0644]
src/job-handler.hh [new file with mode: 0644]
src/job-manager.cc [new file with mode: 0644]
src/job-manager.hh [new file with mode: 0644]
src/job.cc [new file with mode: 0644]
src/job.hh [new file with mode: 0644]
src/log-private.hh [new file with mode: 0644]
src/service_app_main.c [deleted file]
src/service_app_main.cc [new file with mode: 0644]
unittests/CMakeLists.txt
unittests/mock/appcore_mock.cc
unittests/mock/appcore_mock.h
unittests/service_app_test.cc

index dbf7f9e..623f717 100644 (file)
@@ -9,6 +9,7 @@ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
 SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -Wall")
 SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror")
 SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS}")
 
 SET(CMAKE_SKIP_BUILD_RPATH TRUE)
 
@@ -18,7 +19,7 @@ SET(CMAKE_SKIP_BUILD_RPATH TRUE)
 SET(APPCORE_AGENT "appcore-agent")
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(pkg_agent REQUIRED aul dlog capi-appfw-app-control capi-appfw-app-common vconf ecore-core vconf-internal-keys appcore-common capi-system-info)
+pkg_check_modules(pkg_agent REQUIRED aul dlog capi-appfw-app-control capi-appfw-app-common vconf ecore-core vconf-internal-keys app-core-cpp capi-system-info)
 FOREACH(flag ${pkg_agent_CFLAGS})
        SET(EXTRA_CFLAGS_agent "${EXTRA_CFLAGS_agent} ${flag}")
 ENDFOREACH(flag)
@@ -43,4 +44,4 @@ INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/appcore-agent
        PATTERN "*.h*"
        )
 
-ADD_SUBDIRECTORY(unittests)
\ No newline at end of file
+ADD_SUBDIRECTORY(unittests)
index 1d5b0cc..abb8a0c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017 - 2021 Samsung Electronics Co., Ltd All Rights Reserved
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -121,7 +121,7 @@ typedef int (*service_app_job_cb)(int job_status, const char *job_id,
  * @since_tizen 4.0
  * @remarks This is for App Framework internally.
  */
-typedef struct service_app_job_s *service_app_job_h;
+typedef void *service_app_job_h;
 
 /**
  * @brief Adds the job handler.
index c24172b..c5ac47a 100644 (file)
@@ -11,7 +11,7 @@ BuildRequires:  pkgconfig(aul)
 BuildRequires:  pkgconfig(capi-appfw-app-control)
 BuildRequires:  pkgconfig(capi-appfw-app-common)
 BuildRequires:  pkgconfig(capi-system-info)
-BuildRequires:  pkgconfig(appcore-common)
+BuildRequires:  pkgconfig(app-core-cpp)
 BuildRequires:  pkgconfig(dlog)
 BuildRequires:  pkgconfig(ecore-core)
 BuildRequires:  pkgconfig(gmock)
diff --git a/src/job-handler.cc b/src/job-handler.cc
new file mode 100644 (file)
index 0000000..d3881b6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "job-handler.hh"
+#include "log-private.hh"
+
+namespace tizen_cpp {
+
+JobHandler::JobHandler(std::string job_id, service_app_job_cb cb,
+    void* user_data)
+    : job_id_(std::move(job_id)),
+      cb_(cb),
+      user_data_(user_data) {
+}
+
+const std::string& JobHandler::GetJobId() const {
+  return job_id_;
+}
+
+void JobHandler::Do(const Job* job) {
+  if (cb_)
+    cb_(job->GetStatus(), job->GetId().c_str(), job->GetData(), user_data_);
+}
+
+}  // namespace tizen_cpp
diff --git a/src/job-handler.hh b/src/job-handler.hh
new file mode 100644 (file)
index 0000000..81d9769
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JOB_HANDLER_HH_
+#define JOB_HANDLER_HH_
+
+#include <string>
+
+#include "job.hh"
+#include "service_app_internal.h"
+
+namespace tizen_cpp {
+
+class JobHandler {
+ public:
+  JobHandler(std::string job_id, service_app_job_cb cb, void* user_data);
+
+  const std::string& GetJobId() const;
+  void Do(const Job* job);
+
+ private:
+  std::string job_id_;
+  service_app_job_cb cb_;
+  void* user_data_;
+};
+
+}  // namespace tizen_cpp
+
+#endif  // JOB_HANDLER_HH_
diff --git a/src/job-manager.cc b/src/job-manager.cc
new file mode 100644 (file)
index 0000000..3293c11
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aul.h>
+#include <aul_job_scheduler.h>
+
+#include <algorithm>
+
+#include "job-manager.hh"
+#include "log-private.hh"
+
+namespace tizen_cpp {
+
+void JobManager::AddJobHandler(JobHandler* handler) {
+  handlers_.push_back(handler);
+}
+
+void JobManager::RemoveJobHandler(JobHandler* handler) {
+  auto found = std::find(handlers_.begin(), handlers_.end(), handler);
+  if (found == handlers_.end())
+    return;
+
+  handlers_.erase(found);
+}
+
+bool JobManager::ExistJobHandler(const std::string& job_id) {
+  for (auto* h : handlers_) {
+    if (h->GetJobId() == job_id)
+      return true;
+  }
+
+  return false;
+}
+
+bool JobManager::ExistJobHandler(JobHandler* handler) {
+  for (auto* h :  handlers_) {
+    if (h == handler)
+      return true;
+  }
+
+  return false;
+}
+
+void JobManager::Do(std::shared_ptr<Job> job) {
+  job->SetEventListener(this);
+  if (!ExistJobHandler(job->GetId())) {
+    job->SetTimer();
+    AddPendingJob(job);
+  } else {
+    job->SetIdler();
+    AddRunningJob(job);
+  }
+}
+
+int JobManager::Finish(const std::string& job_id) {
+  RemoveRunningJob(job_id);
+  int ret = aul_job_scheduler_update_job_status(job_id.c_str(),
+      JOB_STATUS_FINISHED);
+  if (ret != AUL_R_OK) {
+    _E("aul_job_scheduler_update_job_status() is failed. error(%d)", ret);
+    return ret;
+  }
+
+  return 0;
+}
+
+void JobManager::FinishAllJobs() {
+  running_jobs_.clear();
+  pending_jobs_.clear();
+}
+
+void JobManager::FlushPendingJob(const std::string& job_id) {
+  auto iter = pending_jobs_.begin();
+  while (iter != pending_jobs_.end()) {
+    if ((*iter)->GetId() == job_id) {
+      auto job = *iter;
+      iter = pending_jobs_.erase(iter);
+      job->UnsetTimer();
+      AddPendingJob(job);
+      job->SetIdler();
+    } else {
+      iter++;
+    }
+  }
+}
+
+void JobManager::AddRunningJob(std::shared_ptr<Job> job) {
+  running_jobs_.push_back(job);
+}
+
+void JobManager::RemoveRunningJob(const std::string& job_id) {
+  auto iter = running_jobs_.begin();
+  while (iter != running_jobs_.end()) {
+    if ((*iter)->GetId() == job_id)
+      iter = running_jobs_.erase(iter);
+    else
+      iter++;
+  }
+}
+
+void JobManager::AddPendingJob(std::shared_ptr<Job> job) {
+  pending_jobs_.push_back(job);
+}
+
+void JobManager::RemovePendingJob(const std::string& job_id) {
+  auto iter = pending_jobs_.begin();
+  while (iter != pending_jobs_.end()) {
+    if ((*iter)->GetId() == job_id)
+      iter = pending_jobs_.erase(iter);
+    else
+      iter++;
+  }
+}
+
+void JobManager::OnTimedOut(const Job* job) {
+  _E("[__TIMEDOUT__] Job(%s), Status(%d)",
+      job->GetId().c_str(), job->GetStatus());
+  RemovePendingJob(job->GetId());
+}
+
+void JobManager::OnRun(const Job* job) {
+  _D("[__JOB__] START. Job(%s), Status(%d)",
+      job->GetId().c_str(), job->GetStatus());
+  if (job->GetStatus() == SERVICE_APP_JOB_STATUS_START) {
+    aul_job_scheduler_update_job_status(job->GetId().c_str(), JOB_STATUS_START);
+    for (auto* h : handlers_) {
+      if (h->GetJobId() == job->GetId())
+        h->Do(job);
+    }
+  } else {
+    for (auto* h : handlers_) {
+      if (h->GetJobId() == job->GetId())
+        h->Do(job);
+    }
+    aul_job_scheduler_update_job_status(job->GetId().c_str(),
+        JOB_STATUS_STOPPED);
+  }
+  _D("[__JOB__] END. Job(%s), Status(%d)",
+      job->GetId().c_str(), job->GetStatus());
+  RemoveRunningJob(job->GetId());
+}
+
+}  // namespace tizen_cpp
diff --git a/src/job-manager.hh b/src/job-manager.hh
new file mode 100644 (file)
index 0000000..2d6c35c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JOB_MANAGER_HH_
+#define JOB_MANAGER_HH_
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "job.hh"
+#include "job-handler.hh"
+
+namespace tizen_cpp {
+
+class JobManager : public Job::IEvent {
+ public:
+  void AddJobHandler(JobHandler* handler);
+  void RemoveJobHandler(JobHandler* handler);
+  bool ExistJobHandler(JobHandler* handler);
+  void Do(std::shared_ptr<Job> job);
+  int Finish(const std::string& job_id);
+  void FinishAllJobs();
+  void FlushPendingJob(const std::string& job_id);
+
+ private:
+  bool ExistJobHandler(const std::string& job_id);
+  void AddPendingJob(std::shared_ptr<Job> job);
+  void RemovePendingJob(const std::string& job_id);
+  void AddRunningJob(std::shared_ptr<Job> job);
+  void RemoveRunningJob(const std::string& job_id);
+
+  void OnTimedOut(const Job* job) override;
+  void OnRun(const Job* job) override;
+
+ private:
+  std::list<JobHandler*> handlers_;
+  std::list<std::shared_ptr<Job>> pending_jobs_;
+  std::list<std::shared_ptr<Job>> running_jobs_;
+};
+
+}  // namespace tizen_cpp
+
+#endif  // JOB_MANAGER_HH_
diff --git a/src/job.cc b/src/job.cc
new file mode 100644 (file)
index 0000000..7af489a
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "job.hh"
+#include "log-private.hh"
+
+namespace tizen_cpp {
+
+Job::Job(int status, std::string id, tizen_base::Bundle data)
+    : status_(status), id_(std::move(id)), data_(std::move(data)) {
+}
+
+Job::~Job() {
+  UnsetIdler();
+  UnsetTimer();
+}
+
+int Job::GetStatus() const {
+  return status_;
+}
+
+const std::string& Job::GetId() const {
+  return id_;
+}
+
+bundle* Job::GetData() const {
+  return data_.GetHandle();
+}
+
+void Job::SetEventListener(IEvent* listener) {
+  listener_ = listener;
+}
+
+void Job::SetTimer() {
+  if (timer_ == 0) {
+    timer_ = g_timeout_add(5000, TimedOutCb, this);
+    if (timer_ == 0)
+      _E("g_timeout_add() is failed");
+  }
+}
+
+void Job::UnsetTimer() {
+  if (timer_ != 0) {
+    g_source_remove(timer_);
+    timer_ = 0;
+  }
+}
+
+void Job::SetIdler() {
+  if (idler_ == 0) {
+    idler_ = g_idle_add(IdleCb, this);
+    if (idler_ == 0)
+      _E("g_idle_add() is failed");
+  }
+}
+
+void Job::UnsetIdler() {
+  if (idler_ != 0) {
+    g_source_remove(idler_);
+    idler_ = 0;
+  }
+}
+
+gboolean Job::TimedOutCb(gpointer data) {
+  auto* handle = static_cast<Job*>(data);
+  handle->timer_ = 0;
+
+  auto* listener = handle->listener_;
+  if (listener != nullptr)
+    listener->OnTimedOut(handle);
+
+  return G_SOURCE_REMOVE;
+}
+
+gboolean Job::IdleCb(gpointer data) {
+  auto* handle = static_cast<Job*>(data);
+  handle->idler_ = 0;
+  auto* listener = handle->listener_;
+  if (listener != nullptr)
+    listener->OnRun(handle);
+
+  return G_SOURCE_REMOVE;
+}
+
+}  // namespace tizen_cpp
diff --git a/src/job.hh b/src/job.hh
new file mode 100644 (file)
index 0000000..c7d22fa
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JOB_HH_
+#define JOB_HH_
+
+#include <bundle_cpp.h>
+#include <glib.h>
+
+#include <string>
+
+namespace tizen_cpp {
+
+class Job {
+ public:
+  class IEvent {
+   public:
+    virtual void OnTimedOut(const Job* job) = 0;
+    virtual void OnRun(const Job* job) = 0;
+  };
+
+  Job(int status, std::string id, tizen_base::Bundle data);
+  virtual ~Job();
+
+  int GetStatus() const;
+  const std::string& GetId() const;
+  bundle* GetData() const;
+  void SetEventListener(IEvent* listener);
+  void SetTimer();
+  void UnsetTimer();
+  void SetIdler();
+  void UnsetIdler();
+
+ private:
+  static gboolean TimedOutCb(gpointer data);
+  static gboolean IdleCb(gpointer data);
+
+ private:
+  int status_;
+  std::string id_;
+  tizen_base::Bundle data_;
+  IEvent* listener_ = nullptr;
+  guint timer_ = 0;
+  guint idler_ = 0;
+};
+
+}  // namespace tizen_cpp
+
+#endif  // JOB_HH_
diff --git a/src/log-private.hh b/src/log-private.hh
new file mode 100644 (file)
index 0000000..014c904
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOG_PRIVATE_HH_
+#define LOG_PRIVATE_HH_
+
+#include <dlog.h>
+
+#undef LOG_TAG
+#define LOG_TAG "CAPI_APPFW_APPLICATION"
+
+#undef _E
+#define _E LOGE
+
+#undef _W
+#define _W LOGW
+
+#undef _I
+#define _I LOGI
+
+#undef _D
+#define _D LOGD
+
+#endif  // LOG_PRIVATE_HH_
diff --git a/src/service_app_main.c b/src/service_app_main.c
deleted file mode 100644 (file)
index 35ad3b0..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the License);
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an AS IS BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <bundle.h>
-#include <aul.h>
-#include <dlog.h>
-#include <vconf-internal-keys.h>
-#include <app_common.h>
-#include <Ecore.h>
-#include <appcore_base.h>
-#include <service_app.h>
-#include <aul_job_scheduler.h>
-#include <bundle_internal.h>
-#include <glib.h>
-#include <app_control_internal.h>
-
-#include "service_app_extension.h"
-#include "service_app_internal.h"
-
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-
-#define LOG_TAG "CAPI_APPFW_APPLICATION"
-
-typedef enum {
-       APP_STATE_NOT_RUNNING,
-       APP_STATE_CREATING,
-       APP_STATE_RUNNING,
-} app_state_e;
-
-struct app_event_handler {
-       app_event_type_e type;
-       app_event_cb cb;
-       void *data;
-       void* raw;
-};
-
-struct app_event_info {
-       app_event_type_e type;
-       void *value;
-};
-
-struct service_app_context {
-       service_app_lifecycle_callback_s callback;
-       void *data;
-       GList *running_jobs;
-       GList *pending_jobs;
-       GList *job_handlers;
-};
-
-struct service_app_job_s {
-       char *job_id;
-       service_app_job_cb callback;
-       void *user_data;
-};
-
-struct job_s {
-       int job_status;
-       char *job_id;
-       bundle *job_data;
-       guint timer;
-       guint idler;
-};
-
-static struct service_app_context __context;
-static app_state_e __app_state = APP_STATE_NOT_RUNNING;
-
-static int __app_event_converter[APPCORE_BASE_EVENT_MAX] = {
-       [APP_EVENT_LOW_MEMORY] = APPCORE_BASE_EVENT_LOW_MEMORY,
-       [APP_EVENT_LOW_BATTERY] = APPCORE_BASE_EVENT_LOW_BATTERY,
-       [APP_EVENT_LANGUAGE_CHANGED] = APPCORE_BASE_EVENT_LANG_CHANGE,
-       [APP_EVENT_DEVICE_ORIENTATION_CHANGED] = APPCORE_BASE_EVENT_DEVICE_ORIENTATION_CHANGED,
-       [APP_EVENT_REGION_FORMAT_CHANGED] = APPCORE_BASE_EVENT_REGION_CHANGE,
-       [APP_EVENT_SUSPENDED_STATE_CHANGED] = APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE,
-};
-
-static int __on_error(app_error_e error, const char *function, const char *description);
-static void __destroy_job_handler(gpointer data);
-static bool __exist_job_handler(const char *job_id);
-
-static struct job_s *__create_job(int job_status, const char *job_id,
-               bundle *job_data)
-{
-       struct job_s *job;
-
-       job = calloc(1, sizeof(struct job_s));
-       if (job == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Out of memory");
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       job->job_id = strdup(job_id);
-       if (job->job_id == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Out of memory");
-               free(job);
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       job->job_data = bundle_dup(job_data);
-       if (job->job_data == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Out of memory");
-               free(job->job_id);
-               free(job);
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       job->job_status = job_status;
-
-       return job;
-}
-
-static void __destroy_job(gpointer data)
-{
-       struct job_s *job = (struct job_s *)data;
-
-       if (job == NULL)
-               return;
-
-       if (job->idler)
-               g_source_remove(job->idler); /* LCOV_EXCL_LINE */
-       if (job->timer)
-               g_source_remove(job->timer); /* LCOV_EXCL_LINE */
-       if (job->job_data)
-               bundle_free(job->job_data);
-       if (job->job_id)
-               free(job->job_id);
-       free(job);
-}
-
-/* LCOV_EXCL_START */
-static gboolean __pending_job_timeout_handler(gpointer data)
-{
-       struct job_s *job = (struct job_s *)data;
-
-       LOGE("[__TIMEOUT__] Job(%s) Status(%d)", job->job_id, job->job_status);
-       __context.pending_jobs = g_list_remove(__context.pending_jobs, job);
-       job->timer = 0;
-       __destroy_job(job);
-
-       return G_SOURCE_REMOVE;
-}
-/* LCOV_EXCL_STOP */
-
-static void __job_finish(void)
-{
-       if (__context.running_jobs) {
-               /* LCOV_EXCL_START */
-               g_list_free_full(__context.running_jobs, __destroy_job);
-               __context.running_jobs = NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       if (__context.pending_jobs) {
-               /* LCOV_EXCL_START */
-               g_list_free_full(__context.pending_jobs, __destroy_job);
-               __context.pending_jobs = NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       if (__context.job_handlers) {
-               g_list_free_full(__context.job_handlers, __destroy_job_handler);
-               __context.job_handlers = NULL;
-       }
-}
-
-static int __service_app_create(void *data)
-{
-       LOGW("service_app_create()");
-       appcore_base_on_create();
-
-       if (__context.callback.create == NULL ||
-                       __context.callback.create(__context.data) == false)
-               return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__, "service_app_create_cb() returns false");
-
-       return APP_ERROR_NONE;
-}
-
-static int __service_app_terminate(void *data)
-{
-       LOGW("service_app_terminate()");
-       if (__context.callback.terminate)
-               __context.callback.terminate(__context.data);
-
-       appcore_base_on_terminate();
-
-       return APP_ERROR_NONE;
-}
-
-static int __service_app_control(bundle *b, void *data)
-{
-       app_control_h app_control = NULL;
-       const char *job_id;
-
-       LOGW("service_app_control()");
-       appcore_base_on_control(b);
-
-       job_id = bundle_get_val(b, AUL_K_JOB_ID);
-       if (job_id) {
-               LOGD("[__JOB__] Job(%s)", job_id);
-               return 0;
-       }
-
-       if (app_control_create_event(b, &app_control) != 0)
-               return -1;
-
-       if (__context.callback.app_control)
-               __context.callback.app_control(app_control, __context.data);
-
-       app_control_destroy(app_control);
-
-       return 0;
-}
-
-static int __service_app_receive(aul_type type, bundle *b, void *data)
-{
-       appcore_base_on_receive(type, b);
-
-       if (type == AUL_TERMINATE_BGAPP) {
-               appcore_base_exit();
-               return 0;
-       }
-
-       return 0;
-}
-
-static void __loop_init(int argc, char **argv, void *data)
-{
-       ecore_init();
-}
-
-static void __loop_fini(void)
-{
-       ecore_shutdown();
-}
-
-static void __loop_run(void *data)
-{
-       ecore_main_loop_begin();
-}
-
-static void __exit_main_loop(void *data)
-{
-       ecore_main_loop_quit();
-}
-
-static void __loop_exit(void *data)
-{
-       ecore_main_loop_thread_safe_call_sync((Ecore_Data_Cb)__exit_main_loop, NULL);
-}
-
-static const char *__error_to_string(app_error_e error)
-{
-       switch (error) {
-       case APP_ERROR_NONE:
-               return "NONE";
-       case APP_ERROR_INVALID_PARAMETER:
-               return "INVALID_PARAMETER";
-       case APP_ERROR_OUT_OF_MEMORY:
-               return "OUT_OF_MEMORY";
-       case APP_ERROR_INVALID_CONTEXT:
-               return "INVALID_CONTEXT";
-       case APP_ERROR_NO_SUCH_FILE:
-               return "NO_SUCH_FILE";
-       case APP_ERROR_ALREADY_RUNNING:
-               return "ALREADY_RUNNING";
-       default:
-               return "UNKNOWN";
-       }
-}
-
-static int __on_error(app_error_e error, const char *function, const char *description)
-{
-       if (description)
-               LOGE("[%s] %s(0x%08x) : %s", function, __error_to_string(error), error, description);
-       else
-               LOGE("[%s] %s(0x%08x)", function, __error_to_string(error), error);
-
-       return error;
-}
-
-EXPORT_API int service_app_main_ext(int argc, char **argv, service_app_lifecycle_callback_s *callback,
-               service_app_loop_method_s *method, void *user_data)
-{
-       int ret;
-       appcore_base_ops ops = appcore_base_get_default_ops();
-
-       if (argc < 1 || argv == NULL || callback == NULL || method == NULL)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
-
-       if (callback->create == NULL)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "service_app_create_cb() callback must be registered");
-
-       if (__app_state != APP_STATE_NOT_RUNNING)
-               return __on_error(APP_ERROR_ALREADY_RUNNING, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
-
-       /* override methods */
-       ops.create = __service_app_create;
-       ops.terminate = __service_app_terminate;
-       ops.control = __service_app_control;
-       ops.receive = __service_app_receive;
-       ops.run = method->run;
-       ops.exit = method->exit;
-       ops.init = method->init;
-       ops.finish = method->fini;
-
-       __context.callback = *callback;
-       __context.data = user_data;
-
-       __app_state = APP_STATE_CREATING;
-       ret = appcore_base_init(ops, argc, argv, NULL);
-       if (ret < 0) {
-               /* LCOV_EXCL_START */
-               __app_state = APP_STATE_NOT_RUNNING;
-               return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__, NULL);
-               /* LCOV_EXCL_STOP */
-       }
-
-       appcore_base_fini();
-       __job_finish();
-       __app_state = APP_STATE_NOT_RUNNING;
-       return APP_ERROR_NONE;
-}
-
-EXPORT_API int service_app_main(int argc, char **argv, service_app_lifecycle_callback_s *callback, void *user_data)
-{
-       service_app_loop_method_s method = {
-               .init = __loop_init,
-               .fini = __loop_fini,
-               .run = __loop_run,
-               .exit = __loop_exit,
-       };
-
-       LOGW("service_app_main()");
-       return service_app_main_ext(argc, argv, callback, &method, user_data);
-}
-
-EXPORT_API void service_app_exit(void)
-{
-       LOGW("service_app_exit()");
-       appcore_base_exit();
-}
-
-static int __event_cb(void *event, void *data)
-{
-       app_event_handler_h handler = data;
-
-       struct app_event_info app_event;
-
-       app_event.type = handler->type;
-       app_event.value = event;
-
-       if (handler->cb)
-               handler->cb(&app_event, handler->data);
-       return 0;
-}
-
-EXPORT_API int service_app_add_event_handler(app_event_handler_h *event_handler, app_event_type_e event_type, app_event_cb callback, void *user_data)
-{
-       app_event_handler_h handler;
-
-       if (event_handler == NULL || callback == NULL)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "null parameter");
-
-       if (event_type < APP_EVENT_LOW_MEMORY || event_type > APP_EVENT_SUSPENDED_STATE_CHANGED ||
-                       event_type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, "invalid event type");
-
-       handler = calloc(1, sizeof(struct app_event_handler));
-       if (!handler)
-               return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__, "insufficient memory"); /* LCOV_EXCL_LINE */
-
-       handler->type = event_type;
-       handler->cb = callback;
-       handler->data = user_data;
-       handler->raw = appcore_base_add_event(__app_event_converter[event_type], __event_cb, handler);
-
-       *event_handler = handler;
-
-       return APP_ERROR_NONE;
-}
-
-EXPORT_API int service_app_remove_event_handler(app_event_handler_h event_handler)
-{
-       int ret;
-       app_event_type_e type;
-
-       if (event_handler == NULL)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
-
-       type = event_handler->type;
-       if (type < APP_EVENT_LOW_MEMORY ||
-                       type > APP_EVENT_SUSPENDED_STATE_CHANGED ||
-                       type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
-
-       ret = appcore_base_remove_event(event_handler->raw);
-       free(event_handler);
-
-       if (ret < 0)
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
-
-       return APP_ERROR_NONE;
-}
-
-EXPORT_API void service_app_exit_without_restart(void)
-{
-       aul_status_update_v2(STATUS_NORESTART);
-       appcore_base_exit();
-}
-
-static void __invoke_job_callback(int status, const char *job_id,
-               bundle *job_data)
-{
-       struct service_app_job_s *handle;
-       GList *iter;
-
-       iter = __context.job_handlers;
-       while (iter) {
-               handle = (struct service_app_job_s *)iter->data;
-               iter = iter->next;
-               if (strcmp(handle->job_id, job_id) == 0) {
-                       handle->callback(status, job_id, job_data,
-                                       handle->user_data);
-               }
-       }
-}
-
-static gboolean __handle_job(gpointer data)
-{
-       struct job_s *job = (struct job_s *)data;
-
-       __context.running_jobs = g_list_remove(__context.running_jobs, job);
-
-       LOGD("[__JOB___] Job(%s) Status(%d) START",
-                       job->job_id, job->job_status);
-       if (job->job_status == SERVICE_APP_JOB_STATUS_START) {
-               aul_job_scheduler_update_job_status(job->job_id,
-                               JOB_STATUS_START);
-               __invoke_job_callback(job->job_status, job->job_id,
-                               job->job_data);
-       } else {
-               __invoke_job_callback(job->job_status, job->job_id,
-                               job->job_data);
-               aul_job_scheduler_update_job_status(job->job_id,
-                               JOB_STATUS_STOPPED);
-       }
-       LOGD("[__JOB__] Job(%s) Status(%d) END",
-                       job->job_id, job->job_status);
-
-       job->idler = 0;
-       __destroy_job(job);
-
-       return G_SOURCE_REMOVE;
-}
-
-static void __flush_pending_job(const char *job_id)
-{
-       struct job_s *job;
-       GList *iter;
-
-       iter = __context.pending_jobs;
-       while (iter) {
-               /* LCOV_EXCL_START */
-               job = (struct job_s *)iter->data;
-               iter = iter->next;
-               if (strcmp(job->job_id, job_id) == 0) {
-                       __context.pending_jobs = g_list_remove(
-                                       __context.pending_jobs, job);
-
-                       if (job->timer) {
-                               g_source_remove(job->timer);
-                               job->timer = 0;
-                       }
-
-                       job->idler = g_idle_add(__handle_job, job);
-                       __context.running_jobs = g_list_append(
-                                       __context.running_jobs, job);
-               }
-               /* LCOV_EXCL_STOP */
-       }
-}
-
-static bool __exist_job_handler(const char *job_id)
-{
-       struct service_app_job_s *handle;
-       GList *iter;
-
-       iter = __context.job_handlers;
-       while (iter) {
-               handle = (struct service_app_job_s *)iter->data;
-               if (strcmp(handle->job_id, job_id) == 0)
-                       return true;
-
-               iter = iter->next;
-       }
-
-       return false;
-}
-
-static struct service_app_job_s *__create_job_handler(const char *job_id,
-               service_app_job_cb callback, void *user_data)
-{
-       struct service_app_job_s *handle;
-
-       handle = calloc(1, sizeof(struct service_app_job_s));
-       if (handle == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Out of memory");
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       handle->job_id = strdup(job_id);
-       if (handle->job_id == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Out of memory");
-               free(handle);
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       handle->callback = callback;
-       handle->user_data = user_data;
-
-       return handle;
-}
-
-static void __destroy_job_handler(gpointer data)
-{
-       struct service_app_job_s *handle = (struct service_app_job_s *)data;
-
-       if (handle == NULL)
-               return;
-
-       if (handle->job_id)
-               free(handle->job_id);
-       free(handle);
-}
-
-EXPORT_API service_app_job_h service_app_add_job_handler(const char *job_id,
-               service_app_job_cb callback, void *user_data)
-{
-       struct service_app_job_s *handle;
-
-       if (job_id == NULL || callback == NULL) {
-               /* LCOV_EXCL_START */
-               LOGE("Invalid parameter");
-               return NULL;
-               /* LCOV_EXCL_STOP */
-       }
-
-       handle = __create_job_handler(job_id, callback, user_data);
-       if (handle == NULL)
-               return NULL;
-
-       __context.job_handlers = g_list_append(__context.job_handlers, handle);
-
-       __flush_pending_job(job_id);
-
-       return handle;
-}
-
-EXPORT_API int service_app_remove_job_handler(service_app_job_h handle)
-{
-       if (handle == NULL ||
-                       g_list_index(__context.job_handlers, handle) < 0) {
-               /* LCOV_EXCL_START */
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
-                               NULL);
-               /* LCOV_EXCL_STOP */
-       }
-
-       __context.job_handlers = g_list_remove(__context.job_handlers, handle);
-
-       __destroy_job_handler(handle);
-
-       return APP_ERROR_NONE;
-}
-
-EXPORT_API int service_app_job_raise(int job_status, const char *job_id,
-               bundle *job_data)
-{
-       struct job_s *job;
-
-       if (job_status < SERVICE_APP_JOB_STATUS_START ||
-                       job_status > SERVICE_APP_JOB_STATUS_STOP ||
-                       job_id == NULL || job_data == NULL) {
-               /* LCOV_EXCL_START */
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
-                               NULL);
-               /* LCOV_EXCL_STOP */
-       }
-
-       job = __create_job(job_status, job_id, job_data);
-       if (job == NULL)
-               return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL); /* LCOV_EXCL_LINE */
-
-       if (!__exist_job_handler(job_id)) {
-               /* LCOV_EXCL_START */
-               job->timer = g_timeout_add(5000, __pending_job_timeout_handler,
-                               job);
-               __context.pending_jobs = g_list_append(__context.pending_jobs,
-                               job);
-               /* LCOV_EXCL_STOP */
-       } else {
-               job->idler = g_idle_add(__handle_job, job);
-               __context.running_jobs = g_list_append(__context.running_jobs,
-                               job);
-       }
-
-       return APP_ERROR_NONE;
-}
-
-static void __remove_running_job(const char *job_id)
-{
-       struct job_s *job;
-       GList *iter;
-
-       iter = __context.running_jobs;
-       while (iter) {
-               /* LCOV_EXCL_START */
-               job = (struct job_s *)iter->data;
-               iter = iter->next;
-               if (strcmp(job->job_id, job_id) == 0) {
-                       __context.running_jobs = g_list_remove(
-                                       __context.running_jobs, job);
-                       __destroy_job(job);
-               }
-               /* LCOV_EXCL_STOP */
-       }
-}
-
-EXPORT_API int service_app_job_finished(const char *job_id)
-{
-       int r;
-
-       if (job_id == NULL) {
-               /* LCOV_EXCL_START */
-               return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
-                               NULL);
-               /* LCOV_EXCL_STOP */
-       }
-
-       __remove_running_job(job_id);
-
-       r = aul_job_scheduler_update_job_status(job_id, JOB_STATUS_FINISHED);
-       if (r != AUL_R_OK) {
-               /* LCOV_EXCL_START */
-               return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__,
-                               NULL);
-               /* LCOV_EXCL_STOP */
-       }
-
-       return APP_ERROR_NONE;
-}
diff --git a/src/service_app_main.cc b/src/service_app_main.cc
new file mode 100644 (file)
index 0000000..2e7335b
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Ecore.h>
+#include <app_control_internal.h>
+#include <aul.h>
+#include <aul_job_scheduler.h>
+#include <dlog.h>
+#include <glib.h>
+#include <service_app.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vconf-internal-keys.h>
+
+#include <app_core_base.hh>
+#include <app_event_internal.hh>
+
+#include <list>
+#include <memory>
+#include <type_traits>
+
+#include "service_app_extension.h"
+#include "service_app_internal.h"
+
+#include "job-manager.hh"
+#include "log-private.hh"
+
+using namespace tizen_cpp;
+
+namespace {
+
+const char* __error_to_string(app_error_e error) {
+  switch (error) {
+    case APP_ERROR_NONE:
+      return "NONE";
+    case APP_ERROR_INVALID_PARAMETER:
+      return "INVALID_PARAMETER";
+    case APP_ERROR_OUT_OF_MEMORY:
+      return "OUT_OF_MEMORY";
+    case APP_ERROR_INVALID_CONTEXT:
+      return "INVALID_CONTEXT";
+    case APP_ERROR_NO_SUCH_FILE:
+      return "NO_SUCH_FILE";
+    case APP_ERROR_ALREADY_RUNNING:
+      return "ALREADY_RUNNING";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+int __on_error(app_error_e error, const char* function,
+    const char* description) {
+  if (description) {
+    _E("[%s] %s(0x%08x) : %s",
+        function, __error_to_string(error), error, description);
+  } else {
+    _E("[%s] %s(0x%08x)", function, __error_to_string(error), error);
+  }
+
+  return error;
+}
+
+constexpr int APP_EVENT_MAX = 7;
+constexpr IAppCore::IEvent::Type __app_event_converter[APP_EVENT_MAX] = {
+  [APP_EVENT_LOW_MEMORY] = IAppCore::IEvent::Type::LOW_MEMORY,
+  [APP_EVENT_LOW_BATTERY] = IAppCore::IEvent::Type::LOW_BATTERY,
+  [APP_EVENT_LANGUAGE_CHANGED] = IAppCore::IEvent::Type::LANG_CHANGE,
+  [APP_EVENT_DEVICE_ORIENTATION_CHANGED] = IAppCore::IEvent::Type::DEVICE_ORIENTATION_CHANGED,
+  [APP_EVENT_REGION_FORMAT_CHANGED] = IAppCore::IEvent::Type::REGION_CHANGE,
+  [APP_EVENT_SUSPENDED_STATE_CHANGED] = IAppCore::IEvent::Type::SUSPENDED_STATE_CHANGE,
+};
+
+class AppContext : public AppCoreBase {
+ public:
+  enum AppState {
+    APP_STATE_NOT_RUNNING,
+    APP_STATE_CREATING,
+    APP_STATE_RUNNING,
+  };
+
+  AppContext(service_app_lifecycle_callback_s callback,
+      service_app_loop_method_s method, void* data)
+          : callback_(callback), method_(method), data_(data) {
+    state_ = APP_STATE_CREATING;
+  }
+
+  void Run(int argc, char** argv) override {
+    SetAppState(APP_STATE_RUNNING);
+    AppCoreBase::Run(argc, argv);
+    SetAppState(APP_STATE_NOT_RUNNING);
+  }
+
+  int OnReceive(aul_type type, tizen_base::Bundle b) override {
+    AppCoreBase::OnReceive(type, b);
+
+    if (type == AUL_TERMINATE_BGAPP) {
+      Exit();
+      return 0;
+    }
+
+    return 0;
+  }
+
+  int OnCreate() override {
+    _W("service_app_create()");
+    AppCoreBase::OnCreate();
+
+    if (callback_.create == nullptr || callback_.create(data_) == false) {
+      return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__,
+          "service_app_create_cb() returns false");
+    }
+
+    return APP_ERROR_NONE;
+  }
+
+  int OnControl(tizen_base::Bundle b) override {
+    _W("service_app_control()");
+    AppCoreBase::OnControl(b);
+
+    std::string job_id = b.GetString(AUL_K_JOB_ID);
+    if (!job_id.empty()) {
+      _D("[__JOB__] Job(%s)", job_id.c_str());
+      return 0;
+    }
+
+    app_control_h app_control = nullptr;
+    if (app_control_create_event(b.GetHandle(), &app_control) != 0)
+      return -1;
+
+    if (callback_.app_control)
+      callback_.app_control(app_control, data_);
+
+    app_control_destroy(app_control);
+
+    return 0;
+  }
+
+  int OnTerminate() override {
+    _W("service_app_terminate()");
+    if (callback_.terminate)
+      callback_.terminate(data_);
+
+    AppCoreBase::OnTerminate();
+
+    return APP_ERROR_NONE;
+  }
+
+  void OnLoopInit(int argc, char** argv) override {
+    if (method_.init)
+      method_.init(argc, argv, data_);
+    else
+      ecore_init();
+  }
+
+  void OnLoopFinish() override {
+    if (method_.fini)
+      method_.fini();
+    else
+      ecore_shutdown();
+  }
+
+  void OnLoopRun() override {
+    if (method_.run)
+      method_.run(data_);
+    else
+      ecore_main_loop_begin();
+  }
+
+  void OnLoopExit() override {
+    if (method_.exit) {
+      method_.exit(data_);
+    } else {
+      ecore_main_loop_thread_safe_call_sync([](void* data) -> void* {
+        ecore_main_loop_quit();
+        return nullptr;
+      }, nullptr);
+    }
+  }
+
+  AppState GetAppState() const {
+    return state_;
+  }
+
+  void SetAppState(AppState state) {
+    state_ = state;
+  }
+
+ private:
+  service_app_lifecycle_callback_s callback_;
+  service_app_loop_method_s method_;
+  void* data_;
+  AppState state_ = APP_STATE_NOT_RUNNING;
+};
+
+std::unique_ptr<AppContext> __context;
+std::list<std::shared_ptr<AppEvent>> __pending_app_events;
+JobManager __job_manager;
+
+}  // namespace
+
+extern "C" EXPORT_API int service_app_main_ext(int argc, char** argv,
+    service_app_lifecycle_callback_s* callback,
+    service_app_loop_method_s* method, void* user_data) {
+  if (argc < 1 || argv == nullptr || callback == nullptr || method == nullptr)
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, nullptr);
+
+  if (callback->create == nullptr) {
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
+        "service_app_create_cb() callback must be registered");
+  }
+
+  if (__context.get() != nullptr &&
+      __context->GetAppState() != AppContext::APP_STATE_NOT_RUNNING)
+    return __on_error(APP_ERROR_ALREADY_RUNNING, __FUNCTION__, nullptr);
+
+  try {
+    __context = std::make_unique<AppContext>(*callback, *method, user_data);
+    for (auto& i : __pending_app_events) {
+      __context->AddEvent(i);
+    }
+    __context->Run(argc, argv);
+  } catch(std::runtime_error& e) {
+    __context->SetAppState(AppContext::APP_STATE_NOT_RUNNING);
+    return __on_error(APP_ERROR_INVALID_CONTEXT, __FUNCTION__, nullptr);
+  }
+
+  __job_manager.FinishAllJobs();
+  return APP_ERROR_NONE;
+}
+
+extern "C" EXPORT_API int service_app_main(int argc, char** argv,
+    service_app_lifecycle_callback_s* callback, void* user_data) {
+  service_app_loop_method_s method = { nullptr, };
+  _W("service_app_main()");
+  return service_app_main_ext(argc, argv, callback, &method, user_data);
+}
+
+extern "C" EXPORT_API void service_app_exit(void) {
+  _W("service_app_exit()");
+  if (__context.get() &&
+      __context->GetAppState() == AppContext::APP_STATE_RUNNING)
+    __context->Exit();
+}
+
+extern "C" EXPORT_API int service_app_add_event_handler(
+    app_event_handler_h* event_handler, app_event_type_e event_type,
+    app_event_cb callback, void *user_data) {
+  if (event_handler == nullptr || callback == nullptr) {
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
+        "null parameter");
+  }
+
+  if (event_type < APP_EVENT_LOW_MEMORY ||
+      event_type > APP_EVENT_SUSPENDED_STATE_CHANGED ||
+      event_type == APP_EVENT_DEVICE_ORIENTATION_CHANGED) {
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
+        "invalid event type");
+  }
+
+  auto* ae = new (std::nothrow) AppEvent(__app_event_converter[event_type],
+      callback, user_data);
+  if (!ae) {
+    return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__,
+        "failed to create handler");
+  }
+
+  auto* h = new (std::nothrow) std::shared_ptr<AppEvent>(ae);
+  if (!h) {
+    delete ae;
+    return __on_error(APP_ERROR_OUT_OF_MEMORY, __FUNCTION__,
+        "failed to create handler");
+  }
+
+  if (__context.get() &&
+      __context->GetAppState() == AppContext::APP_STATE_RUNNING) {
+    __context->AddEvent(*h);
+  } else {
+    __pending_app_events.push_back(*h);
+  }
+
+  *event_handler = reinterpret_cast<app_event_handler_h>(h);
+
+  return APP_ERROR_NONE;
+}
+
+extern "C" EXPORT_API int service_app_remove_event_handler(
+    app_event_handler_h event_handler) {
+  if (event_handler == nullptr)
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, nullptr);
+
+  auto* eb = reinterpret_cast<std::shared_ptr<AppEvent>*>(event_handler);
+  auto type = (*eb)->GetType();
+
+  if (type < IAppCore::IEvent::Type::LOW_MEMORY ||
+      type > IAppCore::IEvent::Type::SUSPENDED_STATE_CHANGE ||
+      type == IAppCore::IEvent::Type::DEVICE_ORIENTATION_CHANGED)
+    return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__, nullptr);
+
+  if (__context.get() &&
+      __context->GetAppState() == AppContext::APP_STATE_RUNNING) {
+    if (!__context->RemoveEvent(*eb)) {
+      return __on_error(APP_ERROR_INVALID_PARAMETER, __FUNCTION__,
+          "invalid raw handler");
+    }
+  } else {
+    __pending_app_events.remove(*eb);
+  }
+
+  delete eb;
+
+  return APP_ERROR_NONE;
+}
+
+extern "C" EXPORT_API void service_app_exit_without_restart(void) {
+  if (__context.get() &&
+      __context->GetAppState() == AppContext::APP_STATE_RUNNING) {
+    aul_status_update_v2(STATUS_NORESTART);
+    __context->Exit();
+  }
+}
+
+extern "C" EXPORT_API service_app_job_h service_app_add_job_handler(
+    const char* job_id, service_app_job_cb callback, void* user_data) {
+  if (job_id == nullptr || callback == nullptr) {
+    _E("Invalid parameter");
+    return nullptr;
+  }
+
+  auto* handle = new (std::nothrow) JobHandler(job_id, callback, user_data);
+  if (handle == nullptr) {
+    _E("Out of memory");
+    return nullptr;
+  }
+
+  __job_manager.AddJobHandler(handle);
+  __job_manager.FlushPendingJob(job_id);
+  return handle;
+}
+
+extern "C" EXPORT_API int service_app_remove_job_handler(
+    service_app_job_h handle) {
+  if (handle == nullptr) {
+    _E("Invalid parameter");
+    return APP_ERROR_INVALID_PARAMETER;
+  }
+
+  auto* h = static_cast<JobHandler*>(handle);
+  if (!__job_manager.ExistJobHandler(h)) {
+    _E("Invalid parameter");
+    return APP_ERROR_INVALID_PARAMETER;
+  }
+
+  __job_manager.RemoveJobHandler(h);
+  delete h;
+  return APP_ERROR_NONE;
+}
+
+extern "C" EXPORT_API int service_app_job_raise(int job_status,
+    const char* job_id, bundle* job_data) {
+  if (job_status < SERVICE_APP_JOB_STATUS_START ||
+      job_status > SERVICE_APP_JOB_STATUS_STOP ||
+      job_id == nullptr || job_data == nullptr) {
+    _E("Invalid parameter");
+    return APP_ERROR_INVALID_PARAMETER;
+  }
+
+  auto job = std::make_shared<Job>(job_status, job_id,
+      tizen_base::Bundle(job_data, true, true));
+  if (job == nullptr) {
+    _E("Out of memory");
+    return APP_ERROR_OUT_OF_MEMORY;
+  }
+
+  __job_manager.Do(job);
+  return APP_ERROR_NONE;
+}
+
+extern "C" EXPORT_API int service_app_job_finished(const char* job_id) {
+  if (job_id == nullptr) {
+    _E("Invalid parameter");
+    return APP_ERROR_INVALID_PARAMETER;
+  }
+
+  if (__job_manager.Finish(job_id) != 0)
+    return APP_ERROR_INVALID_CONTEXT;
+
+  return APP_ERROR_NONE;
+}
index c8e9964..c8fc391 100644 (file)
@@ -1,24 +1,38 @@
-AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TEST_SRCS)
-AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/mock MOCK_SRCS)
-INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/)
+SET(TARGET_TEST "appcore-agent-test")
 
 ENABLE_TESTING()
 
-SET(TARGET_TEST "appcore-agent-test")
-ADD_EXECUTABLE(${TARGET_TEST} ${TEST_SRCS} ${MOCK_SRCS})
-ADD_TEST(${TARGET_TEST} ${TARGET_TEST})
+INCLUDE(FindPkgConfig)
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/)
 
-pkg_check_modules(gtest_pkgs REQUIRED gmock)
-INCLUDE_DIRECTORIES(${gtest_pkgs_INCLUDE_DIRS})
-LINK_DIRECTORIES(${gtest_pkgs_LIBRARY_DIRS})
+pkg_check_modules(test REQUIRED
+       aul
+       dlog
+       capi-appfw-app-control
+       vconf
+       ecore-core
+       vconf-internal-keys
+       app-core-cpp
+       capi-system-info
+       )
 
-INCLUDE(FindPkgConfig)
-pkg_check_modules(test REQUIRED aul dlog capi-appfw-app-control capi-appfw-app-common vconf ecore-core vconf-internal-keys appcore-common capi-system-info)
 FOREACH(flag ${test_CFLAGS})
        SET(EXTRA_CXXFLAGS_test "${EXTRA_CXXFLAGS_test} ${flag}")
 ENDFOREACH(flag)
 
-SET(${EXTRA_CXXFLAGS_test} "${EXTRA_CXXFLAGS_test} --std=c++14")
-SET_TARGET_PROPERTIES(${TARGET_TEST} PROPERTIES COMPILE_FLAGS ${EXTRA_CXXFLAGS_test})
+pkg_check_modules(gtest_pkgs REQUIRED gmock)
+INCLUDE_DIRECTORIES(${gtest_pkgs_INCLUDE_DIRS})
+LINK_DIRECTORIES(${gtest_pkgs_LIBRARY_DIRS})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CXXFLAGS_test}")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS_test}")
 
-TARGET_LINK_LIBRARIES(${TARGET_TEST} ${gtest_pkgs_LIBRARIES} appcore-agent)
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../src/ LIB_SRCS)
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TEST_SRCS)
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/mock/ MOCK_SRCS)
+
+ADD_EXECUTABLE(${TARGET_TEST} ${MOCK_SRCS} ${TEST_SRCS} ${LIB_SRCS})
+TARGET_LINK_LIBRARIES(${TARGET_TEST} ${test_LDFLAGS} ${gtest_pkgs_LIBRARIES})
+
+ADD_TEST(${TARGET_TEST} ${TARGET_TEST})
index 18cc1a7..91ded87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 - 2021 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "mock_hook.h"
 #include "test_fixture.h"
 
-extern "C" int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void* data) {
-  return MOCK_HOOK_P4(AppCoreMock, appcore_base_init, ops, argc, argv, data);
+namespace tizen_cpp {
+
+void AppCoreBase::Run(int argc, char** argv) {
+  TestFixture::GetMock<AppCoreMock>().Run(argc, argv);
+  TestFixture::GetMock<AppCoreMock>().OnLoopInit(argc, argv, this);
+  TestFixture::GetMock<AppCoreMock>().OnCreate();
+  TestFixture::GetMock<AppCoreMock>().OnLoopRun();
+  TestFixture::GetMock<AppCoreMock>().OnLoopFinish();
+  TestFixture::GetMock<AppCoreMock>().OnTerminate();
 }
 
-extern "C" void appcore_base_fini() {
-  return MOCK_HOOK_P0(AppCoreMock, appcore_base_fini);
+void AppCoreBase::Exit() {
+  TestFixture::GetMock<AppCoreMock>().Exit();
+  TestFixture::GetMock<AppCoreMock>().OnLoopExit();
 }
 
-extern "C" void appcore_base_exit() {
-  return MOCK_HOOK_P0(AppCoreMock, appcore_base_exit);
-}
\ No newline at end of file
+}  // namespace tizen_cpp
index e130ece..683c17d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 - 2021 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #ifndef MOCK_APPCORE_MOCK_H_
 #define MOCK_APPCORE_MOCK_H_
+
 #include <gmock/gmock.h>
-#include <appcore_base.h>
+#include <app_core_base.hh>
 
 #include "module_mock.h"
 
 class AppCoreMock : public virtual ModuleMock {
  public:
-  virtual ~AppCoreMock() {}
-
-  MOCK_METHOD4(appcore_base_init, int(appcore_base_ops, int, char**, void*));
-  MOCK_METHOD0(appcore_base_fini, void());
-  MOCK_METHOD0(appcore_base_exit, void());
+  MOCK_METHOD2(Run, void(int, char**));
+  MOCK_METHOD0(Exit, void());
+  MOCK_METHOD0(OnCreate, void());
+  MOCK_METHOD0(OnTerminate, void());
+  MOCK_METHOD3(OnLoopInit, void(int, char**, void*));
+  MOCK_METHOD0(OnLoopFinish, void());
+  MOCK_METHOD0(OnLoopRun, void());
+  MOCK_METHOD0(OnLoopExit, void());
 };
 
-#endif  // MOCK_APPCORE_HOOK_H_
\ No newline at end of file
+#endif  // MOCK_APPCORE_MOCK_H_
\ No newline at end of file
index 9ad6fb9..87cdab9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 - 2021 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@
 #include <service_app.h>
 #include <service_app_extension.h>
 #include <service_app_internal.h>
-#include <appcore_base.h>
 #include <app_common.h>
 #include <aul_job_scheduler.h>
 #include <glib.h>
@@ -49,14 +48,8 @@ class ServiceAppTest : public TestFixture {
   virtual void TearDown() {}
 };
 
-extern "C" int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...) {
-  printf("%s:", tag);
-  va_list ap;
-  va_start(ap, fmt);
-  vprintf(fmt, ap);
-  va_end(ap);
-  printf("\n");
-
+extern "C" int __dlog_print(log_id_t log_id, int prio, const char* tag,
+    const char* fmt, ...) {
   return 0;
 }
 
@@ -66,26 +59,13 @@ extern "C" int aul_job_scheduler_update_job_status(const char* job_id,
 }
 
 bool __service_app_create_cb(void* user_data) {
-  return false;
-}
-
-bool __service_app_create_cb_loop(void* user_data) {
-  bool* loop = reinterpret_cast<bool*>(user_data);
-
-  while (*loop) {
-    std::cout << "wait..." << std::endl;
-    std::this_thread::sleep_for(std::chrono::milliseconds(100));
-  }
-
   return true;
 }
 
 void __service_app_terminate_cb(void* user_data) {
-
 }
 
 void __service_app_control_cb(app_control_h app_control, void* user_data) {
-
 }
 
 TEST_F(ServiceAppTest, Basic) {
@@ -102,14 +82,13 @@ TEST_F(ServiceAppTest, Basic) {
 
   char** argv;
   int argc = bundle_export_to_argv(b.GetHandle(), &argv);
-  std::unique_ptr<char**, std::function<void(char***)>> ptr(&argv, [argc](char*** ptr){
+  std::unique_ptr<char**, std::function<void(char***)>> ptr(&argv,
+      [argc](char*** ptr) {
     bundle_free_exported_argv(argc, ptr);
   });
 
-  ASSERT_TRUE(argc > 0);
-
-  ret = service_app_main(argc, argv, &callback, nullptr);
-  EXPECT_THAT(ret, testing::Eq(APP_ERROR_NONE));
+  ASSERT_GT(argc, 0);
+  using testing::_;
 
   ret = service_app_main(0, nullptr, &callback, nullptr);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_INVALID_PARAMETER));
@@ -118,31 +97,32 @@ TEST_F(ServiceAppTest, Basic) {
   ret = service_app_main(argc, argv, &callback, nullptr);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_INVALID_PARAMETER));
 
-  using testing::_;
-
-  EXPECT_CALL(GetMock<AppCoreMock>(), appcore_base_init(_,_,_,_))
-    .WillOnce(testing::Invoke([&](appcore_base_ops ops, int argc, char** argv,
-        void* data) {
-      ops.create(data);
-      return 0;
-  }));
-
+  EXPECT_CALL(GetMock<AppCoreMock>(), Run(_, _)).Times(1);
   bool loop = true;
+  EXPECT_CALL(GetMock<AppCoreMock>(), OnLoopRun())
+    .WillOnce(testing::Invoke([&]() {
+      while (loop) {
+        std::cout << "wait..." << std::endl;
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+      }
+    }));
+
   std::thread t([&]() {
-      callback.create = __service_app_create_cb_loop;
+      callback.create = __service_app_create_cb;
       service_app_main(argc, argv, &callback, reinterpret_cast<void*>(&loop));
   });
 
   std::this_thread::sleep_for(std::chrono::milliseconds(50));
-  callback.create = __service_app_create_cb;
-  ret = service_app_main(1, argv, &callback, &loop);
+  ret = service_app_main(argc, argv, &callback, &loop);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_ALREADY_RUNNING));
 
-  loop = false;
-  t.join();
-
   // test service_app_exit
-  EXPECT_CALL(GetMock<AppCoreMock>(), appcore_base_exit()).Times(testing::AtLeast(1));
+  EXPECT_CALL(GetMock<AppCoreMock>(), Exit()).Times(1);
+  EXPECT_CALL(GetMock<AppCoreMock>(), OnLoopExit())
+    .WillOnce(testing::Invoke([&]() {
+      loop = false;
+      t.join();
+    }));
   service_app_exit();
 }
 
@@ -154,12 +134,13 @@ TEST_F(ServiceAppTest, Internals) {
   handle = service_app_add_job_handler(nullptr, nullptr, nullptr);
   EXPECT_TRUE(handle == nullptr);
 
-  handle = service_app_add_job_handler("temp", [] (int status, const char* job_id,
-      bundle* job_data, void* user_data) -> int {
-    int* calledCnt = reinterpret_cast<int*>(user_data);
-    *calledCnt = *calledCnt + 1;
-    return 0;
-  }, reinterpret_cast<void*>(&calledCnt));
+  handle = service_app_add_job_handler("temp",
+      [] (int status, const char* job_id, bundle* job_data,
+          void* user_data) -> int {
+        int* calledCnt = reinterpret_cast<int*>(user_data);
+        *calledCnt = *calledCnt + 1;
+        return 0;
+      }, reinterpret_cast<void*>(&calledCnt));
 
   EXPECT_TRUE(handle != nullptr);
   EXPECT_THAT(calledCnt, testing::Eq(0));
@@ -169,7 +150,8 @@ TEST_F(ServiceAppTest, Internals) {
   ret = service_app_job_raise(0, nullptr, nullptr);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_INVALID_PARAMETER));
 
-  ret = service_app_job_raise(SERVICE_APP_JOB_STATUS_START, "temp", b.GetHandle());
+  ret = service_app_job_raise(SERVICE_APP_JOB_STATUS_START, "temp",
+      b.GetHandle());
 
   g_main_context_iteration(g_main_context_default(), 0);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_NONE));
@@ -206,9 +188,9 @@ TEST_F(ServiceAppTest, EventHandlers) {
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_NONE));
   EXPECT_THAT(calledCnt, testing::Eq(0));
 
-  appcore_base_raise_event(&calledCnt, APPCORE_BASE_EVENT_LOW_MEMORY);
+  //appcore_base_raise_event(&calledCnt, APPCORE_BASE_EVENT_LOW_MEMORY);
 
-  EXPECT_THAT(calledCnt, testing::Eq(1));
+  //EXPECT_THAT(calledCnt, testing::Eq(1));
 
   ret = service_app_remove_event_handler(nullptr);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_INVALID_PARAMETER));
@@ -216,11 +198,11 @@ TEST_F(ServiceAppTest, EventHandlers) {
   ret = service_app_remove_event_handler(handle);
   EXPECT_THAT(ret, testing::Eq(APP_ERROR_NONE));
 
-  appcore_base_raise_event(&calledCnt, APPCORE_BASE_EVENT_LOW_MEMORY);
-  EXPECT_THAT(calledCnt, testing::Eq(1));
+  //appcore_base_raise_event(&calledCnt, APPCORE_BASE_EVENT_LOW_MEMORY);
+  //EXPECT_THAT(calledCnt, testing::Eq(1));
 }
 
-} // namespace appcore_agent
+}  // namespace appcore_agent
 
 int main(int argc, char* argv[]) {
   int ret = 0;