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>
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+ml_agent_headers = files('ml-agent-interface.h')
+
+install_headers(ml_agent_headers,
+ subdir: 'ml-agent'
+)
--- /dev/null
+/**
+ * @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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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')
--- /dev/null
+/**
+ * @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;
+}
--- /dev/null
+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
--- /dev/null
+/* 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)
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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)
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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)
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+<!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>
--- /dev/null
+[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
--- /dev/null
+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
+)
--- /dev/null
+<?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>
--- /dev/null
+[D-BUS Service]
+Name=org.tizen.machinelearning.service
+Exec=/bin/false
+SystemdService=machine-learning-agent.service
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+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')
--- /dev/null
+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')