[Build] prepare meson build
authorJaeyun Jung <jy1210.jung@samsung.com>
Wed, 6 Dec 2023 05:42:19 +0000 (14:42 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 6 Dec 2023 08:10:39 +0000 (17:10 +0900)
Initial commit, migrate ml-agent and dbus code from api repo.
Prepare meson build for dbus and ml-agent first.

TODO:
1. fix version of ml-agent packages.
2. enable testcase.
3. add .spec for tizen release.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
29 files changed:
daemon/common.h [new file with mode: 0644]
daemon/dbus-interface.h [new file with mode: 0644]
daemon/gdbus-util.c [new file with mode: 0644]
daemon/gdbus-util.h [new file with mode: 0644]
daemon/include/meson.build [new file with mode: 0755]
daemon/include/ml-agent-interface.h [new file with mode: 0755]
daemon/log.h [new file with mode: 0644]
daemon/main.c [new file with mode: 0644]
daemon/meson.build [new file with mode: 0644]
daemon/ml-agent-interface.c [new file with mode: 0755]
daemon/ml-agent.pc.in [new file with mode: 0755]
daemon/model-dbus-impl.cc [new file with mode: 0644]
daemon/modules.c [new file with mode: 0644]
daemon/modules.h [new file with mode: 0644]
daemon/pipeline-dbus-impl.cc [new file with mode: 0644]
daemon/pkg-mgr.cc [new file with mode: 0755]
daemon/pkg-mgr.h [new file with mode: 0755]
daemon/resource-dbus-impl.cc [new file with mode: 0644]
daemon/service-db.cc [new file with mode: 0644]
daemon/service-db.hh [new file with mode: 0644]
dbus/machine-learning-agent.conf.in [new file with mode: 0644]
dbus/machine-learning-agent.service.in [new file with mode: 0644]
dbus/meson.build [new file with mode: 0644]
dbus/model-dbus.xml [new file with mode: 0644]
dbus/org.tizen.machinelearning.service.service.in [new file with mode: 0644]
dbus/pipeline-dbus.xml [new file with mode: 0644]
dbus/resource-dbus.xml [new file with mode: 0644]
meson.build [new file with mode: 0644]
meson_options.txt [new file with mode: 0644]

diff --git a/daemon/common.h b/daemon/common.h
new file mode 100644 (file)
index 0000000..0e09824
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    common.h
+ * @date    25 June 2022
+ * @brief   Internal common header of Machine Learning agent daemon
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This provides the common utility macros for Machine Learning agent daemon.
+ */
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
+
+#ifndef __CONSTRUCTOR__
+#define __CONSTRUCTOR__ __attribute__ ((constructor))
+#endif
+
+#ifndef __DESTRUCTOR__
+#define __DESTRUCTOR__ __attribute__ ((destructor))
+#endif
+
+#endif /* __COMMON_H__ */
diff --git a/daemon/dbus-interface.h b/daemon/dbus-interface.h
new file mode 100644 (file)
index 0000000..ffea9ee
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    dbus-interface.h
+ * @date    25 June 2022
+ * @brief   Internal header for the definition of DBus node and interfaces.
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This defines the machine learning agent's bus, interface, and method names.
+ */
+
+#ifndef __GDBUS_INTERFACE_H__
+#define __GDBUS_INTERFACE_H__
+
+#define DBUS_ML_BUS_NAME                "org.tizen.machinelearning.service"
+#define DBUS_ML_PATH                    "/Org/Tizen/MachineLearning/Service"
+
+/* Pipeline Interface */
+#define DBUS_PIPELINE_INTERFACE          "org.tizen.machinelearning.service.pipeline"
+#define DBUS_PIPELINE_PATH               "/Org/Tizen/MachineLearning/Service/Pipeline"
+
+#define DBUS_PIPELINE_I_SET_HANDLER             "handle-set-pipeline"
+#define DBUS_PIPELINE_I_GET_HANDLER             "handle-get-pipeline"
+#define DBUS_PIPELINE_I_DELETE_HANDLER          "handle-delete-pipeline"
+
+#define DBUS_PIPELINE_I_LAUNCH_HANDLER          "handle-launch-pipeline"
+#define DBUS_PIPELINE_I_START_HANDLER           "handle-start-pipeline"
+#define DBUS_PIPELINE_I_STOP_HANDLER            "handle-stop-pipeline"
+#define DBUS_PIPELINE_I_DESTROY_HANDLER         "handle-destroy-pipeline"
+#define DBUS_PIPELINE_I_GET_STATE_HANDLER       "handle-get-state"
+
+/* Model Interface */
+#define DBUS_MODEL_INTERFACE            "org.tizen.machinelearning.service.model"
+#define DBUS_MODEL_PATH                 "/Org/Tizen/MachineLearning/Service/Model"
+
+#define DBUS_MODEL_I_HANDLER_REGISTER           "handle-register"
+#define DBUS_MODEL_I_HANDLER_UPDATE_DESCRIPTION "handle-update-description"
+#define DBUS_MODEL_I_HANDLER_ACTIVATE           "handle-activate"
+#define DBUS_MODEL_I_HANDLER_GET                "handle-get"
+#define DBUS_MODEL_I_HANDLER_GET_ACTIVATED      "handle-get-activated"
+#define DBUS_MODEL_I_HANDLER_GET_ALL            "handle-get-all"
+#define DBUS_MODEL_I_HANDLER_DELETE             "handle-delete"
+
+/* Resource Interface */
+#define DBUS_RESOURCE_INTERFACE         "org.tizen.machinelearning.service.resource"
+#define DBUS_RESOURCE_PATH              "/Org/Tizen/MachineLearning/Service/Resource"
+
+#define DBUS_RESOURCE_I_HANDLER_ADD                "handle-add"
+#define DBUS_RESOURCE_I_HANDLER_GET                "handle-get"
+#define DBUS_RESOURCE_I_HANDLER_DELETE             "handle-delete"
+
+#endif /* __GDBUS_INTERFACE_H__ */
diff --git a/daemon/gdbus-util.c b/daemon/gdbus-util.c
new file mode 100644 (file)
index 0000000..caefec4
--- /dev/null
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file gdbus-util.c
+ * @date 25 June 2022
+ * @brief Internal GDbus utility wrapper of Machine Learning agent daemon
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <systemd/sd-daemon.h>
+
+#include "gdbus-util.h"
+#include "log.h"
+
+static GDBusConnection *g_dbus_sys_conn = NULL;
+
+/**
+ * @brief Export the DBus interface at the Object path on the bus connection.
+ */
+int
+gdbus_export_interface (gpointer instance, const char *obj_path)
+{
+  if (g_dbus_sys_conn == NULL) {
+    _E ("Cannot get the dbus connection to the system message bus");
+    return -ENOSYS;
+  }
+
+  if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (instance),
+          g_dbus_sys_conn, obj_path, NULL)) {
+    return -EBUSY;
+  }
+
+  return 0;
+}
+
+/**
+ * @brief Callback function for acquireing the bus name.
+ * @remarks If the daemon is launched by systemd service,
+ * it should notify to the systemd about its status when it is ready.
+ */
+static void
+name_acquired_cb (GDBusConnection * connection,
+    const gchar * name, gpointer user_data)
+{
+  sd_notify (0, "READY=1");
+}
+
+/**
+ * @brief Acquire the given name on the SYSTEM session of the DBus message bus.
+ */
+int
+gdbus_get_name (const char *name)
+{
+  guint id;
+
+  id = g_bus_own_name_on_connection (g_dbus_sys_conn, name,
+      G_BUS_NAME_OWNER_FLAGS_NONE, name_acquired_cb, NULL, NULL, NULL);
+  if (id == 0)
+    return -ENOSYS;
+
+  return 0;
+}
+
+/**
+ * @brief Connects the callback functions for each signal of the particular DBus interface.
+ */
+int
+gdbus_connect_signal (gpointer instance, int num_signals,
+    struct gdbus_signal_info *signal_infos)
+{
+  int i;
+  unsigned long handler_id;
+
+  for (i = 0; i < num_signals; i++) {
+    handler_id = g_signal_connect (instance,
+        signal_infos[i].signal_name,
+        signal_infos[i].cb, signal_infos[i].cb_data);
+    if (handler_id <= 0)
+      goto out_err;
+    signal_infos[i].handler_id = handler_id;
+  }
+  return 0;
+
+out_err:
+  for (i = 0; i < num_signals; i++) {
+    if (signal_infos[i].handler_id > 0) {
+      g_signal_handler_disconnect (instance, signal_infos[i].handler_id);
+      signal_infos[i].handler_id = 0;
+    }
+  }
+
+  return -EINVAL;
+}
+
+/**
+ * @brief Disconnects the callback functions from the particular DBus interface.
+ */
+void
+gdbus_disconnect_signal (gpointer instance, int num_signals,
+    struct gdbus_signal_info *signal_infos)
+{
+  int i;
+
+  for (i = 0; i < num_signals; i++) {
+    if (signal_infos[i].handler_id > 0) {
+      g_signal_handler_disconnect (instance, signal_infos[i].handler_id);
+      signal_infos[i].handler_id = 0;
+    }
+  }
+}
+
+/**
+ * @brief Connect to the DBus message bus, which type is SYSTEM.
+ */
+int
+gdbus_get_system_connection (gboolean is_session)
+{
+  GError *err = NULL;
+  GBusType bus_type = is_session ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM;
+
+  g_dbus_sys_conn = g_bus_get_sync (bus_type, NULL, &err);
+  if (g_dbus_sys_conn == NULL) {
+    _E ("Cannot connect to the system message bus: %s", err ? err->message : "Unknown error");
+    g_clear_error (&err);
+    return -ENOSYS;
+  }
+
+  return 0;
+}
+
+/**
+ * @brief Disconnect the DBus message bus.
+ */
+void
+gdbus_put_system_connection (void)
+{
+  g_clear_object (&g_dbus_sys_conn);
+}
+
+/**
+ * @brief Common function to initialize the DBus module.
+ */
+void
+gdbus_initialize (void)
+{
+  GError *err = NULL;
+
+  if (!gst_init_check (NULL, NULL, &err))
+    _E ("Failed to initialize GStreamer: %s", (err ? err->message : "Unknown error"));
+
+  g_clear_error (&err);
+}
diff --git a/daemon/gdbus-util.h b/daemon/gdbus-util.h
new file mode 100644 (file)
index 0000000..bb09d06
--- /dev/null
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    gdbus-util.h
+ * @date    25 June 2022
+ * @brief   Internal GDbus utility header of Machine Learning agent daemon
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This provides the wrapper functions to use DBus easily.
+ */
+#ifndef __GDBUS_UTIL_H__
+#define __GDBUS_UTIL_H__
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief DBus signal handler information to connect
+ */
+struct gdbus_signal_info
+{
+  const gchar *signal_name; /**< specific signal name to handle */
+  GCallback cb;         /**< Callback function to connect */
+  gpointer cb_data;     /**< Data to pass to callback function */
+  gulong handler_id;    /**< Connected handler ID */
+};
+
+/**
+ * @brief Export the DBus interface at the Object path on the bus connection.
+ * @param instance The instance of the DBus interface to export.
+ * @param obj_path The path to export the interface at.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int gdbus_export_interface (gpointer instance, const char *obj_path);
+
+/**
+ * @brief Acquire the given name on the SYSTEM session of the DBus message bus.
+ * @remarks If the name is acquired, 'READY=1' signal will be sent to the systemd.
+ * @param name The well-known name to own.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int gdbus_get_name (const char *name);
+
+/**
+ * @brief Connects the callback functions for each signal of the particular DBus interface.
+ * @param instance The instance of the DBus interface.
+ * @param num_signals The number of signals to connect.
+ * @param signal_infos The array of DBus signal handler.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int gdbus_connect_signal (gpointer instance, int num_signals,
+    struct gdbus_signal_info *signal_infos);
+
+/**
+ * @brief Disconnects the callback functions from the particular DBus interface.
+ * @param instance The instance of the DBus interface.
+ * @param num_signals The number of signals to connect.
+ * @param signal_infos The array of DBus signal handler.
+ */
+void gdbus_disconnect_signal (gpointer instance, int num_signals,
+    struct gdbus_signal_info *signal_infos);
+
+/**
+ * @brief Connect to the DBus message bus
+ * @param is_session True is DBus Bus type is session.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int gdbus_get_system_connection (gboolean is_session);
+
+/**
+ * @brief Disconnect the DBus message bus.
+ */
+void gdbus_put_system_connection (void);
+
+/**
+ * @brief Common function to initialize the DBus module.
+ */
+void gdbus_initialize (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __GDBUS_UTIL_H__ */
diff --git a/daemon/include/meson.build b/daemon/include/meson.build
new file mode 100755 (executable)
index 0000000..8f4738b
--- /dev/null
@@ -0,0 +1,5 @@
+ml_agent_headers = files('ml-agent-interface.h')
+
+install_headers(ml_agent_headers,
+  subdir: 'ml-agent'
+)
diff --git a/daemon/include/ml-agent-interface.h b/daemon/include/ml-agent-interface.h
new file mode 100755 (executable)
index 0000000..52bc726
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * @file    ml-agent-interface.h
+ * @date    5 April 2023
+ * @brief   A set of exported ml-agent interfaces for managing pipelines, models, and other service.
+ * @see     https://github.com/nnstreamer/api
+ * @author  Wook Song <wook16.song@samsung.com>
+ * @bug     No known bugs except for NYI items
+ */
+
+#ifndef __ML_AGENT_INTERFACE_H__
+#define __ML_AGENT_INTERFACE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <glib.h>
+
+/**
+ * @brief An interface exported for setting the description of a pipeline
+ * @param[in] name A name indicating the pipeline whose description would be set
+ * @param[in] pipeline_desc A stringified description of the pipeline
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_set_description (const gchar *name, const gchar *pipeline_desc, GError **err);
+
+/**
+ * @brief An interface exported for getting the pipeline's description corresponding to the given @name
+ * @param[in] name A given name of the pipeline to get the description
+ * @param[out] pipeline_desc A stringified description of the pipeline
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_get_description (const gchar *name, gchar **pipeline_desc, GError **err);
+
+/**
+ * @brief An interface exported for deletion of the pipeline's description corresponding to the given @name
+ * @param[in] name A given name of the pipeline to remove the description
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_delete (const gchar *name, GError **err);
+
+/**
+ * @brief An interface exported for launching the pipeline's description corresponding to the given @name
+ * @param[in] name A given name of the pipeline to launch
+ * @param[out] id Return an integer identifier for the launched pipeline
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_launch (const gchar *name, gint64 *id, GError **err);
+
+/**
+ * @brief An interface exported for changing the pipeline's state of the given @id to start
+ * @param[in] id An identifier of the launched pipeline whose state would be changed to start
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_start (gint64 id, GError **err);
+
+/**
+ * @brief An interface exported for changing the pipeline's state of the given @id to stop
+ * @param[in] id An identifier of the launched pipeline whose state would be changed to stop
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_stop (gint64 id, GError **err);
+
+/**
+ * @brief An interface exported for destroying a launched pipeline corresponding to the given @id
+ * @param[in] id An identifier of the launched pipeline that would be destroyed
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_destroy (gint64 id, GError **err);
+
+/**
+ * @brief An interface exported for getting the pipeline's state of the given @id
+ * @param[in] id An identifier of the launched pipeline that would be destroyed
+ * @param[out] state Return location for the pipeline's state
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_pipeline_get_state (gint64 id, gint *state, GError **err);
+
+/**
+ * @brief An interface exported for registering a model
+ * @param[in] name A name indicating the model that would be registered
+ * @param[in] path A path that specifies the location of the model file
+ * @param[in] activate An initial activation state
+ * @param[in] description A stringified description of the given model
+ * @param[in] app_info Application-specific information from Tizen's RPK
+ * @param[out] version Return location for the version of the given model registered
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_register(const gchar *name, const gchar *path, const gboolean activate, const gchar *description, const gchar *app_info, guint *version, GError **err);
+
+/**
+ * @brief An interface exported for updating the description of the given model, @name
+ * @param[in] name A name indicating the model whose description would be updated
+ * @param[in] version A version for identifying the model whose description would be updated
+ * @param[in] description A new description to update the existing one
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_update_description (const gchar *name, const guint version, const gchar *description, GError **err);
+
+/**
+ * @brief An interface exported for activating the model of @name and @version
+ * @param[in] name A name indicating a registered model
+ * @param[in] version A version of the given model, @name
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_activate(const gchar *name, const guint version, GError **err);
+
+/**
+ * @brief An interface exported for getting the description of the given model of @name and @version
+ * @param[in] name A name indicating the model whose description would be get
+ * @param[in] version A version of the given model, @name
+ * @param[out] description Return location for the description of the given model of @name and @version
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_get(const gchar *name, const guint version, gchar **description, GError **err);
+
+/**
+ * @brief An interface exported for getting the description of the activated model of the given @name
+ * @param[in] name A name indicating the model whose description would be get
+ * @param[out] description Return location for the description of an activated model of the given @name
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_get_activated(const gchar *name, gchar **description, GError **err);
+
+/**
+ * @brief An interface exported for getting the description of all the models corresponding to the given @name
+ * @param[in] name A name indicating the models whose description would be get
+ * @param[out] description Return location for the description of all the models corresponding to the given @name
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_get_all(const gchar *name, gchar **description, GError **err);
+
+/**
+ * @brief An interface exported for removing the model of @name and @version
+ * @param[in] name A name indicating the model that would be removed
+ * @param[in] version A version for identifying a specific model whose name is @name
+ * @param[in] description A new description to update the existing one
+ * @param[out] err Return location for error of NULL
+ * @return 0 on success, a negative error value if @err is set
+ */
+gint ml_agent_model_delete(const gchar *name, const guint version, GError **err);
+
+/**
+ * @brief An interface exported for adding the resource
+ * @param[in] name A name indicating the resource
+ * @param[in] path A path that specifies the location of the resource
+ * @param[in] description A stringified description of the resource
+ * @param[in] app_info Application-specific information from Tizen's RPK
+ * @param[out] err Return location for error
+ * @return 0 on success, a negative error value if failed
+ */
+gint ml_agent_resource_add (const gchar *name, const gchar *path, const gchar *description, const gchar *app_info, GError **err);
+
+/**
+ * @brief An interface exported for removing the resource with @name
+ * @param[in] name A name indicating the resource
+ * @param[out] err Return location for error
+ * @return 0 on success, a negative error value if failed
+ */
+gint ml_agent_resource_delete (const gchar *name, GError **err);
+
+/**
+ * @brief An interface exported for getting the description of the resource with @name
+ * @param[in] name A name indicating the resource
+ * @param[out] res_info Return location for the information of the resource
+ * @param[out] err Return location for error
+ * @return 0 on success, a negative error value if failed
+ */
+gint ml_agent_resource_get (const gchar *name, gchar **res_info, GError **err);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __ML_AGENT_INTERFACE_H__ */
diff --git a/daemon/log.h b/daemon/log.h
new file mode 100644 (file)
index 0000000..186f5a9
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    log.h
+ * @date    25 June 2022
+ * @brief   Internal log header of Machine Learning agent daemon
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This provides the log macro for Machine Learning agent daemon.
+ */
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#if defined(__TIZEN__)
+#include <dlog.h>
+
+#define AGENT_LOG_TAG "ml-agent"
+
+#define LOG_V(prio, tag, fmt, arg...) \
+  ({ do { \
+    dlog_print(prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg);\
+  } while (0); })
+
+#define _D(fmt, arg...)                LOG_V(DLOG_DEBUG, AGENT_LOG_TAG, fmt, ##arg)
+#define _I(fmt, arg...)                LOG_V(DLOG_INFO, AGENT_LOG_TAG, fmt, ##arg)
+#define _W(fmt, arg...)                LOG_V(DLOG_WARN, AGENT_LOG_TAG, fmt, ##arg)
+#define _E(fmt, arg...)                LOG_V(DLOG_ERROR, AGENT_LOG_TAG, fmt, ##arg)
+#define _F(fmt, arg...)                LOG_V(DLOG_FATAL, AGENT_LOG_TAG, fmt, ##arg)
+#else
+#include <glib.h>
+
+#define _D g_debug
+#define _I g_info
+#define _W g_warning
+#define _E g_critical
+#define _F g_error
+#endif
+
+#endif /* __LOG_H__ */
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644 (file)
index 0000000..7348827
--- /dev/null
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file main.c
+ * @date 25 June 2022
+ * @brief core module for the Machine Learning agent daemon
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <errno.h>
+
+#include "common.h"
+#include "modules.h"
+#include "gdbus-util.h"
+#include "log.h"
+#include "dbus-interface.h"
+#include "pkg-mgr.h"
+
+static GMainLoop *g_mainloop = NULL;
+static gboolean verbose = FALSE;
+static gboolean is_session = FALSE;
+
+/**
+ * @brief Handle the SIGTERM signal and quit the main loop
+ */
+static void
+handle_sigterm (int signo)
+{
+  _D ("received SIGTERM signal %d", signo);
+  g_main_loop_quit (g_mainloop);
+}
+
+/**
+ * @brief Handle the post init tasks before starting the main loop.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+static int
+postinit (void)
+{
+  int ret;
+  /** Register signal handler */
+  signal (SIGTERM, handle_sigterm);
+
+  ret = gdbus_get_name (DBUS_ML_BUS_NAME);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+/**
+ * @brief Parse commandline option.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+static int
+parse_args (gint *argc, gchar ***argv)
+{
+  GError *err;
+  GOptionContext *context = NULL;
+  gboolean ret;
+
+  static GOptionEntry entries[] = {
+    { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL },
+    { "session", 's', 0, G_OPTION_ARG_NONE, &is_session, "Bus type is session", NULL },
+    { NULL }
+  };
+
+  context = g_option_context_new (NULL);
+  if (!context) {
+    _E ("Failed to call g_option_context_new\n");
+    return -ENOMEM;
+  }
+
+  g_option_context_add_main_entries (context, entries, NULL);
+  g_option_context_set_help_enabled (context, TRUE);
+  g_option_context_set_ignore_unknown_options (context, TRUE);
+
+  err = NULL;
+  ret = g_option_context_parse (context, argc, argv, &err);
+  g_option_context_free (context);
+  if (!ret) {
+    _E ("Fail to option parsing: %s", err->message);
+    g_clear_error (&err);
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+/**
+ * @brief main function of the Machine Learning agent daemon.
+ */
+int
+main (int argc, char **argv)
+{
+  if (parse_args (&argc, &argv)) {
+    return -EINVAL;
+  }
+
+  g_mainloop = g_main_loop_new (NULL, FALSE);
+  gdbus_get_system_connection (is_session);
+
+  init_modules (NULL);
+  if (postinit () < 0)
+    _E ("cannot init system");
+
+  /* Register package manager callback */
+  if (pkg_mgr_init () < 0) {
+    _E ("cannot init package manager");
+  }
+
+  g_main_loop_run (g_mainloop);
+  exit_modules (NULL);
+
+  gdbus_put_system_connection ();
+  g_main_loop_unref (g_mainloop);
+  g_mainloop = NULL;
+
+  if (pkg_mgr_deinit () < 0)
+    _W ("cannot finalize package manager");
+
+  return 0;
+}
diff --git a/daemon/meson.build b/daemon/meson.build
new file mode 100644 (file)
index 0000000..e7dfd76
--- /dev/null
@@ -0,0 +1,101 @@
+# Machine Learning Agent
+ml_agent_incs = include_directories('.', 'include')
+ml_agent_lib_common_srcs = files('modules.c', 'gdbus-util.c', 'ml-agent-interface.c',
+  'pipeline-dbus-impl.cc', 'model-dbus-impl.cc', 'resource-dbus-impl.cc')
+ml_agent_lib_service_db_srcs = files('service-db.cc')
+
+if (get_option('enable-tizen'))
+  ml_agent_lib_common_srcs += files('pkg-mgr.cc')
+endif
+
+ml_agent_deps = [
+  gdbus_gen_header_dep,
+  glib_dep,
+  gio_dep,
+  gst_dep,
+  sqlite_dep,
+  libsystemd_dep,
+  json_glib_dep
+]
+
+if (get_option('enable-tizen'))
+  ml_agent_deps += [
+    dependency('capi-appfw-app-common'),
+    dependency('capi-appfw-package-manager'),
+    dependency('dlog')
+  ]
+endif
+
+serviceDBPath = get_option('service-db-path')
+ml_agent_db_path_arg = '-DDB_PATH="' + serviceDBPath + '"'
+
+serviceDBKeyPrefix = get_option('service-db-key-prefix')
+ml_agent_db_key_prefix_arg = '-DMESON_KEY_PREFIX="' + serviceDBKeyPrefix + '"'
+
+ml_agent_lib_srcs = [ml_agent_lib_common_srcs, ml_agent_lib_service_db_srcs]
+ml_agent_shared_lib = shared_library ('ml-agent',
+  ml_agent_lib_srcs,
+  dependencies: ml_agent_deps,
+  include_directories: ml_agent_incs,
+  install: true,
+  install_dir: ml_agent_install_libdir,
+  cpp_args: [ml_agent_db_path_arg, ml_agent_db_key_prefix_arg],
+  version: ml_agent_version,
+)
+
+ml_agent_static_lib = static_library('ml-agent',
+  ml_agent_lib_srcs,
+  dependencies: ml_agent_deps,
+  include_directories: ml_agent_incs,
+  install: true,
+  install_dir: ml_agent_install_libdir,
+  cpp_args: [ml_agent_db_path_arg, ml_agent_db_key_prefix_arg],
+  pic: true,
+)
+
+ml_agent_lib = ml_agent_shared_lib
+if get_option('default_library') == 'static'
+  ml_agent_lib = ml_agent_static_lib
+endif
+
+ml_agent_lib_common_objs = ml_agent_shared_lib.extract_objects(ml_agent_lib_common_srcs)
+ml_agent_test_both_lib = both_libraries('ml-agent-test',
+  ml_agent_lib_service_db_srcs,
+  dependencies: ml_agent_deps,
+  include_directories: ml_agent_incs,
+  install: get_option('install-test'),
+  install_dir: ml_agent_install_libdir,
+  cpp_args: ['-DDB_PATH="."', ml_agent_db_key_prefix_arg],
+  objects: ml_agent_lib_common_objs,
+  version: ml_agent_version,
+  pic: true,
+)
+
+ml_agent_test_lib = ml_agent_test_both_lib.get_shared_lib()
+if get_option('default_library') == 'static'
+  ml_agent_test_lib = ml_agent_test_both_lib.get_static_lib()
+endif
+
+ml_agent_test_dep = declare_dependency(
+  dependencies: ml_agent_deps,
+  include_directories: ml_agent_incs,
+  link_with: ml_agent_test_lib,
+)
+
+ml_agent_main_file = files('main.c')
+ml_agent_executable = executable('machine-learning-agent',
+  ml_agent_main_file,
+  link_with: ml_agent_lib,
+  dependencies: ml_agent_deps,
+  include_directories: ml_agent_incs,
+  install: true,
+  install_dir: ml_agent_install_bindir,
+  pie: true
+)
+
+configure_file(input: 'ml-agent.pc.in', output: 'ml-agent.pc',
+  install_dir: join_paths(ml_agent_install_libdir, 'pkgconfig'),
+  configuration: ml_agent_conf
+)
+
+subdir('include')
diff --git a/daemon/ml-agent-interface.c b/daemon/ml-agent-interface.c
new file mode 100755 (executable)
index 0000000..7a3b32a
--- /dev/null
@@ -0,0 +1,580 @@
+/**
+ * @file    ml-agent-interface.c
+ * @date    5 April 2023
+ * @brief   A set of exported ml-agent interfaces for managing pipelines, models, and other service.
+ * @see     https://github.com/nnstreamer/api
+ * @author  Wook Song <wook16.song@samsung.com>
+ * @bug     No known bugs except for NYI items
+ */
+
+#include <errno.h>
+#include <glib.h>
+
+#include "include/ml-agent-interface.h"
+#include "dbus-interface.h"
+#include "model-dbus.h"
+#include "pipeline-dbus.h"
+#include "resource-dbus.h"
+
+typedef enum
+{
+  ML_AGENT_SERVICE_PIPELINE = 0,
+  ML_AGENT_SERVICE_MODEL,
+  ML_AGENT_SERVICE_RESOURCE,
+  ML_AGENT_SERVICE_END
+} ml_agent_service_type_e;
+
+typedef gpointer ml_agent_proxy_h;
+
+/**
+ * @brief An internal helper to get the dbus proxy
+ */
+static ml_agent_proxy_h
+_get_proxy_new_for_bus_sync (ml_agent_service_type_e type, GError ** err)
+{
+  static const GBusType bus_types[] = { G_BUS_TYPE_SYSTEM, G_BUS_TYPE_SESSION };
+  static const size_t num_bus_types =
+      sizeof (bus_types) / sizeof (bus_types[0]);
+  ml_agent_proxy_h *proxy = NULL;
+  GError *_err = NULL;
+  size_t i;
+
+  switch (type) {
+    case ML_AGENT_SERVICE_PIPELINE:
+    {
+      MachinelearningServicePipeline *mlsp;
+
+      for (i = 0; i < num_bus_types; ++i) {
+        g_clear_error (&_err);
+        mlsp =
+            machinelearning_service_pipeline_proxy_new_for_bus_sync (bus_types
+            [i], G_DBUS_PROXY_FLAGS_NONE, DBUS_ML_BUS_NAME, DBUS_PIPELINE_PATH,
+            NULL, &_err);
+        if (mlsp) {
+          break;
+        }
+      }
+      proxy = (ml_agent_proxy_h *) mlsp;
+      break;
+    }
+    case ML_AGENT_SERVICE_MODEL:
+    {
+      MachinelearningServiceModel *mlsm;
+
+      for (i = 0; i < num_bus_types; ++i) {
+        g_clear_error (&_err);
+
+        mlsm =
+            machinelearning_service_model_proxy_new_for_bus_sync (bus_types[i],
+            G_DBUS_PROXY_FLAGS_NONE, DBUS_ML_BUS_NAME, DBUS_MODEL_PATH, NULL,
+            &_err);
+        if (mlsm)
+          break;
+      }
+      proxy = (ml_agent_proxy_h *) mlsm;
+      break;
+    }
+    case ML_AGENT_SERVICE_RESOURCE:
+    {
+      MachinelearningServiceResource *mlsr;
+
+      for (i = 0; i < num_bus_types; ++i) {
+        g_clear_error (&_err);
+
+        mlsr =
+            machinelearning_service_resource_proxy_new_for_bus_sync (bus_types[i],
+            G_DBUS_PROXY_FLAGS_NONE, DBUS_ML_BUS_NAME, DBUS_RESOURCE_PATH, NULL,
+            &_err);
+        if (mlsr)
+          break;
+      }
+      proxy = (ml_agent_proxy_h *) mlsr;
+      break;
+    }
+    default:
+      break;
+  }
+
+  if (_err) {
+    *err = g_error_copy (_err);
+    g_clear_error (&_err);
+  }
+
+  return proxy;
+}
+
+/**
+ * @brief An interface exported for setting the description of a pipeline
+ */
+gint
+ml_agent_pipeline_set_description (const gchar * name,
+    const gchar * pipeline_desc, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+
+  if (!name || !pipeline_desc) {
+    g_return_val_if_reached (-EINVAL);
+  }
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_set_pipeline_sync
+      (MACHINELEARNING_SERVICE_PIPELINE (mlsp), name, pipeline_desc, NULL, NULL,
+      err);
+  g_object_unref (mlsp);
+  g_return_val_if_fail (result, -EIO);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for getting the pipeline's description corresponding to the given @name
+ */
+gint
+ml_agent_pipeline_get_description (const gchar * name,
+    gchar ** pipeline_desc, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+  gint ret;
+
+  if (!name || !pipeline_desc) {
+    g_return_val_if_reached (-EINVAL);
+  }
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_get_pipeline_sync
+      (MACHINELEARNING_SERVICE_PIPELINE (mlsp), name, &ret, pipeline_desc, NULL,
+      err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for deletion of the pipeline's description corresponding to the given @name
+ */
+gint
+ml_agent_pipeline_delete (const gchar * name, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+  gint ret;
+
+  if (!name) {
+    g_return_val_if_reached (-EINVAL);
+  }
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_delete_pipeline_sync
+      (MACHINELEARNING_SERVICE_PIPELINE (mlsp), name, &ret, NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for launching the pipeline's description corresponding to the given @name
+ */
+gint
+ml_agent_pipeline_launch (const gchar * name, gint64 * id, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+  gint ret;
+
+  if (!name) {
+    g_return_val_if_reached (-EINVAL);
+  }
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_launch_pipeline_sync (mlsp, name,
+      &ret, id, NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for changing the pipeline's state of the given @id to start
+ */
+gint
+ml_agent_pipeline_start (gint64 id, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+  gint ret;
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_start_pipeline_sync (mlsp, id, &ret,
+      NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for changing the pipeline's state of the given @id to stop
+ */
+gint
+ml_agent_pipeline_stop (gint64 id, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+  gboolean result;
+  gint ret;
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_stop_pipeline_sync (mlsp, id, &ret,
+      NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for destroying a launched pipeline corresponding to the given @id
+ */
+gint
+ml_agent_pipeline_destroy (gint64 id, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+
+  gboolean result;
+  gint ret;
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_destroy_pipeline_sync (mlsp, id,
+      &ret, NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for getting the pipeline's state of the given @id
+ */
+gint
+ml_agent_pipeline_get_state (gint64 id, gint * state, GError ** err)
+{
+  MachinelearningServicePipeline *mlsp;
+
+  gboolean result;
+  gint ret;
+
+  mlsp = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_PIPELINE, err);
+  if (!mlsp) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_pipeline_call_get_state_sync (mlsp, id, &ret,
+      state, NULL, err);
+  g_object_unref (mlsp);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for registering a model
+ */
+gint
+ml_agent_model_register (const gchar * name, const gchar * path,
+    const gboolean activate, const gchar * description, const gchar * app_info,
+    guint * version, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result = machinelearning_service_model_call_register_sync (mlsm, name, path,
+      activate, description ? description : "", app_info ? app_info : "",
+      version, &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for updating the description of the given model, @name
+ */
+gint
+ml_agent_model_update_description (const gchar * name,
+    const guint version, const gchar * description, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_update_description_sync (mlsm, name,
+      version, description, &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for activating the model of @name and @version
+ */
+gint
+ml_agent_model_activate (const gchar * name, const guint version, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_activate_sync (mlsm, name, version,
+      &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for getting the description of the given model of @name and @version
+ */
+gint
+ml_agent_model_get (const gchar * name, const guint version,
+    gchar ** description, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_get_sync (mlsm, name, version,
+      description, &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for getting the description of the activated model of the given @name
+ */
+gint
+ml_agent_model_get_activated (const gchar * name,
+    gchar ** description, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_get_activated_sync (mlsm, name,
+      description, &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief  An interface exported for getting the description of all the models corresponding to the given @name
+ */
+gint
+ml_agent_model_get_all (const gchar * name, gchar ** description, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_get_all_sync (mlsm, name, description,
+      &ret, NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for removing the model of @name and @version
+ */
+gint
+ml_agent_model_delete (const gchar * name, const guint version, GError ** err)
+{
+  MachinelearningServiceModel *mlsm;
+  gboolean result;
+  gint ret;
+
+  mlsm = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_MODEL, err);
+  if (!mlsm) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result =
+      machinelearning_service_model_call_delete_sync (mlsm, name, version, &ret,
+      NULL, err);
+  g_object_unref (mlsm);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+
+  return 0;
+}
+
+/**
+ * @brief An interface exported for adding the resource
+ */
+gint
+ml_agent_resource_add (const gchar * name, const gchar * path,
+    const gchar * description, const gchar * app_info, GError ** err)
+{
+  MachinelearningServiceResource *mlsr;
+
+  gboolean result;
+  gint ret;
+
+  mlsr = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_RESOURCE, err);
+  if (!mlsr) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result = machinelearning_service_resource_call_add_sync (mlsr, name, path,
+      description ? description : "", app_info ? app_info : "",
+      &ret, NULL, err);
+  g_object_unref (mlsr);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+  return 0;
+}
+
+/**
+ * @brief An interface exported for removing the resource with @name
+ */
+gint
+ml_agent_resource_delete (const gchar * name, GError ** err)
+{
+  MachinelearningServiceResource *mlsr;
+  gboolean result;
+  gint ret;
+
+  mlsr = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_RESOURCE, err);
+  if (!mlsr) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result = machinelearning_service_resource_call_delete_sync (mlsr, name, &ret,
+      NULL, err);
+  g_object_unref (mlsr);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+  return 0;
+}
+
+/**
+ * @brief An interface exported for getting the description of the resource with @name
+ */
+gint
+ml_agent_resource_get (const gchar * name, gchar ** res_info, GError ** err)
+{
+  MachinelearningServiceResource *mlsr;
+  gboolean result;
+  gint ret;
+
+  mlsr = _get_proxy_new_for_bus_sync (ML_AGENT_SERVICE_RESOURCE, err);
+  if (!mlsr) {
+    g_return_val_if_reached (-EIO);
+  }
+
+  result = machinelearning_service_resource_call_get_sync (mlsr, name,
+      res_info, &ret, NULL, err);
+  g_object_unref (mlsr);
+
+  g_return_val_if_fail (ret == 0 && result, ret);
+  return 0;
+}
diff --git a/daemon/ml-agent.pc.in b/daemon/ml-agent.pc.in
new file mode 100755 (executable)
index 0000000..f273253
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@PREFIX@
+exec_prefix=@PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: ml-agent-service
+Description: Development headers and libraries for interfaces provided by Machine Learning Agent Service
+Version: @VERSION@
+Libs: -L${libdir} -lml-agent
+Cflags: -I${includedir}/ml-agent
diff --git a/daemon/model-dbus-impl.cc b/daemon/model-dbus-impl.cc
new file mode 100644 (file)
index 0000000..484d318
--- /dev/null
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file model-dbus-impl.cc
+ * @date 29 Jul 2022
+ * @brief DBus implementation for Model Interface
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <errno.h>
+#include <glib.h>
+
+#include "common.h"
+#include "dbus-interface.h"
+#include "gdbus-util.h"
+#include "log.h"
+#include "model-dbus.h"
+#include "modules.h"
+#include "service-db.hh"
+
+static MachinelearningServiceModel *g_gdbus_instance = NULL;
+
+/**
+ * @brief Utility function to get the DBus proxy of Model interface.
+ */
+static MachinelearningServiceModel *
+gdbus_get_model_instance (void)
+{
+  return machinelearning_service_model_skeleton_new ();
+}
+
+/**
+ * @brief Utility function to release DBus proxy of Model interface.
+ */
+static void
+gdbus_put_model_instance (MachinelearningServiceModel **instance)
+{
+  g_clear_object (instance);
+}
+
+/**
+ * @brief The callback function of Register method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @param path The file path of target.
+ * @param is_active The active status of target model.
+ * @param description The description of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_register (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name, const gchar *path,
+    const bool is_active, const gchar *description, const gchar *app_info)
+{
+  int ret = 0;
+  guint version = 0U;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.set_model (name, path, is_active, description, app_info, &version);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_register (obj, invoc, version, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of update description method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @param version The version of target model.
+ * @param description The description of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_update_description (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name, const guint version,
+    const gchar *description)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.update_model_description (name, version, description);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_update_description (obj, invoc, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of activate method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @param version The version of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_activate (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name, const guint version)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.activate_model (name, version);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_activate (obj, invoc, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of get method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_get (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name, const guint version)
+{
+  int ret = 0;
+  std::string model_info;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.get_model (name, model_info, version);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_get (obj, invoc, model_info.c_str (), ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of get activated method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_get_activated (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name)
+{
+  int ret = 0;
+  std::string model_info;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.get_model (name, model_info, -1);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_get_activated (
+      obj, invoc, model_info.c_str (), ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of get all method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_get_all (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  std::string all_model_list;
+
+  try {
+    db.connectDB ();
+    db.get_model (name, all_model_list, 0);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+
+  machinelearning_service_model_complete_get_all (obj, invoc, all_model_list.c_str (), ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of delete method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @param version The version of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_delete (MachinelearningServiceModel *obj,
+    GDBusMethodInvocation *invoc, const gchar *name, const guint version)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.delete_model (name, version);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_model_complete_delete (obj, invoc, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief Event handler list of Model interface
+ */
+static struct gdbus_signal_info handler_infos[] = {
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_REGISTER,
+      .cb = G_CALLBACK (gdbus_cb_model_register),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_UPDATE_DESCRIPTION,
+      .cb = G_CALLBACK (gdbus_cb_model_update_description),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_ACTIVATE,
+      .cb = G_CALLBACK (gdbus_cb_model_activate),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_GET,
+      .cb = G_CALLBACK (gdbus_cb_model_get),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_GET_ACTIVATED,
+      .cb = G_CALLBACK (gdbus_cb_model_get_activated),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_GET_ALL,
+      .cb = G_CALLBACK (gdbus_cb_model_get_all),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_MODEL_I_HANDLER_DELETE,
+      .cb = G_CALLBACK (gdbus_cb_model_delete),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+};
+
+/**
+ * @brief The callback function for probing Model Interface module.
+ */
+static int
+probe_model_module (void *data)
+{
+  int ret = 0;
+  _D ("probe_model_module");
+
+  g_gdbus_instance = gdbus_get_model_instance ();
+  if (NULL == g_gdbus_instance) {
+    _E ("cannot get a dbus instance for the %s interface\n", DBUS_MODEL_INTERFACE);
+    return -ENOSYS;
+  }
+
+  ret = gdbus_connect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+  if (ret < 0) {
+    _E ("cannot register callbacks as the dbus method invocation handlers\n ret: %d", ret);
+    ret = -ENOSYS;
+    goto out;
+  }
+
+  ret = gdbus_export_interface (g_gdbus_instance, DBUS_MODEL_PATH);
+  if (ret < 0) {
+    _E ("cannot export the dbus interface '%s' at the object path '%s'\n",
+        DBUS_MODEL_INTERFACE, DBUS_MODEL_PATH);
+    ret = -ENOSYS;
+    goto out_disconnect;
+  }
+
+  return 0;
+
+out_disconnect:
+  gdbus_disconnect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+
+out:
+  gdbus_put_model_instance (&g_gdbus_instance);
+
+  return ret;
+}
+
+/**
+ * @brief The callback function for initializing Model Interface module.
+ */
+static void
+init_model_module (void *data)
+{
+  gdbus_initialize ();
+}
+
+/**
+ * @brief The callback function for exiting Model Interface module.
+ */
+static void
+exit_model_module (void *data)
+{
+  gdbus_disconnect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+  gdbus_put_model_instance (&g_gdbus_instance);
+}
+
+static const struct module_ops model_ops = {
+  .name = "model-interface",
+  .probe = probe_model_module,
+  .init = init_model_module,
+  .exit = exit_model_module,
+};
+
+MODULE_OPS_REGISTER (&model_ops)
diff --git a/daemon/modules.c b/daemon/modules.c
new file mode 100644 (file)
index 0000000..9f85527
--- /dev/null
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file modules.c
+ * @date 25 June 2022
+ * @brief NNStreamer/Utilities C-API Wrapper.
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <glib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "modules.h"
+#include "log.h"
+
+static GList *module_head = NULL;
+
+/**
+ * @brief Add the specific DBus interface into the Machine Learning agent daemon.
+ */
+void
+add_module (const struct module_ops *module)
+{
+  module_head = g_list_append (module_head, (gpointer) module);
+}
+
+/**
+ * @brief Remove the specific DBus interface from the Machine Learning agent daemon.
+ */
+void
+remove_module (const struct module_ops *module)
+{
+  module_head = g_list_remove (module_head, (gconstpointer) module);
+}
+
+/**
+ * @brief Initialize all added modules by calling probe and init callback functions.
+ */
+void
+init_modules (void *data)
+{
+  GList *elem, *elem_n;
+  const struct module_ops *module;
+
+  elem = module_head;
+  while (elem != NULL) {
+    module = elem->data;
+    elem_n = elem->next;
+
+    if (module->probe && module->probe (data) != 0) {
+      _E ("[%s] probe fail", module->name);
+      module_head = g_list_remove (module_head, (gconstpointer) module);
+      elem = elem_n;
+      continue;
+    }
+
+    if (module->init)
+      module->init (data);
+    elem = elem_n;
+  }
+}
+
+/**
+ * @brief Clean up all added modules by calling the exit callback function.
+ */
+void
+exit_modules (void *data)
+{
+  GList *elem;
+  const struct module_ops *module;
+
+  for (elem = module_head; elem != NULL; elem = elem->next) {
+    module = elem->data;
+    if (module->exit)
+      module->exit (data);
+  }
+}
diff --git a/daemon/modules.h b/daemon/modules.h
new file mode 100644 (file)
index 0000000..eb8d2b0
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    modules.h
+ * @date    25 June 2022
+ * @brief   Internal module utility header of Machine Learning agent daemon
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This provides the DBus module utility functions for the Machine Learning agent daemon.
+ */
+#ifndef __MODULES_H__
+#define __MODULES_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief Data structure contains the name and callback functions for a specific DBus interface.
+ */
+struct module_ops
+{
+  const char *name;     /**< Name of DBus Interface. */
+  int (*probe) (void *data);    /**< Callback function for probing the DBus Interface */
+  void (*init) (void *data);    /**< Callback function for initializing the DBus Interface */
+  void (*exit) (void *data);    /**< Callback function for exiting the DBus Interface */
+};
+
+/**
+ * @brief Utility macro for adding and removing the specific DBus interface.
+ */
+#define MODULE_OPS_REGISTER(module)    \
+static void __CONSTRUCTOR__ module_init(void)  \
+{      \
+  add_module (module); \
+}      \
+static void __DESTRUCTOR__ module_exit(void)   \
+{      \
+  remove_module (module);      \
+}
+
+/**
+ * @brief Initialize all added modules by calling probe and init callback functions.
+ * @param[in/out] data user data for passing the callback functions.
+ */
+void init_modules (void *data);
+
+/**
+ * @brief Clean up all added modules by calling the exit callback function.
+ * @param[in/out] data user data for passing the callback functions.
+ */
+void exit_modules (void *data);
+
+/**
+ * @brief Add the specific DBus interface into the Machine Learning agent daemon.
+ * @param[in] module DBus interface information.
+ */
+void add_module (const struct module_ops *module);
+
+/**
+ * @brief Remove the specific DBus interface from the Machine Learning agent daemon.
+ * @param[in] module DBus interface information.
+ */
+void remove_module (const struct module_ops *module);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __MODULES_H__ */
diff --git a/daemon/pipeline-dbus-impl.cc b/daemon/pipeline-dbus-impl.cc
new file mode 100644 (file)
index 0000000..567a6b0
--- /dev/null
@@ -0,0 +1,553 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    pipeline-dbus-impl.cc
+ * @date    20 Jul 2022
+ * @brief   Implementation of pipeline dbus interface.
+ * @see     https://github.com/nnstreamer/api
+ * @author  Yongjoo Ahn <yongjoo1.ahn@samsung.com>
+ * @bug     No known bugs except for NYI items
+ * @details
+ *    This implements the pipeline dbus interface.
+ */
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "dbus-interface.h"
+#include "gdbus-util.h"
+#include "log.h"
+#include "modules.h"
+#include "pipeline-dbus.h"
+#include "service-db.hh"
+
+static MachinelearningServicePipeline *g_gdbus_instance = NULL;
+static GHashTable *pipeline_table = NULL;
+G_LOCK_DEFINE_STATIC (pipeline_table_lock);
+
+/**
+ * @brief Structure for pipeline.
+ */
+typedef struct _pipeline {
+  GstElement *element;
+  gint64 id;
+  GMutex lock;
+  gchar *service_name;
+  gchar *description;
+} pipeline_s;
+
+/**
+ * @brief Internal function to destroy pipeline instances.
+ */
+static void
+_pipeline_free (gpointer data)
+{
+  pipeline_s *p;
+
+  if (!data) {
+    _E ("internal error, the data should not be NULL");
+    return;
+  }
+
+  p = (pipeline_s *) data;
+
+  if (p->element)
+    gst_object_unref (p->element);
+
+  g_free (p->service_name);
+  g_free (p->description);
+  g_mutex_clear (&p->lock);
+
+  g_free (p);
+}
+
+/**
+ * @brief Get the skeleton object of the DBus interface.
+ */
+static MachinelearningServicePipeline *
+gdbus_get_pipeline_instance (void)
+{
+  return machinelearning_service_pipeline_skeleton_new ();
+}
+
+/**
+ * @brief Put the obtained skeleton object and release the resource.
+ */
+static void
+gdbus_put_pipeline_instance (MachinelearningServicePipeline **instance)
+{
+  g_clear_object (instance);
+}
+
+/**
+ * @brief Set the service with given description. Return the call result.
+ */
+static gboolean
+dbus_cb_core_set_pipeline (MachinelearningServicePipeline *obj, GDBusMethodInvocation *invoc,
+    const gchar *service_name, const gchar *pipeline_desc, gpointer user_data)
+{
+  gint result = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.set_pipeline (service_name, pipeline_desc);
+  } catch (const std::invalid_argument &e) {
+    _E ("An exception occurred during write to the DB. Error message: %s", e.what ());
+    result = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("An exception occurred during write to the DB. Error message: %s", e.what ());
+    result = -EIO;
+  }
+
+  db.disconnectDB ();
+
+  if (result) {
+    _E ("Failed to set pipeline description of %s", service_name);
+    machinelearning_service_pipeline_complete_set_pipeline (obj, invoc, result);
+    return TRUE;
+  }
+
+  machinelearning_service_pipeline_complete_set_pipeline (obj, invoc, result);
+
+  return TRUE;
+}
+
+/**
+ * @brief Get the pipeline description of the given service. Return the call result and the pipeline description.
+ */
+static gboolean
+dbus_cb_core_get_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, const gchar *service_name, gpointer user_data)
+{
+  gint result = 0;
+  std::string stored_pipeline_description;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.get_pipeline (service_name, stored_pipeline_description);
+  } catch (const std::invalid_argument &e) {
+    _E ("An exception occurred during read the DB. Error message: %s", e.what ());
+    result = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("An exception occurred during read the DB. Error message: %s", e.what ());
+    result = -EIO;
+  }
+
+  db.disconnectDB ();
+
+  if (result) {
+    _E ("Failed to get pipeline description of %s", service_name);
+  }
+
+  machinelearning_service_pipeline_complete_get_pipeline (
+      obj, invoc, result, stored_pipeline_description.c_str ());
+
+  return TRUE;
+}
+
+/**
+ * @brief Delete the pipeline description of the given service. Return the call result.
+ */
+static gboolean
+dbus_cb_core_delete_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, const gchar *service_name, gpointer user_data)
+{
+  gint result = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.delete_pipeline (service_name);
+  } catch (const std::invalid_argument &e) {
+    _E ("An exception occurred during delete an item in the DB. Error message: %s",
+        e.what ());
+    result = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("An exception occurred during delete an item in the DB. Error message: %s",
+        e.what ());
+    result = -EIO;
+  }
+
+  db.disconnectDB ();
+
+  if (result) {
+    _E ("Failed to delete the pipeline description of %s", service_name);
+    machinelearning_service_pipeline_complete_delete_pipeline (obj, invoc, result);
+    return TRUE;
+  }
+
+  machinelearning_service_pipeline_complete_delete_pipeline (obj, invoc, result);
+
+  return TRUE;
+}
+
+/**
+ * @brief Launch the pipeline with given description. Return the call result and its id.
+ */
+static gboolean
+dbus_cb_core_launch_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, const gchar *service_name, gpointer user_data)
+{
+  gint result = 0;
+  GError *err = NULL;
+  GstStateChangeReturn sc_ret;
+  GstElement *pipeline = NULL;
+  pipeline_s *p;
+
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  std::string stored_pipeline_description;
+
+  /** get pipeline description from the DB */
+  try {
+    db.connectDB ();
+    db.get_pipeline (service_name, stored_pipeline_description);
+  } catch (const std::invalid_argument &e) {
+    _E ("An exception occurred during read the DB. Error message: %s", e.what ());
+    result = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("An exception occurred during read the DB. Error message: %s", e.what ());
+    result = -EIO;
+  }
+
+  db.disconnectDB ();
+
+  if (result) {
+    _E ("Failed to launch pipeline of %s", service_name);
+    machinelearning_service_pipeline_complete_launch_pipeline (obj, invoc, result, -1);
+    return TRUE;
+  }
+
+  pipeline = gst_parse_launch (stored_pipeline_description.c_str (), &err);
+  if (!pipeline || err) {
+    _E ("gst_parse_launch with %s Failed. error msg: %s",
+        stored_pipeline_description.c_str (), (err) ? err->message : "unknown reason");
+    g_clear_error (&err);
+
+    if (pipeline)
+      gst_object_unref (pipeline);
+
+    result = -ESTRPIPE;
+    machinelearning_service_pipeline_complete_launch_pipeline (obj, invoc, result, -1);
+    return TRUE;
+  }
+
+  /** now set pipeline as paused state */
+  sc_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+  if (sc_ret == GST_STATE_CHANGE_FAILURE) {
+    _E ("Failed to set the state of the pipeline to PAUSED. For the detail, please check the GStreamer log message. The input pipeline was %s",
+        stored_pipeline_description.c_str ());
+
+    gst_object_unref (pipeline);
+    result = -ESTRPIPE;
+    machinelearning_service_pipeline_complete_launch_pipeline (obj, invoc, result, -1);
+    return TRUE;
+  }
+
+  /** now fill the struct and store into hash table */
+  p = g_new0 (pipeline_s, 1);
+  p->element = pipeline;
+  p->description = g_strdup (stored_pipeline_description.c_str ());
+  p->service_name = g_strdup (service_name);
+  g_mutex_init (&p->lock);
+
+  G_LOCK (pipeline_table_lock);
+  p->id = g_get_monotonic_time ();
+  g_hash_table_insert (pipeline_table, GINT_TO_POINTER (p->id), p);
+  G_UNLOCK (pipeline_table_lock);
+
+  machinelearning_service_pipeline_complete_launch_pipeline (obj, invoc, result, p->id);
+
+  return TRUE;
+}
+
+/**
+ * @brief Start the pipeline with given id. Return the call result.
+ */
+static gboolean
+dbus_cb_core_start_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, gint64 id, gpointer user_data)
+{
+  gint result = 0;
+  GstStateChangeReturn sc_ret;
+  pipeline_s *p = NULL;
+
+  G_LOCK (pipeline_table_lock);
+  p = (pipeline_s *) g_hash_table_lookup (pipeline_table, GINT_TO_POINTER (id));
+
+  if (!p) {
+    _E ("The callback start_pipeline is called, but there is no pipeline matched with ID.");
+    G_UNLOCK (pipeline_table_lock);
+    result = -EINVAL;
+  } else {
+    g_mutex_lock (&p->lock);
+    G_UNLOCK (pipeline_table_lock);
+    sc_ret = gst_element_set_state (p->element, GST_STATE_PLAYING);
+    g_mutex_unlock (&p->lock);
+
+    if (sc_ret == GST_STATE_CHANGE_FAILURE) {
+      _E ("Failed to set the state of the pipline to PLAYING whose service name is %s.",
+          p->service_name);
+      result = -ESTRPIPE;
+    }
+  }
+
+  machinelearning_service_pipeline_complete_start_pipeline (obj, invoc, result);
+
+  return TRUE;
+}
+
+/**
+ * @brief Stop the pipeline with given id. Return the call result.
+ */
+static gboolean
+dbus_cb_core_stop_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, gint64 id, gpointer user_data)
+{
+  gint result = 0;
+  GstStateChangeReturn sc_ret;
+  pipeline_s *p = NULL;
+
+  G_LOCK (pipeline_table_lock);
+  p = (pipeline_s *) g_hash_table_lookup (pipeline_table, GINT_TO_POINTER (id));
+
+  if (!p) {
+    _E ("The callback stop_pipeline is called, but there is no pipeline matched with ID.");
+    G_UNLOCK (pipeline_table_lock);
+    result = -EINVAL;
+  } else {
+    g_mutex_lock (&p->lock);
+    G_UNLOCK (pipeline_table_lock);
+    sc_ret = gst_element_set_state (p->element, GST_STATE_PAUSED);
+    g_mutex_unlock (&p->lock);
+
+    if (sc_ret == GST_STATE_CHANGE_FAILURE) {
+      _E ("Failed to set the state of the pipline to PAUSED whose service name is %s.",
+          p->service_name);
+      result = -ESTRPIPE;
+    }
+  }
+
+  machinelearning_service_pipeline_complete_stop_pipeline (obj, invoc, result);
+
+  return TRUE;
+}
+
+/**
+ * @brief Destroy the pipeline with given id. Return the call result.
+ */
+static gboolean
+dbus_cb_core_destroy_pipeline (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, gint64 id, gpointer user_data)
+{
+  gint result = 0;
+  pipeline_s *p = NULL;
+
+  G_LOCK (pipeline_table_lock);
+  p = (pipeline_s *) g_hash_table_lookup (pipeline_table, GINT_TO_POINTER (id));
+
+  if (!p) {
+    _E ("The callback destroy_pipeline is called, but there is no pipeline matched with ID.");
+    result = -EINVAL;
+  } else {
+    /**
+     * @todo Fix hanging issue when trying to set GST_STATE_NULL state for pipelines
+     * containing tensor_query_*. As a workaround, just unref the pipeline instance.
+     * To fix this issue, tensor_query elements and nnstreamer-edge should well-behavior
+     * to the state change. And it should properly free socket resources. Revive following code after then.
+     *
+     *   GstStateChangeReturn sc_ret;
+     *   g_mutex_lock (&p->lock);
+     *   sc_ret = gst_element_set_state (p->element, GST_STATE_NULL);
+     *   g_mutex_unlock (&p->lock);
+     *   if (sc_ret == GST_STATE_CHANGE_FAILURE) {
+     *     _E ("Failed to set the state of the pipeline to NULL whose service name is %s. Destroy it anyway.", p->service_name);
+     *     result = -ESTRPIPE;
+     *   }
+     */
+    g_hash_table_remove (pipeline_table, GINT_TO_POINTER (id));
+  }
+
+  G_UNLOCK (pipeline_table_lock);
+  machinelearning_service_pipeline_complete_destroy_pipeline (obj, invoc, result);
+
+  return TRUE;
+}
+
+/**
+ * @brief Get the state of pipeline with given id. Return the call result and its state.
+ */
+static gboolean
+dbus_cb_core_get_state (MachinelearningServicePipeline *obj,
+    GDBusMethodInvocation *invoc, gint64 id, gpointer user_data)
+{
+  gint result = 0;
+  GstStateChangeReturn sc_ret;
+  GstState state = GST_STATE_NULL;
+  pipeline_s *p = NULL;
+
+  G_LOCK (pipeline_table_lock);
+  p = (pipeline_s *) g_hash_table_lookup (pipeline_table, GINT_TO_POINTER (id));
+
+  if (!p) {
+    _E ("The callback get_state is called, but there is no pipeline matched with ID.");
+    result = -EINVAL;
+    machinelearning_service_pipeline_complete_get_state (obj, invoc, result, (gint) state);
+    G_UNLOCK (pipeline_table_lock);
+    return TRUE;
+  }
+
+  g_mutex_lock (&p->lock);
+  G_UNLOCK (pipeline_table_lock);
+  sc_ret = gst_element_get_state (p->element, &state, NULL, GST_MSECOND);
+  g_mutex_unlock (&p->lock);
+
+  if (sc_ret == GST_STATE_CHANGE_FAILURE) {
+    _E ("Failed to get the state of the pipline whose service name is %s.", p->service_name);
+    result = -ESTRPIPE;
+    machinelearning_service_pipeline_complete_get_state (obj, invoc, result, (gint) state);
+    return TRUE;
+  }
+
+  machinelearning_service_pipeline_complete_get_state (obj, invoc, result, (gint) state);
+
+  return TRUE;
+}
+
+static struct gdbus_signal_info handler_infos[] = {
+  {
+      .signal_name = DBUS_PIPELINE_I_SET_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_set_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_GET_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_get_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_DELETE_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_delete_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_LAUNCH_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_launch_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_START_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_start_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_STOP_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_stop_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_DESTROY_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_destroy_pipeline),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_PIPELINE_I_GET_STATE_HANDLER,
+      .cb = G_CALLBACK (dbus_cb_core_get_state),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+};
+
+/**
+ * @brief Probe the D-BUS and connect this module.
+ */
+static int
+probe_pipeline_module (void *data)
+{
+  int ret = 0;
+
+  g_gdbus_instance = gdbus_get_pipeline_instance ();
+  if (g_gdbus_instance == NULL) {
+    _E ("cannot get a dbus instance for the %s interface\n", DBUS_PIPELINE_INTERFACE);
+    return -ENOSYS;
+  }
+
+  ret = gdbus_connect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+  if (ret < 0) {
+    _E ("cannot register callbacks as the dbus method invocation handlers\n ret: %d", ret);
+    ret = -ENOSYS;
+    goto out;
+  }
+
+  ret = gdbus_export_interface (g_gdbus_instance, DBUS_PIPELINE_PATH);
+  if (ret < 0) {
+    _E ("cannot export the dbus interface '%s' at the object path '%s'\n",
+        DBUS_PIPELINE_INTERFACE, DBUS_PIPELINE_PATH);
+    ret = -ENOSYS;
+    goto out_disconnect;
+  }
+
+  return 0;
+
+out_disconnect:
+  gdbus_disconnect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+out:
+  gdbus_put_pipeline_instance (&g_gdbus_instance);
+
+  return ret;
+}
+
+/**
+ * @brief Initialize this module.
+ */
+static void
+init_pipeline_module (void *data)
+{
+  gdbus_initialize ();
+
+  G_LOCK (pipeline_table_lock);
+  g_assert (NULL == pipeline_table); /** Internal error */
+  pipeline_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, _pipeline_free);
+  G_UNLOCK (pipeline_table_lock);
+}
+
+/**
+ * @brief Finalize this module.
+ */
+static void
+exit_pipeline_module (void *data)
+{
+  G_LOCK (pipeline_table_lock);
+  g_assert (pipeline_table); /** Internal error */
+  g_hash_table_destroy (pipeline_table);
+  pipeline_table = NULL;
+  G_UNLOCK (pipeline_table_lock);
+
+  gdbus_disconnect_signal (g_gdbus_instance, ARRAY_SIZE (handler_infos), handler_infos);
+  gdbus_put_pipeline_instance (&g_gdbus_instance);
+}
+
+static const struct module_ops pipeline_ops = {
+  .name = "pipeline",
+  .probe = probe_pipeline_module,
+  .init = init_pipeline_module,
+  .exit = exit_pipeline_module,
+};
+
+MODULE_OPS_REGISTER (&pipeline_ops)
diff --git a/daemon/pkg-mgr.cc b/daemon/pkg-mgr.cc
new file mode 100755 (executable)
index 0000000..e5bb73d
--- /dev/null
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file    pkg-mgr.cc
+ * @date    16 Feb 2023
+ * @brief   NNStreamer/Utilities C-API Wrapper.
+ * @see            https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ */
+
+#include <glib.h>
+#include <json-glib/json-glib.h>
+#include <package_manager.h>
+#include <stdio.h>
+
+#include "pkg-mgr.h"
+#include "service-db.hh"
+
+/**
+ * @brief Internal enumeration for data types of json.
+ */
+typedef enum {
+  MLSVC_JSON_MODEL = 0,
+  MLSVC_JSON_PIPELINE = 1,
+  MLSVC_JSON_RESOURCE = 2,
+  MLSVC_JSON_MAX
+} mlsvc_json_type_e;
+
+/**
+ * @brief Global handle for Tizen package manager.
+ */
+static package_manager_h pkg_mgr = NULL;
+
+/**
+ * @brief Get app-info json string.
+ */
+static gchar *
+_get_app_info (const gchar *package_name, const gchar *res_type, const gchar *res_version)
+{
+  g_autoptr (JsonBuilder) builder = json_builder_new ();
+  json_builder_begin_object (builder);
+
+  json_builder_set_member_name (builder, "is_rpk");
+  json_builder_add_string_value (builder, "T");
+
+  json_builder_set_member_name (builder, "app_id");
+  json_builder_add_string_value (builder, package_name);
+
+  json_builder_set_member_name (builder, "res_type");
+  json_builder_add_string_value (builder, res_type);
+
+  json_builder_set_member_name (builder, "res_version");
+  json_builder_add_string_value (builder, res_version);
+
+  json_builder_end_object (builder);
+
+  g_autoptr (JsonNode) root = json_builder_get_root (builder);
+  g_autoptr (JsonGenerator) gen = json_generator_new ();
+  json_generator_set_root (gen, root);
+  json_generator_set_pretty (gen, TRUE);
+
+  return json_generator_to_data (gen, NULL);
+}
+
+/**
+ * @brief Parse json and update ml-service database.
+ */
+static void
+_parse_json (const gchar *json_path, mlsvc_json_type_e json_type, const gchar *app_info)
+{
+  g_autofree gchar *json_file = NULL;
+
+  switch (json_type) {
+    case MLSVC_JSON_MODEL:
+      json_file = g_build_filename (json_path, "model_description.json", NULL);
+      break;
+    case MLSVC_JSON_PIPELINE:
+      json_file = g_build_filename (json_path, "pipeline_description.json", NULL);
+      break;
+    case MLSVC_JSON_RESOURCE:
+      json_file = g_build_filename (json_path, "resource_description.json", NULL);
+      break;
+    default:
+      _E ("Unknown data type '%d', internal error?", json_type);
+      return;
+  }
+
+  if (!g_file_test (json_file, (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
+    _W ("Failed to find json file '%s'. RPK using ML Service API should provide this json file.",
+        json_file);
+    return;
+  }
+
+  g_autoptr (JsonParser) parser = json_parser_new ();
+  g_autoptr (GError) err = NULL;
+
+  json_parser_load_from_file (parser, json_file, &err);
+  if (err) {
+    _E ("Failed to parse json file '%s': %s", json_file, err->message);
+    return;
+  }
+
+  JsonNode *root = json_parser_get_root (parser);
+  JsonArray *array = NULL;
+  JsonObject *object = NULL;
+  guint json_len = 1U;
+
+  if (JSON_NODE_HOLDS_ARRAY (root)) {
+    array = json_node_get_array (root);
+    if (!array) {
+      _E ("Failed to get root array from json file '%s'", json_file);
+      return;
+    }
+
+    json_len = json_array_get_length (array);
+  }
+
+  /* Update ML service database. */
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  try {
+    db.connectDB ();
+
+    for (guint i = 0; i < json_len; ++i) {
+      if (array)
+        object = json_array_get_object_element (array, i);
+      else
+        object = json_node_get_object (root);
+
+      switch (json_type) {
+        case MLSVC_JSON_MODEL:
+          {
+            const gchar *name = json_object_get_string_member (object, "name");
+            const gchar *model = json_object_get_string_member (object, "model");
+            const gchar *desc = json_object_get_string_member (object, "description");
+            const gchar *activate = json_object_get_string_member (object, "activate");
+            const gchar *clear = json_object_get_string_member (object, "clear");
+
+            if (!name || !model) {
+              _E ("Failed to get name or model from json file '%s'.", json_file);
+              continue;
+            }
+
+            guint version;
+            bool active = (activate && g_ascii_strcasecmp (activate, "true") == 0);
+            bool clear_old = (clear && g_ascii_strcasecmp (clear, "true") == 0);
+
+            /* Remove old model from database. */
+            if (clear_old) {
+              try {
+                db.delete_model (name, 0U);
+              } catch (const std::exception &e) {
+                /* Ignore error case. */
+                _W ("%s", e.what ());
+              }
+            }
+
+            db.set_model (name, model, active, desc ? desc : "",
+                app_info ? app_info : "", &version);
+
+            _I ("The model with name '%s' is registered as version '%u'.", name, version);
+          }
+          break;
+        case MLSVC_JSON_PIPELINE:
+          {
+            const gchar *name = json_object_get_string_member (object, "name");
+            const gchar *desc = json_object_get_string_member (object, "description");
+
+            if (!name || !desc) {
+              _E ("Failed to get name or description from json file '%s'.", json_file);
+              continue;
+            }
+
+            db.set_pipeline (name, desc);
+
+            _I ("The pipeline description with name '%s' is registered.", name);
+          }
+          break;
+        case MLSVC_JSON_RESOURCE:
+          {
+            const gchar *name = json_object_get_string_member (object, "name");
+            const gchar *path = json_object_get_string_member (object, "path");
+            const gchar *desc = json_object_get_string_member (object, "description");
+            const gchar *clear = json_object_get_string_member (object, "clear");
+
+            if (!name || !path) {
+              _E ("Failed to get name or path from json file '%s'.", json_file);
+              continue;
+            }
+
+            bool clear_old = (clear && g_ascii_strcasecmp (clear, "true") == 0);
+
+            /* Remove old resource from database. */
+            if (clear_old) {
+              try {
+                db.delete_resource (name);
+              } catch (const std::exception &e) {
+                /* Ignore error case. */
+                _W ("%s", e.what ());
+              }
+            }
+
+            db.set_resource (name, path, desc ? desc : "", app_info ? app_info : "");
+
+            _I ("The resource with name '%s' is registered.", name);
+          }
+          break;
+        default:
+          _E ("Unknown data type '%d', internal error?", json_type);
+          break;
+      }
+    }
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+  }
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief A simple package manager event handler for temporary use
+ * @param pkg_path The path where the target package is installed
+ */
+static inline void
+_pkg_mgr_echo_pkg_path_info (const gchar *pkg_path)
+{
+  GDir *dir;
+
+  if (g_file_test (pkg_path, G_FILE_TEST_IS_DIR)) {
+    _I ("package path: %s", pkg_path);
+
+    dir = g_dir_open (pkg_path, 0, NULL);
+    if (dir) {
+      const gchar *file_name;
+
+      while ((file_name = g_dir_read_name (dir))) {
+        _I ("- file: %s", file_name);
+      }
+      g_dir_close (dir);
+    }
+  }
+}
+
+/**
+ * @brief Callback function to be invoked for resource package.
+ * @param type the package type such as rpk, tpk, wgt, etc.
+ * @param package_name the name of the package
+ * @param event_type the type of the request such as install, uninstall, update
+ * @param event_state the current state of the requests such as completed, failed
+ * @param progress the progress for the request
+ * @param error the error code when the package manager is failed
+ * @param user_data user data to be passed
+ */
+static void
+_pkg_mgr_event_cb (const char *type, const char *package_name,
+    package_manager_event_type_e event_type, package_manager_event_state_e event_state,
+    int progress, package_manager_error_e error, void *user_data)
+{
+  g_autofree gchar *pkg_path = NULL;
+  package_info_h pkg_info = NULL;
+  int ret;
+
+  _I ("type: %s, package_name: %s, event_type: %d, event_state: %d", type,
+      package_name, event_type, event_state);
+
+  /* TODO: find out when this callback is called */
+  if (event_type == PACKAGE_MANAGER_EVENT_TYPE_RES_COPY) {
+    _I ("resource package copy is being started");
+    return;
+  }
+
+  if (g_ascii_strcasecmp (type, "rpk") != 0)
+    return;
+
+  /**
+   * @todo package path
+   * 1. Handle allowed resources. Currently this only supports global resources.
+   * 2. Find API to get this hardcoded path prefix (/opt/usr/globalapps/)
+   */
+  pkg_path = g_strdup_printf ("/opt/usr/globalapps/%s/res/global", package_name);
+
+  if (event_type == PACKAGE_MANAGER_EVENT_TYPE_INSTALL
+      && event_state == PACKAGE_MANAGER_EVENT_STATE_COMPLETED) {
+    /* Get res information from package_info APIs */
+    ret = package_info_create (package_name, &pkg_info);
+    if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+      _E ("package_info_create failed: %d", ret);
+      return;
+    }
+
+    g_autofree gchar *res_type = NULL;
+    ret = package_info_get_res_type (pkg_info, &res_type);
+    if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+      _E ("package_info_get_res_type failed: %d", ret);
+      ret = package_info_destroy (pkg_info);
+      if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+        _E ("package_info_destroy failed: %d", ret);
+      }
+      return;
+    }
+
+    g_autofree gchar *res_version = NULL;
+    ret = package_info_get_res_version (pkg_info, &res_version);
+    if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+      _E ("package_info_get_res_version failed: %d", ret);
+      ret = package_info_destroy (pkg_info);
+      if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+        _E ("package_info_destroy failed: %d", ret);
+      }
+      return;
+    }
+
+    _I ("resource package %s is installed. res_type: %s, res_version: %s",
+        package_name, res_type, res_version);
+
+    if (pkg_info) {
+      ret = package_info_destroy (pkg_info);
+      if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+        _E ("package_info_destroy failed: %d", ret);
+      }
+    }
+
+    g_autofree gchar *app_info = _get_app_info (package_name, res_type, res_version);
+    g_autofree gchar *json_path = g_build_filename (pkg_path, res_type, NULL);
+
+    for (gint t = MLSVC_JSON_MODEL; t < MLSVC_JSON_MAX; t++)
+      _parse_json (json_path, (mlsvc_json_type_e) t, app_info);
+  } else if (event_type == PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL
+             && event_state == PACKAGE_MANAGER_EVENT_STATE_STARTED) {
+    _I ("resource package %s is being uninstalled", package_name);
+    _pkg_mgr_echo_pkg_path_info (pkg_path);
+    /* TODO: Invalidate models related to the package would be uninstalled */
+  } else if (event_type == PACKAGE_MANAGER_EVENT_TYPE_UPDATE
+             && event_state == PACKAGE_MANAGER_EVENT_STATE_COMPLETED) {
+    _I ("resource package %s is updated", package_name);
+    _pkg_mgr_echo_pkg_path_info (pkg_path);
+    /* TODO: Update database */
+  } else {
+    /* Do not consider other events: do nothing */
+  }
+}
+
+/**
+ * @brief Initialize the package manager handler for the resource package.
+ */
+int
+pkg_mgr_init (void)
+{
+  int ret = 0;
+
+  ret = package_manager_create (&pkg_mgr);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+    _E ("package_manager_create() failed: %d", ret);
+    return -1;
+  }
+
+  /* Monitoring install, uninstall and upgrade events of the resource package. */
+  /** @todo Find when these ACKAGE_MANAGER_STATUS_TYPE_RES_* status called */
+  ret = package_manager_set_event_status (pkg_mgr,
+      PACKAGE_MANAGER_STATUS_TYPE_INSTALL | PACKAGE_MANAGER_STATUS_TYPE_UNINSTALL
+          | PACKAGE_MANAGER_STATUS_TYPE_UPGRADE | PACKAGE_MANAGER_STATUS_TYPE_RES_COPY
+          | PACKAGE_MANAGER_STATUS_TYPE_RES_CREATE_DIR | PACKAGE_MANAGER_STATUS_TYPE_RES_REMOVE
+          | PACKAGE_MANAGER_STATUS_TYPE_RES_UNINSTALL);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+    _E ("package_manager_set_event_status() failed: %d", ret);
+    return -1;
+  }
+
+  ret = package_manager_set_event_cb (pkg_mgr, _pkg_mgr_event_cb, NULL);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+    _E ("package_manager_set_event_cb() failed: %d", ret);
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * @brief Finalize the package manager handler for the resource package.
+ */
+int
+pkg_mgr_deinit (void)
+{
+  int ret = 0;
+  ret = package_manager_destroy (pkg_mgr);
+  if (ret != PACKAGE_MANAGER_ERROR_NONE) {
+    _E ("package_manager_destroy() failed: %d", ret);
+    return -1;
+  }
+  return 0;
+}
diff --git a/daemon/pkg-mgr.h b/daemon/pkg-mgr.h
new file mode 100755 (executable)
index 0000000..4a5d126
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * NNStreamer API / Machine Learning Agent Daemon
+ * Copyright (C) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+ */
+
+/**
+ * @file    pkg-mgr.h
+ * @date    16 Feb 2023
+ * @brief   Internal package manager utility header of Machine Learning agent daemon
+ * @see     https://github.com/nnstreamer/api
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug     No known bugs except for NYI items
+ *
+ * @details
+ *    This provides the Tizen package manager utility functions for the Machine Learning agent daemon.
+ */
+#ifndef __PKG_MGR_H__
+#define __PKG_MGR_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if defined(__TIZEN__)
+#include "log.h"
+
+/**
+ * @brief Initialize the package manager handler for the resource package.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int pkg_mgr_init (void);
+
+/**
+ * @brief Finalize the package manager handler for the resource package.
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int pkg_mgr_deinit (void);
+#else
+#define pkg_mgr_init(...) ((int) 0)
+#define pkg_mgr_deinit(...) ((int) 0)
+#endif /* __TIZEN__ */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __PKG_MGR_H__ */
diff --git a/daemon/resource-dbus-impl.cc b/daemon/resource-dbus-impl.cc
new file mode 100644 (file)
index 0000000..b1e775d
--- /dev/null
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file resource-dbus-impl.cc
+ * @date 17 July 2023
+ * @brief DBus implementation for Resource Interface
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <errno.h>
+#include <glib.h>
+
+#include "common.h"
+#include "dbus-interface.h"
+#include "gdbus-util.h"
+#include "log.h"
+#include "modules.h"
+#include "resource-dbus.h"
+#include "service-db.hh"
+
+static MachinelearningServiceResource *g_gdbus_res_instance = NULL;
+
+/**
+ * @brief Utility function to get the DBus proxy.
+ */
+static MachinelearningServiceResource *
+gdbus_get_resource_instance (void)
+{
+  return machinelearning_service_resource_skeleton_new ();
+}
+
+/**
+ * @brief Utility function to release DBus proxy.
+ */
+static void
+gdbus_put_resource_instance (MachinelearningServiceResource **instance)
+{
+  g_clear_object (instance);
+}
+
+/**
+ * @brief The callback function of Add method
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target resource.
+ * @param path The file path of target.
+ * @param description The description of the resource.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_resource_add (MachinelearningServiceResource *obj, GDBusMethodInvocation *invoc,
+    const gchar *name, const gchar *path, const gchar *description, const gchar *app_info)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.set_resource (name, path, description, app_info);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_resource_complete_add (obj, invoc, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of get method
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target resource.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_resource_get (MachinelearningServiceResource *obj,
+    GDBusMethodInvocation *invoc, const gchar *name)
+{
+  int ret = 0;
+  std::string res_info;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.get_resource (name, res_info);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_resource_complete_get (obj, invoc, res_info.c_str (), ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief The callback function of delete method
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target resource.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_resource_delete (MachinelearningServiceResource *obj,
+    GDBusMethodInvocation *invoc, const gchar *name)
+{
+  int ret = 0;
+  MLServiceDB &db = MLServiceDB::getInstance ();
+
+  try {
+    db.connectDB ();
+    db.delete_resource (name);
+  } catch (const std::invalid_argument &e) {
+    _E ("%s", e.what ());
+    ret = -EINVAL;
+  } catch (const std::exception &e) {
+    _E ("%s", e.what ());
+    ret = -EIO;
+  }
+
+  db.disconnectDB ();
+  machinelearning_service_resource_complete_delete (obj, invoc, ret);
+
+  return TRUE;
+}
+
+/**
+ * @brief Event handler list of resource interface
+ */
+static struct gdbus_signal_info res_handler_infos[] = {
+  {
+      .signal_name = DBUS_RESOURCE_I_HANDLER_ADD,
+      .cb = G_CALLBACK (gdbus_cb_resource_add),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_RESOURCE_I_HANDLER_GET,
+      .cb = G_CALLBACK (gdbus_cb_resource_get),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+  {
+      .signal_name = DBUS_RESOURCE_I_HANDLER_DELETE,
+      .cb = G_CALLBACK (gdbus_cb_resource_delete),
+      .cb_data = NULL,
+      .handler_id = 0,
+  },
+};
+
+/**
+ * @brief The callback function for probing resource Interface module.
+ */
+static int
+probe_resource_module (void *data)
+{
+  int ret = 0;
+  _D ("probe_resource_module");
+
+  g_gdbus_res_instance = gdbus_get_resource_instance ();
+  if (NULL == g_gdbus_res_instance) {
+    _E ("cannot get a dbus instance for the %s interface\n", DBUS_RESOURCE_INTERFACE);
+    return -ENOSYS;
+  }
+
+  ret = gdbus_connect_signal (
+      g_gdbus_res_instance, ARRAY_SIZE (res_handler_infos), res_handler_infos);
+  if (ret < 0) {
+    _E ("cannot register callbacks as the dbus method invocation handlers\n ret: %d", ret);
+    ret = -ENOSYS;
+    goto out;
+  }
+
+  ret = gdbus_export_interface (g_gdbus_res_instance, DBUS_RESOURCE_PATH);
+  if (ret < 0) {
+    _E ("cannot export the dbus interface '%s' at the object path '%s'\n",
+        DBUS_RESOURCE_INTERFACE, DBUS_RESOURCE_PATH);
+    ret = -ENOSYS;
+    goto out_disconnect;
+  }
+
+  return 0;
+
+out_disconnect:
+  gdbus_disconnect_signal (
+      g_gdbus_res_instance, ARRAY_SIZE (res_handler_infos), res_handler_infos);
+
+out:
+  gdbus_put_resource_instance (&g_gdbus_res_instance);
+
+  return ret;
+}
+
+/**
+ * @brief The callback function for initializing resource interface module.
+ */
+static void
+init_resource_module (void *data)
+{
+  gdbus_initialize ();
+}
+
+/**
+ * @brief The callback function for exiting resource interface module.
+ */
+static void
+exit_resource_module (void *data)
+{
+  gdbus_disconnect_signal (
+      g_gdbus_res_instance, ARRAY_SIZE (res_handler_infos), res_handler_infos);
+  gdbus_put_resource_instance (&g_gdbus_res_instance);
+}
+
+static const struct module_ops resource_ops = {
+  .name = "resource-interface",
+  .probe = probe_resource_module,
+  .init = init_resource_module,
+  .exit = exit_resource_module,
+};
+
+MODULE_OPS_REGISTER (&resource_ops)
diff --git a/daemon/service-db.cc b/daemon/service-db.cc
new file mode 100644 (file)
index 0000000..be01ed9
--- /dev/null
@@ -0,0 +1,856 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file service-db.cc
+ * @date 29 Jul 2022
+ * @brief Database implementation of NNStreamer/Service C-API
+ * @see https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include "service-db.hh"
+#include "log.h"
+
+#define sqlite3_clear_errmsg(m) \
+  do {                          \
+    if (m) {                    \
+      sqlite3_free (m);         \
+      (m) = nullptr;            \
+    }                           \
+  } while (0)
+
+#define ML_DATABASE_PATH DB_PATH "/.ml-service.db"
+#define DB_KEY_PREFIX MESON_KEY_PREFIX
+
+/**
+ * @brief The version of pipeline table schema. It should be a positive integer.
+ */
+#define TBL_VER_PIPELINE_DESCRIPTION (1)
+
+/**
+ * @brief The version of model table schema. It should be a positive integer.
+ */
+#define TBL_VER_MODEL_INFO (1)
+
+/**
+ * @brief The version of resource table schema. It should be a positive integer.
+ */
+#define TBL_VER_RESOURCE_INFO (1)
+
+typedef enum {
+  TBL_DB_INFO = 0,
+  TBL_PIPELINE_DESCRIPTION = 1,
+  TBL_MODEL_INFO = 2,
+  TBL_RESOURCE_INFO = 3,
+
+  TBL_MAX
+} mlsvc_table_e;
+
+const char *g_mlsvc_table_schema_v1[] = { [TBL_DB_INFO] = "tblMLDBInfo (name TEXT PRIMARY KEY NOT NULL, version INTEGER DEFAULT 1)",
+  [TBL_PIPELINE_DESCRIPTION] = "tblPipeline (key TEXT PRIMARY KEY NOT NULL, description TEXT, CHECK (length(description) > 0))",
+  [TBL_MODEL_INFO]
+  = "tblModel (key TEXT NOT NULL, version INTEGER DEFAULT 1, active TEXT DEFAULT 'F', "
+    "path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, version), CHECK (length(path) > 0), CHECK (active IN ('T', 'F')))",
+  [TBL_RESOURCE_INFO] = "tblResource (key TEXT NOT NULL, path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, path), CHECK (length(path) > 0))",
+  NULL };
+
+const char **g_mlsvc_table_schema = g_mlsvc_table_schema_v1;
+
+/**
+ * @brief Get an instance of MLServiceDB, which is created only once at runtime.
+ * @return MLServiceDB& MLServiceDB instance
+ */
+MLServiceDB &
+MLServiceDB::getInstance (void)
+{
+  static MLServiceDB instance (ML_DATABASE_PATH);
+
+  return instance;
+}
+
+/**
+ * @brief Construct a new MLServiceDB object.
+ * @param path database path
+ */
+MLServiceDB::MLServiceDB (std::string path)
+    : _path (path), _initialized (false), _db (nullptr)
+{
+}
+
+/**
+ * @brief Destroy the MLServiceDB object.
+ */
+MLServiceDB::~MLServiceDB ()
+{
+  disconnectDB ();
+  _initialized = false;
+}
+
+/**
+ * @brief Create table and handle database version.
+ */
+void
+MLServiceDB::initDB ()
+{
+  int i, tbl_ver;
+
+  if (_initialized)
+    return;
+
+  /**
+   * @todo data migration
+   * handle database version and update each table
+   * 1. get all records from table
+   * 2. drop old table
+   * 3. create new table and insert records
+   */
+  if (!set_transaction (true))
+    return;
+
+  /* Create tables. */
+  for (i = 0; i < TBL_MAX; i++) {
+    if (!create_table (g_mlsvc_table_schema[i]))
+      return;
+  }
+
+  /* Check pipeline table. */
+  if ((tbl_ver = get_table_version ("tblPipeline", TBL_VER_PIPELINE_DESCRIPTION)) < 0)
+    return;
+
+  if (tbl_ver != TBL_VER_PIPELINE_DESCRIPTION) {
+    /** @todo update pipeline table if table schema is changed */
+  }
+
+  if (!set_table_version ("tblPipeline", TBL_VER_PIPELINE_DESCRIPTION))
+    return;
+
+  /* Check model table. */
+  if ((tbl_ver = get_table_version ("tblModel", TBL_VER_MODEL_INFO)) < 0)
+    return;
+
+  if (tbl_ver != TBL_VER_MODEL_INFO) {
+    /** @todo update model table if table schema is changed */
+  }
+
+  if (!set_table_version ("tblModel", TBL_VER_MODEL_INFO))
+    return;
+
+  /* Check resource table. */
+  if ((tbl_ver = get_table_version ("tblResource", TBL_VER_RESOURCE_INFO)) < 0)
+    return;
+
+  if (tbl_ver != TBL_VER_RESOURCE_INFO) {
+    /** @todo update resource table if table schema is changed */
+  }
+
+  if (!set_table_version ("tblResource", TBL_VER_RESOURCE_INFO))
+    return;
+
+  if (!set_transaction (false))
+    return;
+
+  _initialized = true;
+}
+
+/**
+ * @brief Connect to ML Service DB and initialize the private variables.
+ */
+void
+MLServiceDB::connectDB ()
+{
+  int rc;
+
+  if (_db != nullptr)
+    return;
+
+  rc = sqlite3_open (_path.c_str (), &_db);
+  if (rc != SQLITE_OK) {
+    _E ("Failed to open database: %s (%d)", sqlite3_errmsg (_db), rc);
+    goto error;
+  }
+
+  initDB ();
+
+error:
+  if (!_initialized) {
+    disconnectDB ();
+    throw std::runtime_error ("Failed to connect DB.");
+  }
+}
+
+/**
+ * @brief Disconnect the DB.
+ */
+void
+MLServiceDB::disconnectDB ()
+{
+  if (_db) {
+    sqlite3_close (_db);
+    _db = nullptr;
+  }
+}
+
+/**
+ * @brief Get table version.
+ */
+int
+MLServiceDB::get_table_version (const std::string tbl_name, const int default_ver)
+{
+  int rc, tbl_ver;
+  sqlite3_stmt *res;
+  std::string sql = "SELECT version FROM tblMLDBInfo WHERE name = '" + tbl_name + "';";
+
+  rc = sqlite3_prepare_v2 (_db, sql.c_str (), -1, &res, nullptr);
+  if (rc != SQLITE_OK) {
+    _W ("Failed to get the version of table %s: %s (%d)", tbl_name.c_str (),
+        sqlite3_errmsg (_db), rc);
+    return -1;
+  }
+
+  tbl_ver = (sqlite3_step (res) == SQLITE_ROW) ? sqlite3_column_int (res, 0) : default_ver;
+  sqlite3_finalize (res);
+
+  return tbl_ver;
+}
+
+/**
+ * @brief Set table version.
+ */
+bool
+MLServiceDB::set_table_version (const std::string tbl_name, const int tbl_ver)
+{
+  sqlite3_stmt *res;
+  std::string sql = "INSERT OR REPLACE INTO tblMLDBInfo VALUES (?1, ?2);";
+
+  bool is_done = (sqlite3_prepare_v2 (_db, sql.c_str (), -1, &res, nullptr) == SQLITE_OK
+                  && sqlite3_bind_text (res, 1, tbl_name.c_str (), -1, nullptr) == SQLITE_OK
+                  && sqlite3_bind_int (res, 2, tbl_ver) == SQLITE_OK
+                  && sqlite3_step (res) == SQLITE_DONE);
+
+  sqlite3_finalize (res);
+
+  if (!is_done)
+    _W ("Failed to update version of table %s.", tbl_name.c_str ());
+  return is_done;
+}
+
+/**
+ * @brief Create DB table.
+ */
+bool
+MLServiceDB::create_table (const std::string tbl_name)
+{
+  int rc;
+  char *errmsg = nullptr;
+  std::string sql = "CREATE TABLE IF NOT EXISTS " + tbl_name;
+
+  rc = sqlite3_exec (_db, sql.c_str (), nullptr, nullptr, &errmsg);
+  if (rc != SQLITE_OK) {
+    _W ("Failed to create table %s: %s (%d)", tbl_name.c_str (), errmsg, rc);
+    sqlite3_clear_errmsg (errmsg);
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * @brief Begin/end transaction.
+ */
+bool
+MLServiceDB::set_transaction (bool begin)
+{
+  int rc;
+  char *errmsg = nullptr;
+
+  rc = sqlite3_exec (_db, begin ? "BEGIN TRANSACTION;" : "END TRANSACTION;",
+      nullptr, nullptr, &errmsg);
+  if (rc != SQLITE_OK)
+    _W ("Failed to %s transaction: %s (%d)", begin ? "begin" : "end", errmsg, rc);
+
+  sqlite3_clear_errmsg (errmsg);
+  return (rc == SQLITE_OK);
+}
+
+/**
+ * @brief Set the pipeline description with the given name.
+ * @note If the name already exists, the pipeline description is overwritten.
+ * @param[in] name Unique name to set the associated pipeline description.
+ * @param[in] description The pipeline description to be stored.
+ */
+void
+MLServiceDB::set_pipeline (const std::string name, const std::string description)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty () || description.empty ())
+    throw std::invalid_argument ("Invalid name or value parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_pipeline_");
+  key_with_prefix += name;
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  if (sqlite3_prepare_v2 (_db,
+          "INSERT OR REPLACE INTO tblPipeline VALUES (?1, ?2)", -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 2, description.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to insert pipeline description of " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+}
+
+/**
+ * @brief Get the pipeline description with the given name.
+ * @param[in] name The unique name to retrieve.
+ * @param[out] description The pipeline corresponding with the given name.
+ */
+void
+MLServiceDB::get_pipeline (const std::string name, std::string &description)
+{
+  char *value = nullptr;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_pipeline_");
+  key_with_prefix += name;
+
+  if (sqlite3_prepare_v2 (_db,
+          "SELECT description FROM tblPipeline WHERE key = ?1", -1, &res, nullptr)
+          == SQLITE_OK
+      && sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) == SQLITE_OK
+      && sqlite3_step (res) == SQLITE_ROW)
+    value = g_strdup_printf ("%s", sqlite3_column_text (res, 0));
+
+  sqlite3_finalize (res);
+
+  if (value) {
+    description = std::string (value);
+    g_free (value);
+  } else {
+    throw std::invalid_argument ("Failed to get pipeline description of " + name);
+  }
+}
+
+/**
+ * @brief Delete the pipeline description with a given name.
+ * @param[in] name The unique name to delete
+ */
+void
+MLServiceDB::delete_pipeline (const std::string name)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_pipeline_");
+  key_with_prefix += name;
+
+  if (sqlite3_prepare_v2 (_db, "DELETE FROM tblPipeline WHERE key = ?1", -1, &res, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to delete pipeline description of " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  if (sqlite3_changes (_db) == 0) {
+    throw std::invalid_argument ("There is no pipeline description of " + name);
+  }
+}
+
+/**
+ * @brief Check the model is registered.
+ */
+bool
+MLServiceDB::is_model_registered (const std::string key, const guint version)
+{
+  sqlite3_stmt *res;
+  gchar *sql;
+  bool registered;
+
+  if (version > 0U)
+    sql = g_strdup_printf (
+        "SELECT EXISTS(SELECT 1 FROM tblModel WHERE key = ?1 AND version = %u)", version);
+  else
+    sql = g_strdup ("SELECT EXISTS(SELECT 1 FROM tblModel WHERE key = ?1)");
+
+  registered
+      = !(sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+          || sqlite3_bind_text (res, 1, key.c_str (), -1, nullptr) != SQLITE_OK
+          || sqlite3_step (res) != SQLITE_ROW || sqlite3_column_int (res, 0) != 1);
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  return registered;
+}
+
+/**
+ * @brief Check the model is activated.
+ */
+bool
+MLServiceDB::is_model_activated (const std::string key, const guint version)
+{
+  sqlite3_stmt *res;
+  gchar *sql;
+  bool activated;
+
+  sql = g_strdup ("SELECT active FROM tblModel WHERE key = ?1 AND version = ?2");
+
+  activated = !(sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+                || sqlite3_bind_text (res, 1, key.c_str (), -1, nullptr) != SQLITE_OK
+                || sqlite3_bind_int (res, 2, version) != SQLITE_OK
+                || sqlite3_step (res) != SQLITE_ROW
+                || !g_str_equal (sqlite3_column_text (res, 0), "T"));
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  return activated;
+}
+
+/**
+ * @brief Check the resource is registered.
+ */
+bool
+MLServiceDB::is_resource_registered (const std::string key)
+{
+  sqlite3_stmt *res;
+  gchar *sql;
+  bool registered;
+
+  sql = g_strdup_printf ("SELECT EXISTS(SELECT 1 FROM tblResource WHERE key = ?1)");
+
+  registered
+      = !(sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+          || sqlite3_bind_text (res, 1, key.c_str (), -1, nullptr) != SQLITE_OK
+          || sqlite3_step (res) != SQLITE_ROW || sqlite3_column_int (res, 0) != 1);
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  return registered;
+}
+
+/**
+ * @brief Set the model with the given name.
+ * @param[in] name Unique name for model.
+ * @param[in] model The model to be stored.
+ * @param[in] is_active The model is active or not.
+ * @param[in] description The model description.
+ * @param[out] version The version of the model.
+ */
+void
+MLServiceDB::set_model (const std::string name, const std::string model, const bool is_active,
+    const std::string description, const std::string app_info, guint *version)
+{
+  guint _version = 0U;
+  sqlite3_stmt *res;
+
+  if (name.empty () || model.empty () || !version)
+    throw std::invalid_argument ("Invalid name, model, or version parameter!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_model_");
+  key_with_prefix += name;
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  /* set other models as NOT active */
+  if (is_active) {
+    if (sqlite3_prepare_v2 (_db,
+            "UPDATE tblModel SET active = 'F' WHERE key = ?1", -1, &res, nullptr)
+            != SQLITE_OK
+        || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+        || sqlite3_step (res) != SQLITE_DONE) {
+      sqlite3_finalize (res);
+      throw std::runtime_error ("Failed to set other models as NOT active.");
+    }
+    sqlite3_finalize (res);
+  }
+
+  /* insert new row */
+  if (sqlite3_prepare_v2 (_db, "INSERT OR REPLACE INTO tblModel VALUES (?1, IFNULL ((SELECT version from tblModel WHERE key = ?2 ORDER BY version DESC LIMIT 1) + 1, 1), ?3, ?4, ?5, ?6)",
+          -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 2, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 3, is_active ? "T" : "F", -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 4, model.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 5, description.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 6, app_info.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to register the model " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  long long int last_id = sqlite3_last_insert_rowid (_db);
+  if (last_id == 0) {
+    _E ("Failed to get last inserted row id: %s", sqlite3_errmsg (_db));
+    throw std::runtime_error ("Failed to get last inserted row id.");
+  }
+
+  /* get model's version */
+  if (sqlite3_prepare_v2 (_db, "SELECT version FROM tblModel WHERE rowid = ? ORDER BY version DESC LIMIT 1;",
+          -1, &res, nullptr)
+          == SQLITE_OK
+      && sqlite3_bind_int (res, 1, last_id) == SQLITE_OK && sqlite3_step (res) == SQLITE_ROW) {
+    _version = sqlite3_column_int (res, 0);
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+
+  if (_version == 0) {
+    _E ("Failed to get model version with name %s: %s", name.c_str (),
+        sqlite3_errmsg (_db));
+    throw std::invalid_argument ("Failed to get model version of " + name);
+  }
+
+  *version = _version;
+}
+
+/**
+ * @brief Update the model description with the given name.
+ * @param[in] name Unique name for model.
+ * @param[in] version The version of the model.
+ * @param[in] description The model description.
+ */
+void
+MLServiceDB::update_model_description (
+    const std::string name, const guint version, const std::string description)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty () || description.empty ())
+    throw std::invalid_argument ("Invalid name or description parameter!");
+
+  if (version == 0U)
+    throw std::invalid_argument ("Invalid version number!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_model_");
+  key_with_prefix += name;
+
+  /* check the existence of given model */
+  if (!is_model_registered (key_with_prefix, version)) {
+    throw std::invalid_argument ("Failed to check the existence of " + name
+                                 + " version " + std::to_string (version));
+  }
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  /* update model description */
+  if (sqlite3_prepare_v2 (_db, "UPDATE tblModel SET description = ?1 WHERE key = ?2 AND version = ?3",
+          -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, description.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 2, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_int (res, 3, version) != SQLITE_OK || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to update model description.");
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+}
+
+/**
+ * @brief Activate the model with the given name.
+ * @param[in] name Unique name for model.
+ * @param[in] version The version of the model.
+ */
+void
+MLServiceDB::activate_model (const std::string name, const guint version)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameter!");
+
+  if (version == 0U)
+    throw std::invalid_argument ("Invalid version number!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_model_");
+  key_with_prefix += name;
+
+  /* check the existence */
+  if (!is_model_registered (key_with_prefix, version)) {
+    throw std::invalid_argument ("There is no model with name " + name
+                                 + " and version " + std::to_string (version));
+  }
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  /* set other row active as F */
+  if (sqlite3_prepare_v2 (_db, "UPDATE tblModel SET active = 'F' WHERE key = ?1", -1, &res, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to deactivate other models of " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  /* set the given row active as T */
+  if (sqlite3_prepare_v2 (_db, "UPDATE tblModel SET active = 'T' WHERE key = ?1 AND version = ?2",
+          -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_int (res, 2, version) != SQLITE_OK || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to activate model with name " + name
+                              + " and version " + std::to_string (version));
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+}
+
+/**
+ * @brief Get the model with the given name.
+ * @param[in] name The unique name to retrieve.
+ * @param[out] model The model corresponding with the given name.
+ * @param[in] version The version of the model. If it is 0, all models will return, if it is -1, return the active model.
+ */
+void
+MLServiceDB::get_model (const std::string name, std::string &model, const gint version)
+{
+  const char model_info_json[]
+      = "json_object('version', CAST(version AS TEXT), 'active', active, 'path', path, 'description', description, 'app_info', app_info)";
+  char *sql;
+  char *value = nullptr;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_model_");
+  key_with_prefix += name;
+
+  if (version == 0)
+    sql = g_strdup_printf (
+        "SELECT json_group_array(%s) FROM tblModel WHERE key = ?1", model_info_json);
+  else if (version == -1)
+    sql = g_strdup_printf ("SELECT %s FROM tblModel WHERE key = ?1 and active = 'T' ORDER BY version DESC LIMIT 1",
+        model_info_json);
+  else if (version > 0)
+    sql = g_strdup_printf ("SELECT %s FROM tblModel WHERE key = ?1 and version = %d",
+        model_info_json, version);
+  else
+    throw std::invalid_argument ("Invalid version parameter!");
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) == SQLITE_OK
+      && sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) == SQLITE_OK
+      && sqlite3_step (res) == SQLITE_ROW)
+    value = g_strdup_printf ("%s", sqlite3_column_text (res, 0));
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (value) {
+    model = std::string (value);
+    g_free (value);
+  } else {
+    throw std::invalid_argument ("Failed to get model with name " + name
+                                 + " and version " + std::to_string (version));
+  }
+}
+
+/**
+ * @brief Delete the model.
+ * @param[in] name The unique name to delete
+ * @param[in] version The version of the model to delete
+ */
+void
+MLServiceDB::delete_model (const std::string name, const guint version)
+{
+  char *sql;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_model_");
+  key_with_prefix += name;
+
+  /* existence check */
+  if (!is_model_registered (key_with_prefix, version)) {
+    throw std::invalid_argument ("There is no model with name " + name
+                                 + " and version " + std::to_string (version));
+  }
+
+  if (version > 0U) {
+    if (is_model_activated (key_with_prefix, version))
+      throw std::invalid_argument ("The model with name " + name
+                                   + " and version " + std::to_string (version)
+                                   + " is activated, cannot delete it.");
+
+    sql = g_strdup_printf ("DELETE FROM tblModel WHERE key = ?1 and version = %u", version);
+  } else {
+    sql = g_strdup ("DELETE FROM tblModel WHERE key = ?1");
+  }
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    g_free (sql);
+    throw std::runtime_error ("Failed to delete model with name " + name
+                              + " and version " + std::to_string (version));
+  }
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (sqlite3_changes (_db) == 0) {
+    throw std::invalid_argument ("There is no model with the given name " + name
+                                 + " and version " + std::to_string (version));
+  }
+}
+
+/**
+ * @brief Set the resource with given name.
+ * @param[in] name Unique name of ml-resource.
+ * @param[in] path The path to be stored.
+ * @param[in] description The description for ml-resource.
+ */
+void
+MLServiceDB::set_resource (const std::string name, const std::string path,
+    const std::string description, const std::string app_info)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty () || path.empty ())
+    throw std::invalid_argument ("Invalid name or path parameter!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  if (sqlite3_prepare_v2 (_db,
+          "INSERT OR REPLACE INTO tblResource VALUES (?1, ?2, ?3, ?4)", -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 2, path.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 3, description.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 4, app_info.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to add the resource " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+
+  long long int last_id = sqlite3_last_insert_rowid (_db);
+  if (last_id == 0) {
+    _E ("Failed to get last inserted row id: %s", sqlite3_errmsg (_db));
+    throw std::runtime_error ("Failed to get last inserted row id.");
+  }
+}
+
+/**
+ * @brief Get the resource with given name.
+ * @param[in] name The unique name to retrieve.
+ * @param[out] resource The resource corresponding with the given name.
+ */
+void
+MLServiceDB::get_resource (const std::string name, std::string &resource)
+{
+  const char res_info_json[]
+      = "json_object('path', path, 'description', description, 'app_info', app_info)";
+  char *sql;
+  char *value = nullptr;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  /* existence check */
+  if (!is_resource_registered (key_with_prefix))
+    throw std::invalid_argument ("There is no resource with name " + name);
+
+  /* Get json string with insertion order. */
+  sql = g_strdup_printf ("SELECT json_group_array(%s) FROM (SELECT * FROM tblResource WHERE key = ?1 ORDER BY ROWID ASC)",
+      res_info_json);
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) == SQLITE_OK
+      && sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) == SQLITE_OK
+      && sqlite3_step (res) == SQLITE_ROW)
+    value = g_strdup_printf ("%s", sqlite3_column_text (res, 0));
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (!value)
+    throw std::invalid_argument ("Failed to get resource with name " + name);
+
+  resource = std::string (value);
+  g_free (value);
+}
+
+/**
+ * @brief Delete the resource.
+ * @param[in] name The unique name to delete
+ */
+void
+MLServiceDB::delete_resource (const std::string name)
+{
+  char *sql;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  /* existence check */
+  if (!is_resource_registered (key_with_prefix))
+    throw std::invalid_argument ("There is no resource with name " + name);
+
+  sql = g_strdup ("DELETE FROM tblResource WHERE key = ?1");
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    g_free (sql);
+    throw std::runtime_error ("Failed to delete resource with name " + name);
+  }
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (sqlite3_changes (_db) == 0)
+    throw std::invalid_argument ("There is no resource with name " + name);
+}
diff --git a/daemon/service-db.hh b/daemon/service-db.hh
new file mode 100644 (file)
index 0000000..ababe07
--- /dev/null
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file service-db.hh
+ * @date 28 Mar 2022
+ * @brief NNStreamer/Service Database Interface
+ * @see        https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#ifndef __SERVICE_DB_HH__
+#define __SERVICE_DB_HH__
+
+#include <glib.h>
+#include <iostream>
+#include <sqlite3.h>
+
+/**
+ * @brief Class for ML-Service Database.
+ */
+class MLServiceDB
+{
+  public:
+  MLServiceDB (const MLServiceDB &) = delete;
+  MLServiceDB (MLServiceDB &&) = delete;
+  MLServiceDB &operator= (const MLServiceDB &) = delete;
+  MLServiceDB &operator= (MLServiceDB &&) = delete;
+
+  virtual void connectDB ();
+  virtual void disconnectDB ();
+  virtual void set_pipeline (const std::string name, const std::string description);
+  virtual void get_pipeline (const std::string name, std::string &description);
+  virtual void delete_pipeline (const std::string name);
+  virtual void set_model (const std::string name, const std::string model, const bool is_active,
+      const std::string description, const std::string app_info, guint *version);
+  virtual void update_model_description (const std::string name,
+      const guint version, const std::string description);
+  virtual void activate_model (const std::string name, const guint version);
+  virtual void get_model (const std::string name, std::string &model, const gint version);
+  virtual void delete_model (const std::string name, const guint version);
+  virtual void set_resource (const std::string name, const std::string path,
+      const std::string description, const std::string app_info);
+  virtual void get_resource (const std::string name, std::string &resource);
+  virtual void delete_resource (const std::string name);
+
+  static MLServiceDB &getInstance (void);
+
+  private:
+  MLServiceDB (std::string path);
+  virtual ~MLServiceDB ();
+
+  void initDB ();
+  int get_table_version (const std::string tbl_name, const int default_ver);
+  bool set_table_version (const std::string tbl_name, const int tbl_ver);
+  bool create_table (const std::string tbl_name);
+  bool set_transaction (bool begin);
+  bool is_model_registered (const std::string key, const guint version);
+  bool is_model_activated (const std::string key, const guint version);
+  bool is_resource_registered (const std::string key);
+
+  std::string _path;
+  bool _initialized;
+  sqlite3 *_db;
+};
+
+#endif /* __SERVICE_DB_HH__ */
diff --git a/dbus/machine-learning-agent.conf.in b/dbus/machine-learning-agent.conf.in
new file mode 100644 (file)
index 0000000..9f32a41
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow send_destination="org.tizen.machinelearning.service"
+               send_interface="org.tizen.machinelearning.service.pipeline"/>
+    </policy>
+    <policy user="service_fw">
+        <allow own="org.tizen.machinelearning.service"/>
+    </policy>
+    <policy context="default">
+       <deny own="org.tizen.machinelearning.service"/>
+       <deny send_destination="org.tizen.machinelearning.service"/>
+
+       <allow send_destination="org.tizen.machinelearning.service"/>
+    </policy>
+</busconfig>
diff --git a/dbus/machine-learning-agent.service.in b/dbus/machine-learning-agent.service.in
new file mode 100644 (file)
index 0000000..7436a0b
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Machine Learning Agent Daemon
+
+[Service]
+Type=dbus
+BusName=org.tizen.machinelearning.service
+SmackProcessLabel=System
+ExecStart=/usr/bin/machine-learning-agent
+User=service_fw
+Group=service_fw
diff --git a/dbus/meson.build b/dbus/meson.build
new file mode 100644 (file)
index 0000000..a250578
--- /dev/null
@@ -0,0 +1,53 @@
+pipeline_dbus_input = files('pipeline-dbus.xml')
+model_dbus_input = files('model-dbus.xml')
+resource_dbus_input = files('resource-dbus.xml')
+
+# Generate GDbus header and code
+gdbus_prog = find_program('gdbus-codegen', required: true)
+gdbus_gen_pipeline_src = custom_target('gdbus-gencode',
+  input: pipeline_dbus_input,
+  output: ['pipeline-dbus.h', 'pipeline-dbus.c'],
+  command: [gdbus_prog, '--interface-prefix', 'org.tizen',
+            '--generate-c-code', 'pipeline-dbus',
+            '--output-directory', meson.current_build_dir(),
+            '@INPUT@'])
+
+gdbus_gen_model_src = custom_target('gdbus-model-gencode',
+  input: model_dbus_input,
+  output: ['model-dbus.h', 'model-dbus.c'],
+  command: [gdbus_prog, '--interface-prefix', 'org.tizen',
+            '--generate-c-code', 'model-dbus',
+            '--output-directory', meson.current_build_dir(),
+            '@INPUT@'])
+
+gdbus_gen_resource_src = custom_target('gdbus-resource-gencode',
+  input: resource_dbus_input,
+  output: ['resource-dbus.h', 'resource-dbus.c'],
+  command: [gdbus_prog, '--interface-prefix', 'org.tizen',
+            '--generate-c-code', 'resource-dbus',
+            '--output-directory', meson.current_build_dir(),
+            '@INPUT@'])
+
+gdbus_gen_header_dep = declare_dependency(
+  sources: [gdbus_gen_pipeline_src, gdbus_gen_model_src, gdbus_gen_resource_src])
+
+# DBus Policy configuration
+configure_file(input: 'machine-learning-agent.conf.in',
+  output: 'machine-learning-agent.conf',
+  install_dir: dbus_policy_dir,
+  configuration: ml_agent_conf
+)
+
+# DBus System Service
+configure_file(input: 'org.tizen.machinelearning.service.service.in',
+  output: 'org.tizen.machinelearning.service.service',
+  install_dir: dbus_system_service_dir,
+  configuration: ml_agent_conf
+)
+
+# Systemd Service file
+configure_file(input: 'machine-learning-agent.service.in',
+  output: 'machine-learning-agent.service',
+  install_dir: systemd_service_dir,
+  configuration: ml_agent_conf
+)
diff --git a/dbus/model-dbus.xml b/dbus/model-dbus.xml
new file mode 100644 (file)
index 0000000..4f42586
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/Org/Tizen/MachineLearning/Service">
+  <interface name="org.tizen.machinelearning.service.model">
+    <!-- Register the given model -->
+    <method name="Register">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="path" direction="in" />
+      <arg type="b" name="active" direction="in" />
+      <arg type="s" name="description" direction="in" />
+      <arg type="s" name="app_info" direction="in" />
+      <arg type="u" name="version" direction="out" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Update the model description -->
+    <method name="UpdateDescription">
+      <arg type="s" name="name" direction="in" />
+      <arg type="u" name="version" direction="in" />
+      <arg type="s" name="description" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Activate the model -->
+    <method name="Activate">
+      <arg type="s" name="name" direction="in" />
+      <arg type="u" name="version" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Get the model of given version -->
+    <method name="Get">
+      <arg type="s" name="name" direction="in" />
+      <arg type="u" name="version" direction="in" />
+      <arg type="s" name="info" direction="out" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Get the activated model -->
+    <method name="GetActivated">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="info" direction="out" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Get list of models -->
+    <method name="GetAll">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="info_list" direction="out" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Delete model -->
+    <method name="Delete">
+      <arg type="s" name="name" direction="in" />
+      <arg type="u" name="version" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/dbus/org.tizen.machinelearning.service.service.in b/dbus/org.tizen.machinelearning.service.service.in
new file mode 100644 (file)
index 0000000..d87ff07
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.tizen.machinelearning.service
+Exec=/bin/false
+SystemdService=machine-learning-agent.service
diff --git a/dbus/pipeline-dbus.xml b/dbus/pipeline-dbus.xml
new file mode 100644 (file)
index 0000000..916e774
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/Org/Tizen/MachineLearning/Service">
+  <interface name="org.tizen.machinelearning.service.pipeline">
+    <method name="set_pipeline">
+      <arg type="s" name="service_name" direction="in" />
+      <arg type="s" name="pipeline_desc" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <method name="get_pipeline">
+      <arg type="s" name="service_name" direction="in" />
+      <arg type="i" name="result" direction="out" />
+      <arg type="s" name="pipeline_desc" direction="out" />
+    </method>
+    <method name="delete_pipeline">
+      <arg type="s" name="service_name" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <method name="launch_pipeline">
+      <arg type="s" name="service_name" direction="in" />
+      <arg type="i" name="result" direction="out" />
+      <arg type="x" name="id" direction="out" />
+    </method>
+    <method name="start_pipeline">
+      <arg type="x" name="id" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <method name="stop_pipeline">
+      <arg type="x" name="id" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <method name="destroy_pipeline">
+      <arg type="x" name="id" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <method name="get_state">
+      <arg type="x" name="id" direction="in" />
+      <arg type="i" name="result" direction="out" />
+      <arg type="i" name="state" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/dbus/resource-dbus.xml b/dbus/resource-dbus.xml
new file mode 100644 (file)
index 0000000..7c90073
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/Org/Tizen/MachineLearning/Service">
+  <interface name="org.tizen.machinelearning.service.resource">
+    <!-- Add machine-learning resource -->
+    <method name="Add">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="path" direction="in" />
+      <arg type="s" name="description" direction="in" />
+      <arg type="s" name="app_info" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Get the resource -->
+    <method name="Get">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="info" direction="out" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+    <!-- Delete the resource -->
+    <method name="Delete">
+      <arg type="s" name="name" direction="in" />
+      <arg type="i" name="result" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/meson.build b/meson.build
new file mode 100644 (file)
index 0000000..98b2cfa
--- /dev/null
@@ -0,0 +1,112 @@
+project('ml-agent', 'c', 'cpp',
+  version: '0.0.1',
+  license: ['Apache-2.0'],
+  meson_version: '>=0.50.0',
+  default_options: [
+    'b_asneeded=false',
+    'werror=true',
+    'warning_level=1',
+    'c_std=gnu89',
+    'cpp_std=c++14'
+  ]
+)
+
+cc = meson.get_compiler('c')
+cxx = meson.get_compiler('cpp')
+
+# Dependencies
+glib_dep = dependency('glib-2.0')
+gobject_dep = dependency('gobject-2.0')
+gmodule_dep = dependency('gmodule-2.0')
+if host_machine.system() == 'windows'
+  gio_dep = dependency('gio-2.0')
+else
+  gio_dep = [dependency('gio-2.0'), dependency('gio-unix-2.0')]
+endif
+gst_dep = dependency('gstreamer-1.0')
+libsystemd_dep = dependency('libsystemd')
+sqlite_dep = dependency('sqlite3')
+json_glib_dep = dependency('json-glib-1.0')
+
+# Set version info
+ml_agent_version = meson.project_version()
+ml_agent_version_split = ml_agent_version.split('.')
+
+add_project_arguments('-DVERSION="' + ml_agent_version + '"', language: ['c', 'cpp'])
+add_project_arguments('-DVERSION_MAJOR=' + ml_agent_version[0], language: ['c', 'cpp'])
+add_project_arguments('-DVERSION_MINOR=' + ml_agent_version[1], language: ['c', 'cpp'])
+add_project_arguments('-DVERSION_MICRO=' + ml_agent_version[2], language: ['c', 'cpp'])
+
+# Define warning flags for c and cpp
+warning_flags = [
+  '-Wwrite-strings',
+  '-Wformat',
+  '-Wformat-nonliteral',
+  '-Wformat-security',
+  '-Winit-self',
+  '-Waddress',
+  '-Wno-multichar',
+  '-Wvla',
+  '-Wpointer-arith'
+]
+
+warning_c_flags = [
+  '-Wmissing-declarations',
+  '-Wmissing-include-dirs',
+  '-Wmissing-prototypes',
+  '-Wnested-externs',
+  '-Waggregate-return',
+  '-Wold-style-definition',
+  '-Wdeclaration-after-statement'
+]
+
+# Setup warning flags for c and cpp
+foreach extra_arg : warning_flags
+  if cc.has_argument (extra_arg)
+    add_project_arguments([extra_arg], language: 'c')
+  endif
+  if cxx.has_argument (extra_arg)
+    add_project_arguments([extra_arg], language: 'cpp')
+  endif
+endforeach
+
+foreach extra_arg : warning_c_flags
+  if cc.has_argument (extra_arg)
+    add_project_arguments([extra_arg], language: 'c')
+  endif
+endforeach
+
+# Set project args
+if get_option('enable-tizen')
+  # Pass __TIZEN__ to the compiler
+  add_project_arguments('-D__TIZEN__=1', language: ['c', 'cpp'])
+endif
+
+if get_option('enable-gcov')
+  add_project_arguments('-DENABLE_GCOV=1', language: ['c', 'cpp'])
+endif
+
+serviceDBPath = get_option('service-db-path')
+add_project_arguments('-DSYS_DB_DIR="' + serviceDBPath + '"', language: ['c', 'cpp'])
+
+# Set install path
+ml_agent_install_prefix = get_option('prefix')
+ml_agent_install_libdir = join_paths(ml_agent_install_prefix, get_option('libdir'))
+ml_agent_install_bindir = join_paths(ml_agent_install_prefix, get_option('bindir'))
+ml_agent_install_includedir = join_paths(ml_agent_install_prefix, get_option('includedir'))
+ml_agent_install_inidir = get_option('sysconfdir')
+
+dbus_policy_dir = join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
+dbus_system_service_dir = join_paths(ml_agent_install_prefix, 'share', 'dbus-1', 'system-services')
+systemd_service_dir = join_paths(ml_agent_install_prefix, 'lib', 'systemd', 'system')
+
+# Set default configuration
+ml_agent_conf = configuration_data()
+ml_agent_conf.set('VERSION', ml_agent_version)
+ml_agent_conf.set('PREFIX', ml_agent_install_prefix)
+ml_agent_conf.set('EXEC_PREFIX', ml_agent_install_bindir)
+ml_agent_conf.set('LIB_INSTALL_DIR', ml_agent_install_libdir)
+ml_agent_conf.set('INCLUDE_INSTALL_DIR', ml_agent_install_includedir)
+
+subdir('dbus')
+subdir('daemon')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..8ce9f45
--- /dev/null
@@ -0,0 +1,6 @@
+option('enable-test', type: 'boolean', value: false)
+option('install-test', type: 'boolean', value: false)
+option('enable-tizen', type: 'boolean', value: false)
+option('service-db-path', type: 'string', value: '.')
+option('service-db-key-prefix', type: 'string', value: '')
+option('enable-gcov', type: 'boolean', value: false, description: 'Generate gcov package')