Update MMI Framework 2.0 04/297204/2
authorJi-hoon Lee <dalton.lee@samsung.com>
Fri, 11 Aug 2023 12:05:33 +0000 (21:05 +0900)
committerJi-hoon Lee <dalton.lee@samsung.com>
Tue, 12 Sep 2023 03:01:20 +0000 (12:01 +0900)
Change-Id: I332cdda776346435880da0dc0f26859e77506feb

213 files changed:
CODEOWNERS
README.md
capi/meson.build [new file with mode: 0644]
capi/mmi-attribute.h [new file with mode: 0644]
capi/mmi-data.h [new file with mode: 0644]
capi/mmi-defines.h [moved from src/mmi-ipc.h with 52% similarity]
capi/mmi-error.h [new file with mode: 0644]
capi/mmi-node-controller.h [new file with mode: 0644]
capi/mmi-node-logic.h [new file with mode: 0644]
capi/mmi-node-processor.h [new file with mode: 0644]
capi/mmi-node-source.h [new file with mode: 0644]
capi/mmi-node.h [new file with mode: 0644]
capi/mmi-plugin-module.h [new file with mode: 0644]
capi/mmi-plugin-storage.h [new file with mode: 0644]
capi/mmi-port.h [new file with mode: 0644]
capi/mmi-primitive-value.h [new file with mode: 0644]
capi/mmi-signal.h [new file with mode: 0644]
capi/mmi-workflow.h [new file with mode: 0644]
capi/mmi.h [new file with mode: 0644]
external/adishavit/LICENSE.txt [new file with mode: 0644]
external/adishavit/argh.h [new file with mode: 0644]
external/cli/LICENSE [new file with mode: 0644]
external/cli/boostasiocliasyncsession.h [new file with mode: 0644]
external/cli/boostasioremotecli.h [new file with mode: 0644]
external/cli/boostasioscheduler.h [new file with mode: 0644]
external/cli/cli.h [new file with mode: 0644]
external/cli/clifilesession.h [new file with mode: 0644]
external/cli/clilocalsession.h [new file with mode: 0644]
external/cli/colorprofile.h [new file with mode: 0644]
external/cli/detail/boostasiolib.h [new file with mode: 0644]
external/cli/detail/commonprefix.h [new file with mode: 0644]
external/cli/detail/fromstring.h [new file with mode: 0644]
external/cli/detail/genericasioremotecli.h [new file with mode: 0644]
external/cli/detail/genericasioscheduler.h [new file with mode: 0644]
external/cli/detail/genericcliasyncsession.h [new file with mode: 0644]
external/cli/detail/history.h [new file with mode: 0644]
external/cli/detail/inputdevice.h [new file with mode: 0644]
external/cli/detail/inputhandler.h [new file with mode: 0644]
external/cli/detail/keyboard.h [new file with mode: 0644]
external/cli/detail/linuxkeyboard.h [new file with mode: 0644]
external/cli/detail/newboostasiolib.h [new file with mode: 0644]
external/cli/detail/newstandaloneasiolib.h [new file with mode: 0644]
external/cli/detail/oldboostasiolib.h [new file with mode: 0644]
external/cli/detail/oldstandaloneasiolib.h [new file with mode: 0644]
external/cli/detail/rang.h [new file with mode: 0644]
external/cli/detail/server.h [new file with mode: 0644]
external/cli/detail/split.h [new file with mode: 0644]
external/cli/detail/standaloneasiolib.h [new file with mode: 0644]
external/cli/detail/terminal.h [new file with mode: 0644]
external/cli/detail/winkeyboard.h [new file with mode: 0644]
external/cli/filehistorystorage.h [new file with mode: 0644]
external/cli/historystorage.h [new file with mode: 0644]
external/cli/loopscheduler.h [new file with mode: 0644]
external/cli/scheduler.h [new file with mode: 0644]
external/cli/standaloneasiocliasyncsession.h [new file with mode: 0644]
external/cli/standaloneasioremotecli.h [new file with mode: 0644]
external/cli/standaloneasioscheduler.h [new file with mode: 0644]
external/cli/volatilehistorystorage.h [new file with mode: 0644]
meson.build
meson_options.txt [deleted file]
packaging/mmi.spec
plugins/meson.build [new file with mode: 0644]
plugins/nodes/camera/meson.build [new file with mode: 0644]
plugins/nodes/camera/mmi-module-camera-preview.cpp [new file with mode: 0644]
plugins/nodes/camera/mmi-module-camera-preview.h [new file with mode: 0644]
plugins/nodes/camera/mmi-module-camera.cpp [new file with mode: 0644]
plugins/nodes/face-recognition/data/recognizerInfo.json [new file with mode: 0644]
plugins/nodes/face-recognition/meson.build [new file with mode: 0644]
plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp [new file with mode: 0644]
plugins/nodes/face-recognition/mmi-module-fr-data-manager.h [new file with mode: 0644]
plugins/nodes/face-recognition/mmi-module-fr.cpp [new file with mode: 0644]
plugins/nodes/face-recognition/mv_face_recognition_internal.h [new file with mode: 0644]
plugins/nodes/match/meson.build [new file with mode: 0644]
plugins/nodes/match/mmi-module-match-text-candidates.cpp [new file with mode: 0644]
plugins/nodes/match/mmi-module-match-text-candidates.h [new file with mode: 0644]
plugins/nodes/match/mmi-module-match.cpp [new file with mode: 0644]
plugins/nodes/meson.build [new file with mode: 0755]
plugins/nodes/mic/meson.build [new file with mode: 0644]
plugins/nodes/mic/mmi-module-mic-recorder.cpp [new file with mode: 0644]
plugins/nodes/mic/mmi-module-mic-recorder.h [new file with mode: 0644]
plugins/nodes/mic/mmi-module-mic.cpp [new file with mode: 0644]
plugins/nodes/regex-match/meson.build [new file with mode: 0644]
plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp [new file with mode: 0644]
plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h [new file with mode: 0644]
plugins/nodes/regex-match/mmi-module-regex-match.cpp [new file with mode: 0644]
plugins/nodes/voice-touch/meson.build [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-common.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp [moved from src/mmi-client.h with 51% similarity]
plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h [new file with mode: 0644]
plugins/nodes/voice-touch/mmi-module-voice-touch.cpp [new file with mode: 0644]
plugins/workflows/meson.build [new file with mode: 0644]
plugins/workflows/script-parser/meson.build [new file with mode: 0644]
plugins/workflows/script-parser/mmi-module-script-parser.cpp [new file with mode: 0644]
plugins/workflows/user-recognition/meson.build [new file with mode: 0644]
plugins/workflows/user-recognition/user-recognition.mws [new file with mode: 0644]
plugins/workflows/voice-touch/meson.build [new file with mode: 0644]
plugins/workflows/voice-touch/mmi-module-voice-touch.cpp [new file with mode: 0644]
plugins/workflows/voice-touch/voice-touch.mws [new file with mode: 0644]
plugins/workflows/wakeupless-command/meson.build [new file with mode: 0644]
plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp [new file with mode: 0644]
src/common/mmi-communication-channel.h [new file with mode: 0644]
src/common/mmi-communication-message.h [new file with mode: 0644]
src/common/mmi-event-observer.h [new file with mode: 0644]
src/common/mmi-plugin-module-event.h [new file with mode: 0644]
src/common/mmi-workflow-event.h [new file with mode: 0644]
src/common/mmi-workflow-output-event.h [new file with mode: 0644]
src/meson.build
src/mmi-cli/cliecoresession.h [new file with mode: 0644]
src/mmi-cli/meson.build [new file with mode: 0644]
src/mmi-cli/mmi-cli-node-tester.cpp [new file with mode: 0644]
src/mmi-cli/mmi-cli.cpp [new file with mode: 0644]
src/mmi-client.c [deleted file]
src/mmi-ipc.c [deleted file]
src/mmi-manager/main.cpp [new file with mode: 0644]
src/mmi-manager/meson.build [new file with mode: 0644]
src/mmi-manager/mmi-client.cpp [new file with mode: 0644]
src/mmi-manager/mmi-client.h [new file with mode: 0644]
src/mmi-manager/mmi-common.cpp [new file with mode: 0644]
src/mmi-manager/mmi-common.h [new file with mode: 0644]
src/mmi-manager/mmi-data-gateway.cpp [new file with mode: 0644]
src/mmi-manager/mmi-data-gateway.h [new file with mode: 0644]
src/mmi-manager/mmi-ipc-tidl.cpp [new file with mode: 0644]
src/mmi-manager/mmi-ipc-tidl.h [new file with mode: 0644]
src/mmi-manager/mmi-manager-log.h [new file with mode: 0644]
src/mmi-manager/mmi-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-manager.xml [new file with mode: 0644]
src/mmi-manager/mmi-node-instance-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-node-instance-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-node-instance.cpp [new file with mode: 0644]
src/mmi-manager/mmi-node-instance.h [new file with mode: 0644]
src/mmi-manager/mmi-node-prototype-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-node-prototype-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-node-prototype.cpp [new file with mode: 0644]
src/mmi-manager/mmi-node-prototype.h [new file with mode: 0644]
src/mmi-manager/mmi-plugin-module-proxy.cpp [new file with mode: 0644]
src/mmi-manager/mmi-plugin-module-proxy.h [new file with mode: 0644]
src/mmi-manager/mmi-plugin-module-registry.cpp [new file with mode: 0644]
src/mmi-manager/mmi-plugin-module-registry.h [new file with mode: 0644]
src/mmi-manager/mmi-port-instance-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-port-instance-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-port-instance.cpp [new file with mode: 0644]
src/mmi-manager/mmi-port-instance.h [new file with mode: 0644]
src/mmi-manager/mmi-self-container.cpp [new file with mode: 0644]
src/mmi-manager/mmi-self-container.h [new file with mode: 0644]
src/mmi-manager/mmi-workflow-instance-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-workflow-instance-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-workflow-instance.cpp [new file with mode: 0644]
src/mmi-manager/mmi-workflow-instance.h [new file with mode: 0644]
src/mmi-manager/mmi-workflow-prototype-manager.cpp [new file with mode: 0644]
src/mmi-manager/mmi-workflow-prototype-manager.h [new file with mode: 0644]
src/mmi-manager/mmi-workflow-prototype.cpp [new file with mode: 0644]
src/mmi-manager/mmi-workflow-prototype.h [new file with mode: 0644]
src/mmi.c [deleted file]
src/mmi.h [deleted file]
src/mmi/meson.build [new file with mode: 0644]
src/mmi/mmi-attribute.cpp [new file with mode: 0644]
src/mmi/mmi-client-manager.h [new file with mode: 0644]
src/mmi/mmi-data.cpp [new file with mode: 0644]
src/mmi/mmi-ipc-tidl.cpp [new file with mode: 0644]
src/mmi/mmi-ipc-tidl.h [new file with mode: 0644]
src/mmi/mmi-log.h [moved from src/mmi-dbg.h with 92% similarity]
src/mmi/mmi-node-logic.cpp [new file with mode: 0644]
src/mmi/mmi-node-processor.cpp [new file with mode: 0644]
src/mmi/mmi-node-source.cpp [new file with mode: 0644]
src/mmi/mmi-node.cpp [new file with mode: 0644]
src/mmi/mmi-plugin-storage-impl.h [new file with mode: 0644]
src/mmi/mmi-plugin-storage.cpp [new file with mode: 0644]
src/mmi/mmi-port.cpp [new file with mode: 0644]
src/mmi/mmi-primitive-value.cpp [new file with mode: 0644]
src/mmi/mmi-signal.cpp [new file with mode: 0644]
src/mmi/mmi-workflow-instance-manager.h [new file with mode: 0644]
src/mmi/mmi-workflow-instance.cpp [new file with mode: 0644]
src/mmi/mmi-workflow-script-parser.h [new file with mode: 0644]
src/mmi/mmi-workflow-script.cpp [new file with mode: 0644]
src/mmi/mmi-workflow.cpp [new file with mode: 0644]
src/mmi/mmi.cpp [new file with mode: 0644]
tests/meson.build
tests/mmi-ipc-test.cpp [deleted file]
tests/mmi-main-test.cpp [deleted file]
tests/mmi-manager/ecore-event-dispatcher.cpp [new file with mode: 0644]
tests/mmi-manager/meson.build [new file with mode: 0644]
tests/mmi-manager/mmi-client-tests.cpp [new file with mode: 0644]
tests/mmi-manager/mmi-manager-main-tests.cpp [new file with mode: 0644]
tests/mmi-manager/mmi-manager-tests.cpp [new file with mode: 0644]
tests/mmi-manager/mmi-manager-tests.h [new file with mode: 0644]
tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp [new file with mode: 0644]
tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp [new file with mode: 0644]
tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp [new file with mode: 0644]
tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp [new file with mode: 0644]
tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp [new file with mode: 0644]
tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp [new file with mode: 0644]
tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp [new file with mode: 0644]
tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp [new file with mode: 0644]
tests/mmi/attribute/mmi-attribute-test.cpp [new file with mode: 0644]
tests/mmi/meson.build [new file with mode: 0644]
tests/mmi/mmi-data-test.cpp [new file with mode: 0644]
tests/mmi/mmi-ipc-test.cpp [new file with mode: 0644]
tests/mmi/mmi-main-test.cpp [new file with mode: 0644]
tests/mmi/mmi-tests.cpp [moved from tests/mmi-tests.cpp with 53% similarity]
tests/mmi/mmi-tests.h [moved from tests/mmi-tests.h with 96% similarity]
tests/mmi/primitive-value/mmi-primitive-value-test.cpp [new file with mode: 0644]
tests/mmi/workflow/mmi-workflow-test.cpp [new file with mode: 0644]
tests/wait-helper.cpp [deleted file]
tidl/mmi.tidl

index e20cb34..9df4ed0 100644 (file)
@@ -2,4 +2,4 @@
 # the repo. Unless a later match takes precedence,
 # @global-owner1 and @global-owner2 will be requested for
 # review when someone opens a pull request.
-*       @TizenWS/mmi-iu-and-voice
+*       @Tizen-Interaction/MMI_FW
index 4579c8a..fa599b0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,26 +1,61 @@
-# MMI(Multi-modal Interaction) Framework Library
-
-[Source Tree]  
+# MMI(Multi-modal Interaction) Framework Library  
+  
+```
+[Source Tree]    
 .  
+├── capi  
+│   ├── meson.build  
+│   ├── mmi-attribute.h  
+│   ├── mmi-data.h  
+│   ├── mmi-error.h  
+│   ├── mmi.h  
+│   ├── mmi-node-gate.h  
+│   ├── mmi-node.h  
+│   ├── mmi-node-processor.h  
+│   ├── mmi-node-source.h  
+│   ├── mmi-port.h  
+│   ├── mmi-primitive-value.h  
+│   ├── mmi-signal.h  
+│   └── mmi-workflow.h  
 ├── CODEOWNERS  
 ├── COPYING  
 ├── meson.build  
 ├── meson_options.txt  
 ├── packaging  
-│   ├── mmi.manifest
-│   └── mmi.spec
-├── README.md
-├── src
-│   ├── interface
-│   │   ├── mmi_proxy.c
-│   │   └── mmi_proxy.h
-│   ├── meson.build
-│   ├── mmi.c
-│   └── mmi.h
-├── tests
-│   ├── meson.build
-│   ├── mmi-tests.cpp
-│   └── mmi-tests.h
-└── tidl
-    └── mmi.tidl
-  
+│   ├── mmi.manifest  
+│   └── mmi.spec  
+├── README.md  
+├── src  
+│   ├── common  
+│   │   ├── mmi-communication-channel.h  
+│   │   ├── mmi-communication-message.h  
+│   │   └── mmi-event-observer.h  
+│   ├── meson.build  
+│   ├── mmi-attribute.cpp  
+│   ├── mmi-client-manager.h  
+│   ├── mmi.cpp  
+│   ├── mmi-data.cpp  
+│   ├── mmi-ipc-tidl.cpp  
+│   ├── mmi-ipc-tidl.h  
+│   ├── mmi-log.h  
+│   ├── mmi-node.cpp  
+│   ├── mmi-node-gate.cpp  
+│   ├── mmi-node-processor.cpp  
+│   ├── mmi-node-source.cpp  
+│   ├── mmi-port.cpp  
+│   ├── mmi-primitive-value.cpp  
+│   ├── mmi-signal.cpp  
+│   ├── mmi-workflow.cpp  
+│   └── mmi-workflow-instance-manager.h  
+├── tests  
+│   ├── meson.build  
+│   ├── mmi-attribute-test.cpp  
+│   ├── mmi-data-test.cpp  
+│   ├── mmi-ipc-test.cpp  
+│   ├── mmi-main-test.cpp  
+│   ├── mmi-primitive-value-test.cpp  
+│   ├── mmi-tests.cpp  
+│   └── mmi-tests.h  
+└── tidl  
+    └── mmi.tidl  
+```
diff --git a/capi/meson.build b/capi/meson.build
new file mode 100644 (file)
index 0000000..a3a3de2
--- /dev/null
@@ -0,0 +1,17 @@
+install_headers(
+       'mmi-primitive-value.h',
+       'mmi-data.h',
+       'mmi-attribute.h',
+       'mmi-error.h',
+       'mmi-workflow.h',
+       'mmi-node.h',
+       'mmi-node-source.h',
+       'mmi-node-processor.h',
+       'mmi-node-logic.h',
+       'mmi-port.h',
+       'mmi-signal.h',
+       'mmi-defines.h',
+       'mmi-plugin-module.h',
+       'mmi-plugin-storage.h',
+       'mmi.h',
+       )
diff --git a/capi/mmi-attribute.h b/capi/mmi-attribute.h
new file mode 100644 (file)
index 0000000..f09d947
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_ATTRIBUTE_H__
+#define __TIZEN_UIX_MMI_ATTRIBUTE_H__
+
+
+#include <tizen.h>
+#include <stdlib.h>
+#include <mmi-primitive-value.h>
+
+
+/**
+* @file mmi-attribute.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mmi_attribute_s* mmi_attribute_h;
+
+
+int mmi_attribute_create(mmi_primitive_value_h value, const char *name,  mmi_attribute_h *attribute);
+
+int mmi_attribute_set_name(mmi_attribute_h attribute, const char *name);
+
+int mmi_attribute_get_name(mmi_attribute_h attribute, char **name);
+
+int mmi_attribute_get_value(mmi_attribute_h attribute, mmi_primitive_value_h *value);
+
+int mmi_attribute_clone(mmi_attribute_h attribute, mmi_attribute_h *cloned);
+
+int mmi_attribute_destroy(mmi_attribute_h attribute);
+
+int mmi_attribute_to_bytes(mmi_attribute_h attribute, unsigned char **bytes, size_t *length);
+
+int mmi_attribute_from_bytes(const unsigned char *bytes, size_t length, mmi_attribute_h *attribute);
+
+/* Utility functions */
+int mmi_attribute_create_string_array(const char *name, const char *strings[], size_t count, mmi_attribute_h *attribute);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_ATTRIBUTE_H__ */
diff --git a/capi/mmi-data.h b/capi/mmi-data.h
new file mode 100644 (file)
index 0000000..e1a8b5a
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_DATA_H__
+#define __TIZEN_UIX_MMI_DATA_H__
+
+
+#include <tizen.h>
+#include <stdlib.h>
+
+
+/**
+* @file mmi-data.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+    MMI_DATA_TYPE_BOOLEAN,
+    MMI_DATA_TYPE_INTEGER,
+    MMI_DATA_TYPE_FLOAT,
+    MMI_DATA_TYPE_TEXT,
+    MMI_DATA_TYPE_JSON,
+    MMI_DATA_TYPE_USER_IDENTIFICATION,
+    MMI_DATA_TYPE_AUDIO,
+    MMI_DATA_TYPE_VIDEO,
+    MMI_DATA_TYPE_BOUNDING_BOX,
+    MMI_DATA_TYPE_ARRAY,
+    MMI_DATA_TYPE_STRUCT,
+    MMI_DATA_TYPE_ANY,
+} mmi_data_type_e;
+
+/* Data Format */
+/*
+- MMI_DATA_BOOLEAN
+    data : pointer to a byte
+    datalen : 1 byte
+    description : false if all bits are 0, true otherwise
+
+- MMI_DATA_INTEGER
+    data : pointer to a signed integer type
+    datalen : 4 byte
+    description : cast 'data' to a 32-bit signed integer (e.g. int)
+
+- MMI_DATA_FLOAT
+    data : pointer to a floating-point type
+    datalen : 4 byte
+    description : cast 'data' to a 32-bit floating-point type (e.g. float)
+
+- MMI_DATA_TEXT
+    data : pointer to a null-terminated string
+    datalen : length of the text
+    description : cast 'data' to a char*
+
+- MMI_DATA_JSON
+    data : pointer to a null-terminated string
+    datalen : length of the json string
+    description : cast 'data' to a char*
+
+- MMI_DATA_USER_IDENTIFICATION
+    data : pointer to a platform-dependent id for a user
+    datalen : platform-dependent
+    description : this value can be used to acquire platform-dependent user information
+
+- MMI_DATA_AUDIO
+    data : pointer to bytes
+    datalen : size of 'data' in bytes
+    format:
+    {
+        "bitrate" : 48000,
+        "format" : {
+            "signed" : true,
+            "bits" : 16
+        },
+        "channels" : 1
+    }
+    description : audio frame data
+
+- MMI_DATA_VIDEO
+    data : pointer to bytes
+    datalen : size of 'data' in bytes
+    format:
+    {
+        "width":1920,
+        "height":1080,
+        "encoding":"mp4"
+    }
+    description : video frame data
+
+- MMI_DATA_BOUNDING_BOX
+    data : pointer to json string
+    datalen : length of the json string
+    description : data is composed in below json format
+    {
+        "x":300,
+        "y":150,
+        "w":200,
+        "h":200
+    }
+
+- MMI_DATA_ARRAY
+    data : pointer to an array of mmi_data_h struct
+    datalen : size of a pointer * number of items
+    description : A container that can hold multiple mmi_data_h with the same type.
+
+- MMI_DATA_STRUCT
+    data : pointer to an array of mmi_data_h struct
+    datalen : size of a pointer * number of items * 2 (one for key, one for value)
+    description : A container that can hold multiple mmi_data_h with different types.
+                  A key is a MMI_DATA_TEXT that identifies the value.
+- MMI_DATA_ANY
+    data : pointer to an mmi_data_h struct
+    datalen : size of a pointer
+    description : Represents a data type that can be any of the above types.
+*/
+
+
+
+
+typedef struct mmi_data_timestamp_s *mmi_data_timestamp_h;
+
+typedef struct mmi_data_s *mmi_data_h;
+
+int mmi_data_create_bool(bool value, mmi_data_h *data);
+
+int mmi_data_create_int(int value, mmi_data_h *data);
+
+int mmi_data_create_float(float value, mmi_data_h *data);
+
+int mmi_data_create_text(const char *value, mmi_data_h *data);
+
+int mmi_data_create_audio(const void *ptr, size_t len, mmi_data_h *data);
+
+int mmi_data_create_video(const void *ptr, size_t len, mmi_data_h *data);
+
+int mmi_data_create_user_identification(const void *ptr, size_t len, mmi_data_h *data);
+
+int mmi_data_create_array(mmi_data_h *array_handle);
+
+int mmi_data_add_array_element(mmi_data_h array_handle, mmi_data_h element);
+
+int mmi_data_create_struct(mmi_data_h *struct_handle);
+
+int mmi_data_set_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h element);
+
+int mmi_data_get_type(mmi_data_h data, mmi_data_type_e *type);
+
+int mmi_data_get_bool(mmi_data_h data, bool *value);
+
+int mmi_data_get_int(mmi_data_h data, int *value);
+
+int mmi_data_get_float(mmi_data_h data, float *value);
+
+int mmi_data_get_text(mmi_data_h data, const char **string);
+
+int mmi_data_get_audio(mmi_data_h data, const void **ptr, size_t *len);
+
+int mmi_data_get_video(mmi_data_h data, const void **ptr, size_t *len);
+
+int mmi_data_get_user_identification(mmi_data_h data, const void **ptr, size_t *len);
+
+int mmi_data_get_array_count(mmi_data_h array, size_t *count);
+
+int mmi_data_get_array_element(mmi_data_h array, size_t index, mmi_data_h *element);
+
+int mmi_data_get_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h *element);
+
+int mmi_data_get_struct_count(mmi_data_h struct_handle, size_t *count);
+
+int mmi_data_get_struct_element_name(mmi_data_h struct_handle, size_t index, const char **string);
+
+int mmi_data_get_struct_element_value(mmi_data_h struct_handle, size_t index, mmi_data_h *element);
+
+int mmi_data_get_node_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp);
+
+int mmi_data_get_source_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp);
+
+int mmi_data_set_source_timestamp(mmi_data_h data, mmi_data_timestamp_h timestamp);
+
+int mmi_data_to_bytes(mmi_data_h data, unsigned char **bytes, size_t *length);
+
+int mmi_data_from_bytes(unsigned char *bytes, size_t length, mmi_data_h *data);
+
+int mmi_data_destroy(mmi_data_h data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_DATA_H__ */
similarity index 52%
rename from src/mmi-ipc.h
rename to capi/mmi-defines.h
index 8c4367f..e8d26ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  *
  */
 
-#ifndef __MMI_IPC_H__
-#define __MMI_IPC_H__
 
-#include "mmi_proxy.h"
-#include "mmi.h"
+#ifndef __TIZEN_UIX_MMI_DEFINES_H__
+#define __TIZEN_UIX_MMI_DEFINES_H__
+
+
+/**
+* @file mmi-defines.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-int mmi_ipc_initialize(void);
-void mmi_ipc_deinitialize(void);
-int mmi_ipc_register_input_event_result_cb(int input_event_type, rpc_port_proxy_mmi_result_cb_cb callback);
-int mmi_ipc_activate_input_event(int input_event_type);
-int mmi_ipc_deactivate_input_event(int input_event_type);
 
-rpc_port_proxy_mmi_h mmi_ipc_get_rpc_h(void);
-int mmi_ipc_get_uid(void);
-bool mmi_ipc_is_connected(void);
-const char *mmi_ipc_get_stub_appid(void);
+#define MMI_NAME_MAX_LENGTH 32
+#define MMI_PARAMETER_MAX_COUNT 32
+#define MMI_PORT_MAX_COUNT 32
+#define MMI_NODE_MAX_COUNT 128
+#define MMI_LINK_MAX_COUNT 128
+#define MMI_OUTPUT_MAX_COUNT 32
+#define MMI_WORKFLOW_MAX_COUNT 64
+
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif //__MMI_IPC_H__
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_DEFINES_H__ */
diff --git a/capi/mmi-error.h b/capi/mmi-error.h
new file mode 100644 (file)
index 0000000..edbf780
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_ERROR_H__
+#define __TIZEN_UIX_MMI_ERROR_H__
+
+
+#include <tizen.h>
+
+
+/**
+* @file mmi-error.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define TIZEN_ERROR_MMI        -0x030F0000
+
+typedef enum {
+    MMI_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
+    MMI_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of Memory */
+    MMI_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR, /**< I/O error */
+    MMI_ERROR_INVALID_PARAMETER        = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
+    MMI_ERROR_OUT_OF_NETWORK = TIZEN_ERROR_NETWORK_DOWN, /**< Network is down */
+    MMI_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< No answer from the daemon */
+    MMI_ERROR_PERMISSION_DENIED        = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */
+    MMI_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< MMI NOT supported */
+    MMI_ERROR_OPERATION_FAILED = TIZEN_ERROR_MMI | 0x01, /**< Operation failed */
+    MMI_ERROR_RESOURCE_BUSY = TIZEN_ERROR_MMI | 0x02, /**< Resource busy */
+} mmi_error_e;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_ERROR_H__ */
diff --git a/capi/mmi-node-controller.h b/capi/mmi-node-controller.h
new file mode 100644 (file)
index 0000000..b92a4a1
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_NODE_CONTROLLER_H__
+#define __TIZEN_UIX_MMI_NODE_CONTROLLER_H__
+
+
+
+#include <mmi-node.h>
+
+/**
+* @file mmi-node-controller.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum mmi_standard_node_controller_type_e {
+    MMI_STANDARD_NODE_CONTROLLER_TYPE_NONE,
+
+    /* - Description : Turns the node with a name specified in the attribute "TARGET" on or off
+        - Attributes :
+            "TARGET" : string
+        - Input Port 1
+            Name : "VALUE"
+            Data Type : MMI_DATA_TYPE_BOOLEAN
+    */
+    MMI_STANDARD_NODE_CONTROLLER_TYPE_SWITCH,
+};
+
+int mmi_standard_node_create_controller(mmi_standard_node_controller_type_e type, mmi_node_h *node);
+
+int mmi_standard_node_get_controller_type(mmi_node_h node, mmi_standard_node_controller_type_e *type);
+
+int mmi_standard_node_register_controller(mmi_standard_node_controller_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_NODE_LOGIC_H__ */
diff --git a/capi/mmi-node-logic.h b/capi/mmi-node-logic.h
new file mode 100644 (file)
index 0000000..39f2c60
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_NODE_LOGIC_H__
+#define __TIZEN_UIX_MMI_NODE_LOGIC_H__
+
+
+
+#include <mmi-node.h>
+
+/**
+* @file mmi-node-logic.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum mmi_standard_node_logic_type_e {
+    MMI_STANDARD_NODE_LOGIC_TYPE_NONE,
+
+    /*
+        - Description : Compares the text received from the input port 1 ("TEXT") with
+                        1) the fixed strings specified in the attribute "CANDIDATES"
+                        2) the array of texts received from input port 2 ("TEXT_ARRAY")
+                        3) the array of structs received from input port 3 ("STRUCT_ARRAY"),
+                           for the element specified in the attribute "COMPARAND_STRUCT_ELEMENT"
+                           ex) if the struct has two elements named "label" and "text", and
+                               "COMPARAND_STRUCT_ELEMENT" is "label", then the value of
+                               "label" will be compared with the text received from input port 1
+                        And outputs the mmi_data_t to the corresponding OUT port when matched.
+                        ex1) if the text received from input port 1 is "Hello", and the fixed strings
+                             specified in the attribute "CANDIDATES" is ["Hello", "World"], then the
+                             text "Hello" will be sent to output port 1.
+                        ex2) if the text received from input port 1 is "Hello", and the array of texts
+                             received from input port 2 is ["Hello", "World", "Goodbye"], then the
+                             text "Hello" will be sent to output port 2.
+                        ex3) if the text received from input port 1 is "Hello", with the attribute
+                             "COMPARAND_STRUCT_ELEMENT" set to "label", and the array of structs
+                             received from input port 3 is like below,
+                             [
+                                {"label":"Hello"},
+                                {"label":"World"},
+                                {"label":"Goodbye"}
+                             ]
+                             then the struct {"label":"Hello"} will be sent to output port 3.
+        - Attributes :
+            "CANDIDATES" : array [ string ]
+            "COMPARAND_STRUCT_ELEMENT" : string
+        - Input Port 1
+            Name : "TEXT"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Input Port 2
+            Name : "TEXT_ARRAY"
+            Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_TEXT ]
+        - Input Port 3
+            Name : "STRUCT_ARRAY"
+            Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_STRUCT ]
+        - Output Port 1
+            Name : "MATCHED_CANDIDATE"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 2
+            Name : "MATCHED_TEXT_ARRAY"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 3
+            Name : "MATCHED_STRUCT_ARRAY"
+            Data Type : MMI_DATA_TYPE_STRUCT
+    */
+    MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH,
+
+    /*
+        - Description : Tests if the regex strings specified in the attribute "REGEX" with
+                        1) the text received from the input port 1 ("TEXT") matches
+                        2) the array of texts received from input port 2 ("TEXT_ARRAY")
+                        3) the array of structs received from input port 3 ("STRUCT_ARRAY"),
+                           for the element specified in the attribute "COMPARAND_STRUCT_ELEMENT"
+        - Attributes :
+            "REGEX" : array [ string ]
+            "COMPARAND_STRUCT_ELEMENT" : string
+        - Input Port 1
+            Name : "TEXT"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Input Port 2
+            Name : "TEXT_ARRAY"
+            Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_TEXT ]
+        - Input Port 3
+            Name : "STRUCT_ARRAY"
+            Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_STRUCT ]
+        - Output Port 1
+            Name : "MATCHED_TEXT"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 2
+            Name : "MATCHED_TEXT_ARRAY"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 3
+            Name : "MATCHED_STRUCT_ARRAY"
+            Data Type : MMI_DATA_TYPE_STRUCT
+    */
+    MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH,
+
+
+    /*
+        - Description : Compares the User Recognition Struct from each input ports
+                        and selects the one with the highest score.
+        - Input Port 1
+            Name : "Port1"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            Format of MMI_DATA_TYPE_STRUCT :
+                - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION
+                - "SCORE" : MMI_DATA_TYPE_FLOAT
+        - Input Port 2
+            Name : "Port2"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            Format of MMI_DATA_TYPE_STRUCT :
+                - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION
+                - "SCORE" : MMI_DATA_TYPE_FLOAT
+        - Output Port 1
+            Name : "BYPASS_RESULT"
+            Data Type : MMI_DATA_TYPE_USER_IDENTIFICATION
+        - Output Port 2
+            Name : "COMPARISON_RESULT"
+            Data Type : MMI_DATA_TYPE_USER_IDENTIFICATION
+    */
+    MMI_STANDARD_NODE_LOGIC_TYPE_USER_COMPARISON,
+};
+
+int mmi_standard_node_create_logic(mmi_standard_node_logic_type_e type, mmi_node_h *node);
+
+int mmi_standard_node_get_logic_type(mmi_node_h node, mmi_standard_node_logic_type_e *type);
+
+int mmi_standard_node_register_logic(mmi_standard_node_logic_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_NODE_LOGIC_H__ */
diff --git a/capi/mmi-node-processor.h b/capi/mmi-node-processor.h
new file mode 100644 (file)
index 0000000..3cd7551
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_NODE_PROCESSOR_H__
+#define __TIZEN_UIX_MMI_NODE_PROCESSOR_H__
+
+
+
+#include <mmi-node.h>
+
+/**
+* @file mmi-node-processor.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum mmi_standard_node_processor_type_e {
+    MMI_STANDARD_NODE_PROCESSOR_TYPE_NONE,
+
+    /*
+        - Attributes : None
+        - Input Port 1
+            Name : "AUDIO"
+            Data Type : MMI_DATA_TYPE_AUDIO
+        - Output Port 1
+            Name : "PARTIAL"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 2
+            Name : "FINAL"
+            Data Type : MMI_DATA_TYPE_TEXT
+    */
+    MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR,
+
+    /*
+        - Attributes : None
+        - Input Port 1
+            Name : "VIDEO"
+            Data Type : MMI_DATA_TYPE_VIDEO
+        - Output Port 1
+            Name : "MATCHED_FACES"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            Format of MMI_DATA_TYPE_STRUCT :
+                - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION
+                - "SCORE" : MMI_DATA_TYPE_FLOAT
+    */
+    MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION,
+
+    /*
+        - Attributes : None
+        - Input Port 1
+            Name : "AUDIO"
+            Data Type : MMI_DATA_TYPE_AUDIO
+        - Output Port 1
+            Name : "MATCHED_SPEAKERS"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            Format of MMI_DATA_TYPE_STRUCT :
+                - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION
+                - "SCORE" : MMI_DATA_TYPE_FLOAT
+    */
+    MMI_STANDARD_NODE_PROCESSOR_TYPE_SPEAKER_RECOGNITION,
+
+    /*
+        - Attributes :
+            "GRID_CONFIGURATION" : array [ integer ]
+        - Input Port 1
+            Name : "SCREEN_INFO"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            MMI_DATA_TYPE STRUCT format: {
+                "coord_x": MMI_DATA_TYPE_INT,
+                "coord_y": MMI_DATA_TYPE_INT,
+                "width": MMI_DATA_TYPE_INT,
+                "height": MMI_DATA_TYPE_INT,
+                "object_id": MMI_DATA_TYPE_TEXT,
+                "label": MMI_DATA_TYPE_TEXT,
+                "role": MMI_DATA_TYPE_TEXT,
+            }
+        - Input Port 2
+            Name : "MODE_COMMANDS"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Input Port 3
+            Name : "UTTERANCE"
+            Data Type : MMI_DATA_TYPE_TEXT
+        - Output Port 1
+            Name : "CANDIDATES"
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            MMI_DATA_TYPE STRUCT format: {
+                "mode": MMI_DATA_TYPE_INT,
+                "coord_x": MMI_DATA_TYPE_INT,
+                "coord_y": MMI_DATA_TYPE_INT,
+                "width": MMI_DATA_TYPE_INT,
+                "height": MMI_DATA_TYPE_INT,
+                "label": MMI_DATA_TYPE_TEXT,
+            }
+        - Output Port 2
+            Name : "MATCHED_RESULT"
+            Data Type : MMI_DATA_TYPE_STRUCT
+            MMI_DATA_TYPE STRUCT format: {
+                "candidate_info": MMI_DATA_TYPE_STRUCT {
+                    "mode": MMI_DATA_TYPE_INT,
+                    "coord_x": MMI_DATA_TYPE_INT,
+                    "coord_y": MMI_DATA_TYPE_INT,
+                    "width": MMI_DATA_TYPE_INT,
+                    "height": MMI_DATA_TYPE_INT,
+                    "label": MMI_DATA_TYPE_TEXT,
+                },
+                "click_info": MMI_DATA_TYPE_STRUCT {
+                    "x": MMI_DATA_TYPE_INT,
+                    "y": MMI_DATA_TYPE_INT,
+                    "object_id": MMI_DATA_TYPE_TEXT,
+                }
+            }
+        - Output Port 3
+            Name : "REJECTED"
+            Data Type : MMI_DATA_TYPE_TEXT
+    */
+    MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH,
+};
+
+int mmi_standard_node_create_processor(mmi_standard_node_processor_type_e type, mmi_node_h *node);
+
+int mmi_standard_node_get_processor_type(mmi_node_h node, mmi_standard_node_processor_type_e *type);
+
+int mmi_standard_node_register_processor(mmi_standard_node_processor_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_NODE_PROCESSOR_H__ */
diff --git a/capi/mmi-node-source.h b/capi/mmi-node-source.h
new file mode 100644 (file)
index 0000000..dbb445f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_NODE_SOURCE_H__
+#define __TIZEN_UIX_MMI_NODE_SOURCE_H__
+
+
+
+#include <mmi-node.h>
+
+/**
+* @file mmi-node-source.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum mmi_standard_node_source_type_e {
+    MMI_STANDARD_NODE_SOURCE_TYPE_NONE,
+
+    /*
+        - Description : A microphone that captures sound all the time.
+        - Attributes :
+            - "UNFOCUSED_ONLY" : bool
+                Default : false
+                Description : If true, the node will be activated only when there is
+                              no other application currently acquired sound focus.
+        - Output Port 1
+            Name : "AUDIO"
+            Type : OUT
+            Data Type : MMI_DATA_TYPE_AUDIO
+    */
+    MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT,
+
+    /*
+        - Description : A camera that captures video.
+        - Output Port 1
+            Name : "VIDEO"
+            Type : OUT
+            Data Type : MMI_DATA_TYPE_VIDEO
+     */
+    MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA,
+
+    /*
+        - Description : A screen analyzer that gets screen info when rescanning occur.
+        // FIXME: temporary attribute for replacing signal
+        - Attributes :
+            - "RESCANNING" : bool
+                Default : false
+                Description : If true, the node will rescan the screen and make screen info
+        - Output Port 1
+            Name : "SCREEN_INFO"
+            Type : OUT
+            Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT
+            MMI_DATA_TYPE STRUCT format: {
+                "coord_x": MMI_DATA_TYPE_INT,
+                "coord_y": MMI_DATA_TYPE_INT,
+                "width": MMI_DATA_TYPE_INT,
+                "height": MMI_DATA_TYPE_INT,
+                "object_id": MMI_DATA_TYPE_TEXT,
+                "label": MMI_DATA_TYPE_TEXT,
+                "role": MMI_DATA_TYPE_TEXT,
+            }
+    */
+    MMI_STANDARD_NODE_SOURCE_TYPE_SCREEN_ANALYZER,
+};
+
+int mmi_standard_node_create_source(mmi_standard_node_source_type_e type, mmi_node_h *node);
+
+int mmi_standard_node_get_source_type(mmi_node_h node, mmi_standard_node_source_type_e *type);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_NODE_SOURCE_H__ */
diff --git a/capi/mmi-node.h b/capi/mmi-node.h
new file mode 100644 (file)
index 0000000..124ecca
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_NODE_H__
+#define __TIZEN_UIX_MMI_NODE_H__
+
+
+#include <tizen.h>
+
+#include <mmi-attribute.h>
+#include <mmi-data.h>
+#include <mmi-error.h>
+#include <mmi-port.h>
+#include <mmi-signal.h>
+
+/**
+* @file mmi-node.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum mmi_standard_node_type_e {
+    MMI_STANDARD_NODE_TYPE_NONE,
+    MMI_STANDARD_NODE_TYPE_SOURCE,
+    MMI_STANDARD_NODE_TYPE_PROCESSOR,
+    MMI_STANDARD_NODE_TYPE_LOGIC,
+    MMI_STANDARD_NODE_TYPE_CONTROLLER,
+};
+
+typedef void* mmi_node_instance_h;
+
+typedef int (*mmi_node_initialized_cb)(mmi_node_instance_h instance);
+typedef int (*mmi_node_deinitialized_cb)(mmi_node_instance_h instance);
+typedef int (*mmi_node_attribute_set_cb)(mmi_node_instance_h instance, mmi_attribute_h attribute);
+typedef int (*mmi_node_activated_cb)(mmi_node_instance_h instance);
+typedef int (*mmi_node_deactivated_cb)(mmi_node_instance_h instance);
+typedef int (*mmi_node_signal_received_cb)(mmi_node_instance_h instance, mmi_signal_h signal);
+
+typedef struct {
+    mmi_node_initialized_cb initialized_cb;
+    mmi_node_deinitialized_cb deinitialized_cb;
+    mmi_node_attribute_set_cb attribute_set_cb;
+    mmi_node_activated_cb activated_cb;
+    mmi_node_deactivated_cb deactivated_cb;
+    mmi_node_signal_received_cb signal_received_cb;
+} mmi_node_callbacks;
+
+typedef struct {
+    mmi_standard_node_type_e type;
+    int sub_type;
+    mmi_port_h *ports;
+    size_t port_count;
+    mmi_node_callbacks callbacks;
+} mmi_node_s;
+
+typedef mmi_node_s* mmi_node_h;
+
+/* Adds a static port to a static node. The port has to be already created before calling this function. */
+int mmi_node_add_port(mmi_node_h node, mmi_port_h port);
+
+/* Finds a port in a node matching the given name and type. */
+int mmi_node_find_port(mmi_node_h node, mmi_port_type_e port_type, const char *port_name, mmi_port_h *port);
+
+/* Retrieves the type of a node. */
+int mmi_node_get_type(mmi_node_h node, mmi_standard_node_type_e *type);
+
+/* Retrieves the number of ports in a node. */
+int mmi_node_get_port_count(mmi_node_h node, size_t *port_count);
+
+/* Retrieves the port at the given index. */
+int mmi_node_get_port(mmi_node_h node, size_t index, mmi_port_h *port);
+
+/* Get the callbacks of a node */
+int mmi_node_get_callbacks(mmi_node_h node, mmi_node_callbacks *callbacks);
+
+/* Set the callbacks of a node */
+int mmi_node_set_callbacks(mmi_node_h node, mmi_node_callbacks callbacks);
+
+/* Registers a node. */
+int mmi_node_register(mmi_node_h node);
+
+/* Clones a node. */
+int mmi_node_clone(mmi_node_h node, mmi_node_h *cloned);
+
+/* Destroys a node. */
+int mmi_node_destroy(mmi_node_h node);
+
+/* Provide a attribute to a node instance. */
+int mmi_node_instance_set_attribute(mmi_node_instance_h instance, mmi_attribute_h attribute);
+
+/* Provide a attribute bundle to a node instance. */
+//int mmi_node_instance_attribute_bundle_set(mmi_node_instance_h instance, mmi_attribute_bundle_h bundle);
+
+/* Retrieve a port instance of a node instance. */
+int mmi_node_instance_find_port(mmi_node_instance_h instance, mmi_port_type_e port_type, const char *port_name, mmi_port_instance_h *port);
+
+/* Retrieve a sibiling port instance of a port instance in a node, that matches the given name. */
+int mmi_node_instance_find_sibling_port(mmi_port_instance_h instance, const char *sibling_name, mmi_port_instance_h *sibling);
+
+/* Emits a signal from a node instance. */
+int mmi_node_instance_emit_signal(mmi_node_instance_h instance, mmi_signal_h signal);
+
+/* If the correct result could not be returned on last activate_cb() call, it needs to be updated afterwards using this function. */
+int mmi_node_instance_update_pending_activation_result(mmi_error_e result);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_NODE_H__ */
diff --git a/capi/mmi-plugin-module.h b/capi/mmi-plugin-module.h
new file mode 100644 (file)
index 0000000..7581a15
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_PLUGIN_MODULE_H__
+#define __TIZEN_UIX_MMI_PLUGIN_MODULE_H__
+
+
+#include <mmi.h>
+
+/**
+* @file mmi-plugin-module.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If a plugin module wants to provide Nodes to mmi framework,
+ * it should implement a function named "mmi_plugin_module_get_node_list",
+ * with the following prototype. */
+#define MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME "mmi_plugin_module_get_node_list"
+typedef void (*mmi_plugin_module_get_node_list_func)();
+
+/* If a plugin module wants to provide Workflows to mmi framework,
+ * it should implement a function named "mmi_plugin_module_get_workflow_list",
+ * with the following prototype. */
+#define MMI_PLUGIN_MODULE_GET_WORKFLOW_LIST_FUNC_NAME "mmi_plugin_module_get_workflow_list"
+typedef void (*mmi_plugin_module_get_workflow_list_func)();
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_PLUGIN_MODULE_H__ */
diff --git a/capi/mmi-plugin-storage.h b/capi/mmi-plugin-storage.h
new file mode 100644 (file)
index 0000000..b68e22c
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__
+#define __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__
+
+
+#include <mmi.h>
+#include <mmi-defines.h>
+#include <mmi-node.h>
+#include <mmi-node-source.h>
+#include <mmi-node-processor.h>
+#include <mmi-node-logic.h>
+#include <mmi-node-controller.h>
+#include <mmi-workflow.h>
+
+/**
+* @file mmi-plugin-storage.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The functions and structs below are used by MMI framework */
+
+typedef struct {
+    mmi_node_h *nodes;
+    size_t node_count;
+} mmi_plugin_module_node_list_s;
+
+typedef struct {
+    mmi_workflow_h *workflows;
+    size_t workflow_count;
+} mmi_plugin_module_workflow_list_s;
+
+
+typedef struct {
+    char name[MMI_NAME_MAX_LENGTH];
+    mmi_port_instance_h port;
+} mmi_plugin_module_port_instance_info_s;
+
+typedef struct {
+    mmi_node_instance_h node;
+    mmi_plugin_module_port_instance_info_s port_infos[MMI_PORT_MAX_COUNT];
+    size_t port_info_count;
+} mmi_plugin_module_node_instance_info_s;
+
+typedef void (*mmi_plugin_module_port_instance_output_handler_func)(
+    mmi_port_instance_h port, mmi_data_h data, void *user_data);
+
+typedef void (*mmi_plugin_module_switch_node_event_handler_func)(
+    mmi_node_instance_h node, const char *controllee, bool state, void *user_data);
+
+typedef struct {
+    mmi_plugin_module_port_instance_output_handler_func port_instance_output_handler;
+    mmi_plugin_module_switch_node_event_handler_func switch_node_event_handler;
+} mmi_plugin_module_event_handler_info_s;
+
+/* MMI Manager will invoke this function to set the current identifier of the plugin module. */
+void mmi_plugin_storage_set_current_module_identifier(const char *identifier);
+
+/* Each plugin modules will call this functino to get the current identifier of the plugin module. */
+const char* mmi_plugin_storage_get_current_module_identifier();
+
+/* Each plugin modules will call these functions to register their nodes and workflows. */
+void mmi_plugin_storage_set_node_list(mmi_plugin_module_node_list_s node_list);
+void mmi_plugin_storage_set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list);
+
+/* MMI Manager will invoke these functions to get the node list of the plugin module. */
+mmi_plugin_module_node_list_s mmi_plugin_storage_get_node_list(const char *identifier);
+mmi_plugin_module_workflow_list_s mmi_plugin_storage_get_workflow_list(const char *identifier);
+
+/* MMI Manager will invoke these function to notify the node instance addition and removal. */
+void mmi_plugin_storage_add_node_instance_info(mmi_plugin_module_node_instance_info_s *info);
+void mmi_plugin_storage_remove_node_instance_info(mmi_node_instance_h node_instance);
+
+/* Each plugin modules will call this function to find appropriate port instance */
+mmi_port_instance_h mmi_plugin_storage_find_port_instance(
+    mmi_node_instance_h node_instance, const char *port_name);
+mmi_node_instance_h mmi_plugin_storage_find_node_instance_by_port_instance(mmi_port_instance_h port_instance);
+
+/* MMI Manager will invoke these function to register / unregister the event handlers */
+void mmi_plugin_storage_set_plugin_module_event_handler(
+    mmi_plugin_module_event_handler_info_s handler_info, void *user_data);
+void mmi_plugin_storage_unset_plugin_module_event_handler();
+
+/* Each plugin modules will call this function to notify the plugin module events */
+mmi_plugin_module_event_handler_info_s mmi_plugin_storage_get_plugin_module_event_handler();
+void* mmi_plugin_storage_get_plugin_module_event_handler_user_data();
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__ */
diff --git a/capi/mmi-port.h b/capi/mmi-port.h
new file mode 100644 (file)
index 0000000..2efc3de
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_PORT_H__
+#define __TIZEN_UIX_MMI_PORT_H__
+
+
+#include <tizen.h>
+
+#include <mmi-data.h>
+#include <mmi-defines.h>
+#include <mmi-error.h>
+
+/**
+* @file mmi-port.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum mmi_port_type_e {
+    MMI_PORT_TYPE_IN,
+    MMI_PORT_TYPE_OUT
+};
+
+typedef void* mmi_port_instance_h;
+
+typedef int (*mmi_port_output_format_requested_cb)(mmi_port_instance_h instance, const char *format);
+typedef int (*mmi_port_input_data_received_cb)(mmi_port_instance_h instance, mmi_data_h data);
+
+typedef struct {
+    mmi_port_output_format_requested_cb output_format_requested_cb;
+    mmi_port_input_data_received_cb input_data_received_cb;
+} mmi_port_callbacks;
+
+typedef struct {
+    char name[MMI_NAME_MAX_LENGTH];
+    mmi_port_type_e type;
+    mmi_data_type_e data_type;
+    mmi_port_callbacks callbacks;
+} mmi_port_s;
+
+typedef mmi_port_s* mmi_port_h;
+
+/* Create a port */
+int mmi_port_create(mmi_port_h *port);
+
+/* Get the name of a port */
+int mmi_port_get_name(mmi_port_h port, char **name, size_t *length);
+
+/* Get the type of a port */
+int mmi_port_get_type(mmi_port_h port, mmi_port_type_e *type);
+
+/* Get the data type of a port */
+int mmi_port_get_data_type(mmi_port_h port, mmi_data_type_e *data_type);
+
+/* Get the callbacks of a port */
+int mmi_port_get_callbacks(mmi_port_h port, mmi_port_callbacks *callbacks);
+
+/* Set the name of a port */
+int mmi_port_set_name(mmi_port_h port, const char *name);
+
+/* Set the type of a port */
+int mmi_port_set_type(mmi_port_h port, mmi_port_type_e type);
+
+/* Set the data type of a port */
+int mmi_port_set_data_type(mmi_port_h port, mmi_data_type_e data_type);
+
+/* Set the callbacks of a port */
+int mmi_port_set_callbacks(mmi_port_h port, mmi_port_callbacks callbacks);
+
+/* Clones a port */
+int mmi_port_clone(mmi_port_h port, mmi_port_h *cloned);
+
+/* Destroy a port */
+int mmi_port_destroy(mmi_port_h port);
+
+/* Provide a link information between two static ports. */
+// int mmi_port_link(mmi_port_h out, mmi_port_h in);
+
+/* Registers a port with the given type, data type, and callbacks. */
+// int mmi_port_register(mmi_port_type_e port_type, mmi_data_type_e data_type, mmi_port_callbacks *callbacks, mmi_port_h port);
+
+/* Generates data to the port with the given port instance. */
+int mmi_port_instance_generate_output(mmi_port_instance_h instance, mmi_data_h data);
+
+/* Requests the port instance to transform the data to the given format. */
+int mmi_port_instance_request_auto_transform(mmi_port_instance_h instance, const char *from_format, const char *to_format);
+
+/* Requests the port instance to retrieve the data with the given format. */
+int mmi_port_instance_request_input_data_format(mmi_port_instance_h instance, const char *format);
+
+/* Announce the data format to the port instance. */
+int mmi_port_instance_announce_output_data_format(mmi_port_instance_h instance, const char *format);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_PORT_H__ */
diff --git a/capi/mmi-primitive-value.h b/capi/mmi-primitive-value.h
new file mode 100644 (file)
index 0000000..b8b25e8
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__
+#define __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__
+
+
+#include <tizen.h>
+#include <stdlib.h>
+
+
+/**
+* @file mmi-primitive-value.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+    MMI_PRIMITIVE_VALUE_TYPE_INT,
+    MMI_PRIMITIVE_VALUE_TYPE_FLOAT,
+    MMI_PRIMITIVE_VALUE_TYPE_STRING,
+    MMI_PRIMITIVE_VALUE_TYPE_BOOL,
+    MMI_PRIMITIVE_VALUE_TYPE_ARRAY,
+} mmi_primitive_value_type_e;
+
+
+typedef struct mmi_primitive_value_s* mmi_primitive_value_h;
+
+
+int mmi_primitive_value_create_int(int data, mmi_primitive_value_h *handle);
+
+int mmi_primitive_value_create_float(float data, mmi_primitive_value_h *handle);
+
+int mmi_primitive_value_create_string(const char *data, mmi_primitive_value_h *handle);
+
+int mmi_primitive_value_create_bool(bool data, mmi_primitive_value_h *handle);
+
+int mmi_primitive_value_create_array(mmi_primitive_value_h *handle);
+
+int mmi_primitive_value_add_array_element(mmi_primitive_value_h array, mmi_primitive_value_h element);
+
+int mmi_primitive_value_get_type(mmi_primitive_value_h handle, mmi_primitive_value_type_e *type);
+
+int mmi_primitive_value_get_int(mmi_primitive_value_h handle, int *value);
+
+int mmi_primitive_value_get_float(mmi_primitive_value_h handle, float *value);
+
+int mmi_primitive_value_get_string(mmi_primitive_value_h handle, const char **string);
+
+int mmi_primitive_value_get_bool(mmi_primitive_value_h handle, bool *value);
+
+int mmi_primitive_value_get_array_count(mmi_primitive_value_h array, size_t *count);
+
+int mmi_primitive_value_get_array_element(mmi_primitive_value_h array, size_t index, mmi_primitive_value_h *element);
+
+int mmi_primitive_value_clone(mmi_primitive_value_h handle, mmi_primitive_value_h *cloned);
+
+int mmi_primitive_value_destroy(mmi_primitive_value_h handle);
+
+int mmi_primitive_value_to_bytes(mmi_primitive_value_h handle, unsigned char **bytes, size_t *size);
+
+int mmi_primitive_value_from_bytes(unsigned char *bytes, size_t size, mmi_primitive_value_h *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__ */
diff --git a/capi/mmi-signal.h b/capi/mmi-signal.h
new file mode 100644 (file)
index 0000000..3d989ec
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_SIGNAL_H__
+#define __TIZEN_UIX_MMI_SIGNAL_H__
+
+
+#include <tizen.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <mmi-defines.h>
+#include <mmi-primitive-value.h>
+
+/**
+* @file mmi-port.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+    char name[MMI_NAME_MAX_LENGTH];
+    mmi_primitive_value_h value;
+} mmi_signal_parameter_s;
+
+typedef mmi_signal_parameter_s* mmi_signal_parameter_h;
+
+typedef struct {
+    char name[MMI_NAME_MAX_LENGTH];
+    mmi_signal_parameter_h parameters[MMI_PARAMETER_MAX_COUNT];
+    int parameter_count;
+} mmi_signal_s;
+
+typedef mmi_signal_s* mmi_signal_h;
+
+int mmi_signal_parameter_create(mmi_primitive_value_h value, const char *name,  mmi_signal_parameter_h *parameter);
+
+int mmi_signal_parameter_get_name(mmi_signal_parameter_h parameter, char **name);
+
+int mmi_signal_parameter_get_value(mmi_signal_parameter_h parameter, mmi_primitive_value_h *value);
+
+int mmi_signal_parameter_clone(mmi_signal_parameter_h parameter, mmi_signal_parameter_h *cloned);
+
+int mmi_signal_parameter_destroy(mmi_signal_parameter_h parameter);
+
+int mmi_signal_create(const char *name, mmi_signal_h *handle);
+
+int mmi_signal_add_parameter(mmi_signal_h handle, mmi_signal_parameter_h parameter);
+
+int mmi_signal_get_name(mmi_signal_h handle, char **name);
+
+int mmi_signal_get_parameter_count(mmi_signal_h handle, int *count);
+
+int mmi_signal_get_parameter(mmi_signal_h handle, int index, mmi_signal_parameter_h *parameter);
+
+int mmi_signal_destroy(mmi_signal_h handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_SIGNAL_H__ */
diff --git a/capi/mmi-workflow.h b/capi/mmi-workflow.h
new file mode 100644 (file)
index 0000000..148dd3e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __TIZEN_UIX_MMI_WORKFLOW_H__
+#define __TIZEN_UIX_MMI_WORKFLOW_H__
+
+
+#include <tizen.h>
+
+#include <mmi-attribute.h>
+#include <mmi-data.h>
+#include <mmi-node.h>
+
+
+/**
+* @file mmi-workflow.h
+*/
+
+
+/**
+* @addtogroup CAPI_UIX_MMI_MODULE
+* @{
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    MMI_STANDARD_WORKFLOW_NONE,
+    MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND,
+    MMI_STANDARD_WORKFLOW_VOICE_TOUCH,
+    MMI_STANDARD_WORKFLOW_USER_RECOGNITION,
+} mmi_standard_workflow_type_e;
+
+
+/* Functions to define a workflow prototype. Mostly used by workflow developers. */
+
+typedef struct {
+    char name[MMI_NAME_MAX_LENGTH];
+    mmi_node_h node;
+} mmi_workflow_node_info_s;
+
+typedef struct {
+    char from_node_name[MMI_NAME_MAX_LENGTH];
+    char from_port_name[MMI_NAME_MAX_LENGTH];
+    char to_node_name[MMI_NAME_MAX_LENGTH];
+    char to_port_name[MMI_NAME_MAX_LENGTH];
+} mmi_workflow_link_info_s;
+
+typedef struct {
+    char attribute_name[MMI_NAME_MAX_LENGTH];
+    char target_node_name[MMI_NAME_MAX_LENGTH];
+    char target_attribute_name[MMI_NAME_MAX_LENGTH];
+} mmi_workflow_attribute_assignment_info_s;
+
+typedef struct {
+    size_t serialized_default_value_size;
+    unsigned char *serialized_default_value;
+} mmi_workflow_attribute_default_value_info_s;
+
+typedef struct {
+    char output_name[MMI_NAME_MAX_LENGTH];
+    char from_node_name[MMI_NAME_MAX_LENGTH];
+    char from_port_name[MMI_NAME_MAX_LENGTH];
+} mmi_workflow_output_assignment_info_s;
+
+typedef struct {
+    mmi_standard_workflow_type_e type;
+    mmi_workflow_node_info_s *node_infos;
+    size_t node_info_count;
+    mmi_workflow_link_info_s *link_infos;
+    size_t link_info_count;
+    mmi_workflow_attribute_assignment_info_s *attribute_assignment_infos;
+    size_t attribute_assignment_info_count;
+    mmi_workflow_attribute_default_value_info_s *attribute_default_value_infos;
+    size_t attribute_default_value_info_count;
+    mmi_workflow_output_assignment_info_s *output_assignment_infos;
+    size_t output_assignment_info_count;
+} mmi_workflow_s;
+
+typedef mmi_workflow_s* mmi_workflow_h;
+
+/* Create a workflow prototype */
+int mmi_workflow_create(mmi_workflow_h *workflow);
+
+/* Set the type of a workflow prototype */
+int mmi_workflow_set_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e type);
+
+/* Get the type of a workflow prototype */
+int mmi_workflow_get_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e *type);
+
+/* Add a node to a workflow prototype */
+int mmi_workflow_node_add(mmi_workflow_h workflow, const char *node_name, mmi_node_h node);
+
+/* A convinience function to link two nodes by automatically finding the appropriate ports in the nodes. */
+/* The linking succeeds only if there is exactly one pair of compatible input and output ports in the nodes. */
+/* int mmi_workflow_link_nodes(mmi_node_h from_node, mmi_node_h to_node); */
+
+/* Link two nodes with specifying the port names. */
+/* FIXME: It would be better to use handles instead of names. */
+int mmi_workflow_link_nodes_by_names(mmi_workflow_h workflow, const char *from_node_name, const char *from_port_name, const char *to_node_name, const char *to_port_name);
+
+/* Assign an attribute of a workflow to an attribute of a specific node in a workflow prototype */
+/* FIXME: It would be better to use handles instead of names. */
+int mmi_workflow_attribute_assign(mmi_workflow_h workflow, const char *attribute_name, const char *target_node_name, const char *target_attribute_name);
+
+/* Set the default value of an attribute of a workflow */
+int mmi_workflow_attribute_set_default_value(mmi_workflow_h workflow, mmi_attribute_h default_value);
+
+/* Assign a signal of a workflow to a signal of a specific node in a workflow prototype */
+int mmi_workflow_signal_assign(mmi_workflow_h workflow, const char *workflow_signal, mmi_node_h node, const char *node_signal);
+
+/* Assign an output of a workflow to a OUT port of a specific node in a workflow prototype */
+/* FIXME: It would be better to use handles instead of names. */
+int mmi_workflow_output_assign(mmi_workflow_h workflow, const char *workflow_output, const char *out_node_name, const char *node_out_port_name);
+
+/* Assign an output of a workflow to a OUT port of a specific node in a workflow prototype, with a port handle */
+int mmi_workflow_output_assign_by_port(mmi_workflow_h workflow, const char *workflow_output, mmi_port_h port);
+
+/* Register a standard workflow prototype to the workflow manager */
+int mmi_standard_workflow_register(mmi_workflow_h workflow);
+
+/* Clones a workflow. */
+int mmi_workflow_clone(mmi_workflow_h workflow, mmi_workflow_h *cloned);
+
+/* Create a workflow prototype from a script file */
+int mmi_workflow_create_from_script(const char *script_path, mmi_workflow_h *workflow);
+
+/* Destroy a workflow prototype */
+int mmi_workflow_destroy(mmi_workflow_h workflow);
+
+/* Functions to create a workflow instance. Mostly used by application developers. */
+
+typedef void* mmi_workflow_instance_h;
+
+/* Callback function for workflow output */
+typedef void (*mmi_workflow_output_cb)(mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data);
+
+/* Callback function for workflow signal */
+// typedef void (*mmi_workflow_signal_cb)(const char *name, mmi_signal_parameter_h parameter, void *user_data);
+
+/* Instantiate a workflow from a workflow prototype */
+int mmi_standard_workflow_instance_create(mmi_standard_workflow_type_e type, mmi_workflow_instance_h *instance);
+
+/* Destroy a workflow instance */
+int mmi_workflow_instance_destroy(mmi_workflow_instance_h instance);
+
+/* Start a workflow instance */
+int mmi_workflow_instance_activate(mmi_workflow_instance_h instance);
+
+/* Stop a workflow instance */
+int mmi_workflow_instance_deactivate(mmi_workflow_instance_h instance);
+
+/* Set an attribute of a workflow instance */
+int mmi_workflow_instance_set_attribute(mmi_workflow_instance_h instance, mmi_attribute_h attribute);
+
+/* Emit a signal to a workflow instance */
+// int mmi_workflow_instance_signal_emit(mmi_workflow_instance_h instance, mmi_signal_h signal);
+
+/* Set a callback function to receive workflow output */
+int mmi_workflow_instance_set_output_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_output_cb callback, void *user_data);
+
+/* Set a callback function to receive workflow signal */
+// int mmi_workflow_instance_set_signal_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_signal_cb callback, void *user_data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __TIZEN_UIX_MMI_WORKFLOW_H__ */
diff --git a/capi/mmi.h b/capi/mmi.h
new file mode 100644 (file)
index 0000000..e3df8d8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_H__
+#define __MMI_H__
+
+#include <tizen.h>
+#define MMI_API __attribute__ ((visibility("default")))
+
+#include <mmi-defines.h>
+#include <mmi-error.h>
+#include <mmi-data.h>
+#include <mmi-primitive-value.h>
+#include <mmi-attribute.h>
+#include <mmi-workflow.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+    MMI_STATE_NONE = 0,
+    MMI_STATE_READY,
+} mmi_state_e;
+
+typedef int (*mmi_state_changed_cb)(mmi_state_e state, void *user_data);
+
+/** @brief Initialize MMI framework
+ *
+ * @return 0 on success, otherwise a negative error value
+ */
+int mmi_initialize();
+
+/** @brief Deinitialize MMI framework
+ *
+ * @return 0 on success, otherwise a negative error value
+ */
+int mmi_deinitialize();
+
+/** @brief Set callback for state change
+ *
+ * @param[in] callback The callback function to be called when state is changed
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @see mmi_state_changed_cb
+ */
+int mmi_set_state_changed_cb(mmi_state_changed_cb callback, void *user_data);
+
+/** @brief Unset callback for state change
+ *
+ * @param[in] callback The callback function to be unset
+ *
+ * @return 0 on success, otherwise a negative error value
+ */
+int mmi_unset_state_changed_cb(mmi_state_changed_cb callback);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif //__MMI_H__
diff --git a/external/adishavit/LICENSE.txt b/external/adishavit/LICENSE.txt
new file mode 100644 (file)
index 0000000..7cf4796
--- /dev/null
@@ -0,0 +1,26 @@
+Copyright (c) 2016, Adi Shavit 
+All rights reserved. 
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met: 
+
+ * Redistributions of source code must retain the above copyright notice, 
+   this list of conditions and the following disclaimer. 
+ * Redistributions in binary form must reproduce the above copyright 
+   notice, this list of conditions and the following disclaimer in the 
+   documentation and/or other materials provided with the distribution. 
+ * Neither the name of  nor the names of its contributors may be used to 
+   endorse or promote products derived from this software without specific 
+   prior written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE. 
diff --git a/external/adishavit/argh.h b/external/adishavit/argh.h
new file mode 100644 (file)
index 0000000..fb38dea
--- /dev/null
@@ -0,0 +1,434 @@
+#pragma once\r
+\r
+#include <algorithm>\r
+#include <sstream>\r
+#include <limits>\r
+#include <string>\r
+#include <vector>\r
+#include <set>\r
+#include <map>\r
+#include <cassert>\r
+\r
+namespace argh\r
+{\r
+   // Terminology:\r
+   // A command line is composed of 2 types of args:\r
+   // 1. Positional args, i.e. free standing values\r
+   // 2. Options: args beginning with '-'. We identify two kinds:\r
+   //    2.1: Flags: boolean options =>  (exist ? true : false)\r
+   //    2.2: Parameters: a name followed by a non-option value\r
+\r
+#if !defined(__GNUC__) || (__GNUC__ >= 5)\r
+   using string_stream = std::istringstream;\r
+#else\r
+    // Until GCC 5, istringstream did not have a move constructor.\r
+    // stringstream_proxy is used instead, as a workaround.\r
+   class stringstream_proxy\r
+   {\r
+   public:\r
+      stringstream_proxy() = default;\r
+\r
+      // Construct with a value.\r
+      stringstream_proxy(std::string const& value) :\r
+         stream_(value)\r
+      {}\r
+\r
+      // Copy constructor.\r
+      stringstream_proxy(const stringstream_proxy& other) :\r
+         stream_(other.stream_.str())\r
+      {\r
+         stream_.setstate(other.stream_.rdstate());\r
+      }\r
+\r
+      void setstate(std::ios_base::iostate state) { stream_.setstate(state); }\r
+\r
+      // Stream out the value of the parameter.\r
+      // If the conversion was not possible, the stream will enter the fail state,\r
+      // and operator bool will return false.\r
+      template<typename T>\r
+      stringstream_proxy& operator >> (T& thing)\r
+      {\r
+         stream_ >> thing;\r
+         return *this;\r
+      }\r
+\r
+\r
+      // Get the string value.\r
+      std::string str() const { return stream_.str(); }\r
+\r
+      std::stringbuf* rdbuf() const { return stream_.rdbuf(); }\r
+\r
+      // Check the state of the stream.\r
+      // False when the most recent stream operation failed\r
+      explicit operator bool() const { return !!stream_; }\r
+\r
+      ~stringstream_proxy() = default;\r
+   private:\r
+      std::istringstream stream_;\r
+   };\r
+   using string_stream = stringstream_proxy;\r
+#endif\r
+\r
+   class parser\r
+   {\r
+   public:\r
+      enum Mode { PREFER_FLAG_FOR_UNREG_OPTION = 1 << 0,\r
+                  PREFER_PARAM_FOR_UNREG_OPTION = 1 << 1,\r
+                  NO_SPLIT_ON_EQUALSIGN = 1 << 2,\r
+                  SINGLE_DASH_IS_MULTIFLAG = 1 << 3,\r
+                };\r
+\r
+      parser() = default;\r
+\r
+      parser(std::initializer_list<char const* const> pre_reg_names)\r
+      {  add_params(pre_reg_names); }\r
+\r
+      parser(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION)\r
+      {  parse(argv, mode); }\r
+\r
+      parser(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION)\r
+      {  parse(argc, argv, mode); }\r
+\r
+      void add_param(std::string const& name);\r
+      void add_params(std::initializer_list<char const* const> init_list);\r
+\r
+      void parse(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION);\r
+      void parse(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION);\r
+\r
+      std::multiset<std::string>          const& flags()    const { return flags_;    }\r
+      std::map<std::string, std::string>  const& params()   const { return params_;   }\r
+      std::vector<std::string>            const& pos_args() const { return pos_args_; }\r
+\r
+      // begin() and end() for using range-for over positional args.\r
+      std::vector<std::string>::const_iterator begin() const { return pos_args_.cbegin(); }\r
+      std::vector<std::string>::const_iterator end()   const { return pos_args_.cend();   }\r
+      size_t size()                                    const { return pos_args_.size();   }\r
+\r
+      //////////////////////////////////////////////////////////////////////////\r
+      // Accessors\r
+\r
+      // flag (boolean) accessors: return true if the flag appeared, otherwise false.\r
+      bool operator[](std::string const& name) const;\r
+\r
+      // multiple flag (boolean) accessors: return true if at least one of the flag appeared, otherwise false.\r
+      bool operator[](std::initializer_list<char const* const> init_list) const;\r
+\r
+      // returns positional arg string by order. Like argv[] but without the options\r
+      std::string const& operator[](size_t ind) const;\r
+\r
+      // returns a std::istream that can be used to convert a positional arg to a typed value.\r
+      string_stream operator()(size_t ind) const;\r
+\r
+      // same as above, but with a default value in case the arg is missing (index out of range).\r
+      template<typename T>\r
+      string_stream operator()(size_t ind, T&& def_val) const;\r
+\r
+      // parameter accessors, give a name get an std::istream that can be used to convert to a typed value.\r
+      // call .str() on result to get as string\r
+      string_stream operator()(std::string const& name) const;\r
+\r
+      // accessor for a parameter with multiple names, give a list of names, get an std::istream that can be used to convert to a typed value.\r
+      // call .str() on result to get as string\r
+      // returns the first value in the list to be found.\r
+      string_stream operator()(std::initializer_list<char const* const> init_list) const;\r
+\r
+      // same as above, but with a default value in case the param was missing.\r
+      // Non-string def_val types must have an operator<<() (output stream operator)\r
+      // If T only has an input stream operator, pass the string version of the type as in "3" instead of 3.\r
+      template<typename T>\r
+      string_stream operator()(std::string const& name, T&& def_val) const;\r
+\r
+      // same as above but for a list of names. returns the first value to be found.\r
+      template<typename T>\r
+      string_stream operator()(std::initializer_list<char const* const> init_list, T&& def_val) const;\r
+\r
+   private:\r
+      string_stream bad_stream() const;\r
+      std::string trim_leading_dashes(std::string const& name) const;\r
+      bool is_number(std::string const& arg) const;\r
+      bool is_option(std::string const& arg) const;\r
+      bool got_flag(std::string const& name) const;\r
+      bool is_param(std::string const& name) const;\r
+\r
+   private:\r
+      std::vector<std::string> args_;\r
+      std::map<std::string, std::string> params_;\r
+      std::vector<std::string> pos_args_;\r
+      std::multiset<std::string> flags_;\r
+      std::set<std::string> registeredParams_;\r
+      std::string empty_;\r
+   };\r
+\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline void parser::parse(const char * const argv[], int mode)\r
+   {\r
+      int argc = 0;\r
+      for (auto argvp = argv; *argvp; ++argc, ++argvp);\r
+      parse(argc, argv, mode);\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline void parser::parse(int argc, const char* const argv[], int mode /*= PREFER_FLAG_FOR_UNREG_OPTION*/)\r
+   {\r
+      // convert to strings\r
+      args_.resize(static_cast<decltype(args_)::size_type>(argc));
+      std::transform(argv, argv + argc, args_.begin(), [](const char* const arg) { return arg;  });\r
+\r
+      // parse line\r
+      for (auto i = 0u; i < args_.size(); ++i)\r
+      {\r
+         if (!is_option(args_[i]))\r
+         {\r
+            pos_args_.emplace_back(args_[i]);\r
+            continue;\r
+         }\r
+\r
+         auto name = trim_leading_dashes(args_[i]);\r
+\r
+         if (!(mode & NO_SPLIT_ON_EQUALSIGN))\r
+         {\r
+            auto equalPos = name.find('=');\r
+            if (equalPos != std::string::npos)\r
+            {\r
+               params_.insert({ name.substr(0, equalPos), name.substr(equalPos + 1) });\r
+               continue;\r
+            }\r
+         }\r
+\r
+         // if the option is unregistered and should be a multi-flag\r
+         if (1 == (args_[i].size() - name.size()) &&         // single dash\r
+            argh::parser::SINGLE_DASH_IS_MULTIFLAG & mode && // multi-flag mode\r
+            !is_param(name))                                  // unregistered\r
+         {\r
+            std::string keep_param;\r
+\r
+            if (!name.empty() && is_param(std::string(1ul, name.back()))) // last char is param\r
+            {\r
+               keep_param += name.back();\r
+               name.resize(name.size() - 1);\r
+            }\r
+\r
+            for (auto const& c : name)\r
+            {\r
+               flags_.emplace(std::string{ c });\r
+            }\r
+\r
+            if (!keep_param.empty())\r
+            {\r
+               name = keep_param;\r
+            }\r
+            else\r
+            {\r
+               continue; // do not consider other options for this arg\r
+            }\r
+         }\r
+\r
+         // any potential option will get as its value the next arg, unless that arg is an option too\r
+         // in that case it will be determined a flag.\r
+         if (i == args_.size() - 1 || is_option(args_[i + 1]))\r
+         {\r
+            flags_.emplace(name);\r
+            continue;\r
+         }\r
+\r
+         // if 'name' is a pre-registered option, then the next arg cannot be a free parameter to it is skipped\r
+         // otherwise we have 2 modes:\r
+         // PREFER_FLAG_FOR_UNREG_OPTION: a non-registered 'name' is determined a flag.\r
+         //                               The following value (the next arg) will be a free parameter.\r
+         //\r
+         // PREFER_PARAM_FOR_UNREG_OPTION: a non-registered 'name' is determined a parameter, the next arg\r
+         //                                will be the value of that option.\r
+\r
+         assert(!(mode & argh::parser::PREFER_FLAG_FOR_UNREG_OPTION)\r
+             || !(mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION));\r
+\r
+         bool preferParam = mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION;\r
+\r
+         if (is_param(name) || preferParam)\r
+         {\r
+            params_.insert({ name, args_[i + 1] });\r
+            ++i; // skip next value, it is not a free parameter\r
+            continue;\r
+         }\r
+         else\r
+         {\r
+            flags_.emplace(name);\r
+         }\r
+      };\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline string_stream parser::bad_stream() const\r
+   {\r
+      string_stream bad;\r
+      bad.setstate(std::ios_base::failbit);\r
+      return bad;\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool parser::is_number(std::string const& arg) const\r
+   {\r
+      // inefficient but simple way to determine if a string is a number (which can start with a '-')\r
+      std::istringstream istr(arg);\r
+      double number;\r
+      istr >> number;\r
+      return !(istr.fail() || istr.bad());\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool parser::is_option(std::string const& arg) const\r
+   {\r
+      assert(0 != arg.size());\r
+      if (is_number(arg))\r
+         return false;\r
+      return '-' == arg[0];\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline std::string parser::trim_leading_dashes(std::string const& name) const\r
+   {\r
+      auto pos = name.find_first_not_of('-');\r
+      return std::string::npos != pos ? name.substr(pos) : name;\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool argh::parser::got_flag(std::string const& name) const\r
+   {\r
+      return flags_.end() != flags_.find(trim_leading_dashes(name));\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool argh::parser::is_param(std::string const& name) const\r
+   {\r
+      return registeredParams_.count(name);\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool parser::operator[](std::string const& name) const\r
+   {\r
+      return got_flag(name);\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline bool parser::operator[](std::initializer_list<char const* const> init_list) const\r
+   {\r
+      return std::any_of(init_list.begin(), init_list.end(), [&](char const* const name) { return got_flag(name); });\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline std::string const& parser::operator[](size_t ind) const\r
+   {\r
+      if (ind < pos_args_.size())\r
+         return pos_args_[ind];\r
+      return empty_;\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline string_stream parser::operator()(std::string const& name) const\r
+   {\r
+      auto optIt = params_.find(trim_leading_dashes(name));\r
+      if (params_.end() != optIt)\r
+         return string_stream(optIt->second);\r
+      return bad_stream();\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline string_stream parser::operator()(std::initializer_list<char const* const> init_list) const\r
+   {\r
+      for (auto& name : init_list)\r
+      {\r
+         auto optIt = params_.find(trim_leading_dashes(name));\r
+         if (params_.end() != optIt)\r
+            return string_stream(optIt->second);\r
+      }\r
+      return bad_stream();\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   template<typename T>\r
+   string_stream parser::operator()(std::string const& name, T&& def_val) const\r
+   {\r
+      auto optIt = params_.find(trim_leading_dashes(name));\r
+      if (params_.end() != optIt)\r
+         return string_stream(optIt->second);\r
+\r
+      std::ostringstream ostr;\r
+      ostr.precision(std::numeric_limits<long double>::max_digits10);\r
+      ostr << def_val;\r
+      return string_stream(ostr.str()); // use default\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   // same as above but for a list of names. returns the first value to be found.\r
+   template<typename T>\r
+   string_stream parser::operator()(std::initializer_list<char const* const> init_list, T&& def_val) const\r
+   {\r
+      for (auto& name : init_list)\r
+      {\r
+         auto optIt = params_.find(trim_leading_dashes(name));\r
+         if (params_.end() != optIt)\r
+            return string_stream(optIt->second);\r
+      }\r
+      std::ostringstream ostr;\r
+      ostr.precision(std::numeric_limits<long double>::max_digits10);\r
+      ostr << def_val;\r
+      return string_stream(ostr.str()); // use default\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline string_stream parser::operator()(size_t ind) const\r
+   {\r
+      if (pos_args_.size() <= ind)\r
+         return bad_stream();\r
+\r
+      return string_stream(pos_args_[ind]);\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   template<typename T>\r
+   string_stream parser::operator()(size_t ind, T&& def_val) const\r
+   {\r
+      if (pos_args_.size() <= ind)\r
+      {\r
+         std::ostringstream ostr;\r
+         ostr.precision(std::numeric_limits<long double>::max_digits10);\r
+         ostr << def_val;\r
+         return string_stream(ostr.str());\r
+      }\r
+\r
+      return string_stream(pos_args_[ind]);\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline void parser::add_param(std::string const& name)\r
+   {\r
+      registeredParams_.insert(trim_leading_dashes(name));\r
+   }\r
+\r
+   //////////////////////////////////////////////////////////////////////////\r
+\r
+   inline void parser::add_params(std::initializer_list<char const* const> init_list)\r
+   {\r
+      for (auto& name : init_list)\r
+         registeredParams_.insert(trim_leading_dashes(name));\r
+   }\r
+}\r
diff --git a/external/cli/LICENSE b/external/cli/LICENSE
new file mode 100644 (file)
index 0000000..127a5bc
--- /dev/null
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/external/cli/boostasiocliasyncsession.h b/external/cli/boostasiocliasyncsession.h
new file mode 100644 (file)
index 0000000..e2dc607
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_BOOSTASIOCLIASYNCSESSION_H_
+#define CLI_BOOSTASIOCLIASYNCSESSION_H_
+
+#include "detail/genericcliasyncsession.h"
+#include "detail/boostasiolib.h"
+
+
+namespace cli { using BoostAsioCliAsyncSession = detail::GenericCliAsyncSession<detail::BoostAsioLib>; }
+
+#endif // CLI_BOOSTASIOCLIASYNCSESSION_H_
+
diff --git a/external/cli/boostasioremotecli.h b/external/cli/boostasioremotecli.h
new file mode 100644 (file)
index 0000000..4f25749
--- /dev/null
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_BOOSTASIOREMOTECLI_H_
+#define CLI_BOOSTASIOREMOTECLI_H_
+
+#include "detail/boostasiolib.h"
+#include "detail/genericasioremotecli.h"
+
+namespace cli { using BoostAsioCliTelnetServer = detail::CliGenericTelnetServer<detail::BoostAsioLib>; }
+
+#endif // CLI_BOOSTASIOREMOTECLI_H_
+
diff --git a/external/cli/boostasioscheduler.h b/external/cli/boostasioscheduler.h
new file mode 100644 (file)
index 0000000..f4a872a
--- /dev/null
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_BOOSTASIOSCHEDULER_H_
+#define CLI_BOOSTASIOSCHEDULER_H_
+
+#include "detail/genericasioscheduler.h"
+#include "detail/boostasiolib.h"
+
+namespace cli { using BoostAsioScheduler = detail::GenericAsioScheduler<detail::BoostAsioLib>; }
+
+#endif // CLI_BOOSTASIOSCHEDULER_H_
diff --git a/external/cli/cli.h b/external/cli/cli.h
new file mode 100644 (file)
index 0000000..8d61911
--- /dev/null
@@ -0,0 +1,877 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_CLI_H
+#define CLI_CLI_H
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <functional>
+#include <algorithm>
+#include <cctype> // std::isspace
+#include <type_traits>
+#include "colorprofile.h"
+#include "detail/history.h"
+#include "detail/split.h"
+#include "detail/fromstring.h"
+#include "historystorage.h"
+#include "volatilehistorystorage.h"
+#include <iostream>
+#include <utility>
+
+namespace cli
+{
+
+    // ********************************************************************
+
+    template < typename T > struct TypeDesc { static const char* Name() { return ""; } };
+    template <> struct TypeDesc< char > { static const char* Name() { return "<char>"; } };
+    template <> struct TypeDesc< unsigned char > { static const char* Name() { return "<unsigned char>"; } };
+    template <> struct TypeDesc< signed char > { static const char* Name() { return "<signed char>"; } };
+    template <> struct TypeDesc< short > { static const char* Name() { return "<short>"; } };
+    template <> struct TypeDesc< unsigned short > { static const char* Name() { return "<unsigned short>"; } };
+    template <> struct TypeDesc< int > { static const char* Name() { return "<int>"; } };
+    template <> struct TypeDesc< unsigned int > { static const char* Name() { return "<unsigned int>"; } };
+    template <> struct TypeDesc< long > { static const char* Name() { return "<long>"; } };
+    template <> struct TypeDesc< unsigned long > { static const char* Name() { return "<unsigned long>"; } };
+    template <> struct TypeDesc< long long > { static const char* Name() { return "<long long>"; } };
+    template <> struct TypeDesc< unsigned long long > { static const char* Name() { return "<unsigned long long>"; } };
+    template <> struct TypeDesc< float > { static const char* Name() { return "<float>"; } };
+    template <> struct TypeDesc< double > { static const char* Name() { return "<double>"; } };
+    template <> struct TypeDesc< long double > { static const char* Name() { return "<long double>"; } };
+    template <> struct TypeDesc< bool > { static const char* Name() { return "<bool>"; } };
+    template <> struct TypeDesc< std::string > { static const char* Name() { return "<string>"; } };
+    template <> struct TypeDesc< std::vector<std::string> > { static const char* Name() { return "<list of strings>"; } };
+
+    // ********************************************************************
+
+    // this class provides a global output stream
+    class OutStream : public std::basic_ostream<char>, public std::streambuf
+    {
+    public:
+        OutStream() : std::basic_ostream<char>(this)
+        {
+        }
+
+        // std::streambuf overrides
+        std::streamsize xsputn(const char* s, std::streamsize n) override
+        {
+            for (auto os: ostreams)
+                os->rdbuf()->sputn(s, n);
+            return n;
+        }
+        int overflow(int c) override
+        {
+            for (auto os: ostreams)
+                *os << static_cast<char>(c);
+            return c;
+        }            
+
+        void Register(std::ostream& o)
+        {
+            ostreams.push_back(&o);
+        }
+        void UnRegister(std::ostream& o)
+        {
+            ostreams.erase(std::remove(ostreams.begin(), ostreams.end(), &o), ostreams.end());
+        }
+
+    private:
+
+        std::vector<std::ostream*> ostreams;
+    };
+    
+    // forward declarations
+    class Menu;
+    class CliSession;
+
+    class Cli
+    {
+
+    public:
+        ~Cli() = default;
+        // disable value semantics
+        Cli(const Cli&) = delete;
+        Cli& operator = (const Cli&) = delete;
+        // enable move semantics
+        Cli(Cli&&) = default;
+        Cli& operator = (Cli&&) = default;
+
+        /**
+         * @brief Construct a new Cli object having a given root menu that contains the first level commands available.
+         * 
+         * @param _rootMenu is the @c Menu containing the first level commands available to the user.
+         * @param historyStorage is the policy for the storage of the cli commands history. You must pass an instance of
+         * a class derived from @c HistoryStorage. The library provides these policies:
+         *   - @c VolatileHistoryStorage
+         *   - @c FileHistoryStorage it's a persistent history. I.e., the command history is preserved after your application
+         *     is restarted.
+         * 
+         * However, you can develop your own, just derive a class from @c HistoryStorage .
+         */
+        Cli(std::unique_ptr<Menu> _rootMenu, std::unique_ptr<HistoryStorage> historyStorage = std::make_unique<VolatileHistoryStorage>()) :
+            globalHistoryStorage(std::move(historyStorage)),
+            rootMenu(std::move(_rootMenu)),
+            enterAction{},
+            exitAction{}
+        {
+        }
+
+        /**
+         * @brief Add a global enter action that is called every time a session (local or remote) is established.
+         * 
+         * @param action the function to be called when a session exits, taking a @c std::ostream& parameter to write on that session console.
+         */
+        void EnterAction(const std::function< void(std::ostream&)>& action) { enterAction = action; }
+
+        /**
+         * @brief Add a global exit action that is called every time a session (local or remote) gets the "exit" command.
+         * 
+         * @param action the function to be called when a session exits, taking a @c std::ostream& parameter to write on that session console.
+         */
+        void ExitAction(const std::function< void(std::ostream&)>& action) { exitAction = action; }
+
+        /**
+         * @brief Add an handler that will be called when a @c std::exception (or derived) is thrown inside a command handler.
+         * If an exception handler is not set, the exception will be logget on the session output stream.
+         * 
+         * @param handler the function to be called when an exception is thrown, taking a @c std::ostream& parameter to write on that session console
+         * and the exception thrown.
+         */
+        void StdExceptionHandler(const std::function< void(std::ostream&, const std::string& cmd, const std::exception&) >& handler)
+        {
+            exceptionHandler = handler;
+        }
+
+        /**
+         * @brief Get a global out stream object that can be used to print on every session currently connected (local and remote)
+         * 
+         * @return OutStream& the reference to the global out stream writing on every session console. 
+         */
+        static OutStream& cout()
+        {
+            return *CoutPtr();
+        }
+
+    private:
+        friend class CliSession;
+
+        static std::shared_ptr<OutStream> CoutPtr()
+        {
+            static std::shared_ptr<OutStream> s = std::make_shared<OutStream>();
+            return s;
+        }
+
+        Menu* RootMenu() { return rootMenu.get(); }
+
+        void EnterAction(std::ostream& out)
+        {
+            if (enterAction)
+                enterAction(out);
+        }
+
+        void ExitAction(std::ostream& out)
+        {
+            if (exitAction)
+                exitAction(out);
+        }
+
+        void StdExceptionHandler(std::ostream& out, const std::string& cmd, const std::exception& e)
+        {
+            if (exceptionHandler)
+                exceptionHandler(out, cmd, e);
+            else
+                out << e.what() << '\n';
+        }
+
+        void StoreCommands(const std::vector<std::string>& cmds)
+        {
+            globalHistoryStorage->Store(cmds);
+        }
+
+        std::vector<std::string> GetCommands() const
+        {
+            return globalHistoryStorage->Commands();
+        }
+
+    private:
+        std::unique_ptr<HistoryStorage> globalHistoryStorage;
+        std::unique_ptr<Menu> rootMenu; // just to keep it alive
+        std::function<void(std::ostream&)> enterAction;
+        std::function<void(std::ostream&)> exitAction;
+        std::function<void(std::ostream&, const std::string& cmd, const std::exception& )> exceptionHandler;
+    };
+
+    // ********************************************************************
+
+    class Command
+    {
+    public:
+        explicit Command(std::string _name) : name(std::move(_name)), enabled(true) {}
+        virtual ~Command() noexcept = default;
+
+        // disable copy and move semantics
+        Command(const Command&) = delete;
+        Command(Command&&) = delete;
+        Command& operator=(const Command&) = delete;
+        Command& operator=(Command&&) = delete;
+
+        virtual void Enable() { enabled = true; }
+        virtual void Disable() { enabled = false; }
+        virtual bool Exec(const std::vector<std::string>& cmdLine, CliSession& session) = 0;
+        virtual void Help(std::ostream& out) const = 0;
+        // Returns the collection of completions relatives to this command.
+        // For simple commands, provides a base implementation that use the name of the command
+        // for aggregate commands (i.e., Menu), the function is redefined to give the menu command
+        // and the subcommand recursively
+        virtual std::vector<std::string> GetCompletionRecursive(const std::string& line) const
+        {
+            if (!enabled) return {};
+            if (name.rfind(line, 0) == 0) return {name}; // name starts_with line
+            return {};
+        }
+    protected:
+        const std::string& Name() const { return name; }
+        bool IsEnabled() const { return enabled; }
+    private:
+        const std::string name;
+        bool enabled;
+    };
+
+    // ********************************************************************
+
+    // free utility function to get completions from a list of commands and the current line
+    inline std::vector<std::string> GetCompletions(
+        const std::shared_ptr<std::vector<std::shared_ptr<Command>>>& cmds,
+        const std::string& currentLine)
+    {
+        std::vector<std::string> result;
+        std::for_each(cmds->begin(), cmds->end(),
+            [&currentLine,&result](const auto& cmd)
+            {
+                auto c = cmd->GetCompletionRecursive(currentLine);
+                result.insert(
+                    result.end(),
+                    std::make_move_iterator(c.begin()),
+                    std::make_move_iterator(c.end())
+                );
+            }
+        );
+        return result;
+    }
+
+    // ********************************************************************
+
+    class CliSession
+    {
+    public:
+        CliSession(Cli& _cli, std::ostream& _out, std::size_t historySize = 100);
+        virtual ~CliSession() noexcept { coutPtr->UnRegister(out); }
+
+        // disable value semantics
+        CliSession(const CliSession&) = delete;
+        CliSession& operator = (const CliSession&) = delete;
+        // disable move semantics
+        CliSession(CliSession&&) = delete;
+        CliSession& operator = (CliSession&&) = delete;
+
+        void Feed(const std::string& cmd);
+
+        void Prompt();
+
+        void Current(Menu* menu) { current = menu; }
+
+        std::ostream& OutStream() { return out; }
+
+        void Help() const;
+
+        void Enter() 
+        {
+            cli.EnterAction(out);
+
+            if (enterAction)
+                enterAction(out);
+        }
+
+        void Exit()
+        {
+            exitAction(out);
+            cli.ExitAction(out);
+
+            auto cmds = history.GetCommands();
+            cli.StoreCommands(cmds);
+
+            exit = true; // prevent the prompt to be shown
+        }
+
+        void EnterAction(const std::function<void(std::ostream&)>& action)
+        {
+            enterAction = action;
+        }
+
+        void ExitAction(const std::function<void(std::ostream&)>& action)
+        {
+            exitAction = action;
+        }
+
+        void ShowHistory() const { history.Show(out); }
+
+        std::string PreviousCmd(const std::string& line)
+        {
+            return history.Previous(line);
+        }
+
+        std::string NextCmd()
+        {
+            return history.Next();
+        }
+
+        std::vector<std::string> GetCompletions(std::string currentLine) const;
+
+    private:
+
+        Cli& cli;
+        std::shared_ptr<cli::OutStream> coutPtr;
+        Menu* current;
+        std::unique_ptr<Menu> globalScopeMenu;
+        std::ostream& out;
+        std::function< void(std::ostream&)> enterAction = []( std::ostream& ) noexcept {};
+        std::function< void(std::ostream&)> exitAction = []( std::ostream& ) noexcept {};
+        detail::History history;
+        bool exit{ false }; // to prevent the prompt after exit command
+    };
+
+    // ********************************************************************
+
+    class CmdHandler
+    {
+    public:
+        using CmdVec = std::vector<std::shared_ptr<Command>>;
+        CmdHandler() : descriptor(std::make_shared<Descriptor>()) {}
+        CmdHandler(std::weak_ptr<Command> c, std::weak_ptr<CmdVec> v) :
+            descriptor(std::make_shared<Descriptor>(c, v))
+        {}
+        void Enable() { if (descriptor) descriptor->Enable(); }
+        void Disable() { if (descriptor) descriptor->Disable(); }
+        void Remove() { if (descriptor) descriptor->Remove(); }
+    private:
+        struct Descriptor
+        {
+            Descriptor() = default;
+            Descriptor(std::weak_ptr<Command> c, std::weak_ptr<CmdVec> v) :
+                cmd(std::move(c)), cmds(std::move(v))
+            {}
+            void Enable()
+            {
+                if (auto c = cmd.lock())
+                    c->Enable();
+            }
+            void Disable()
+            {
+                if(auto c = cmd.lock())
+                    c->Disable();
+            }
+            void Remove()
+            {
+                auto scmd = cmd.lock();
+                auto scmds = cmds.lock();
+                if (scmd && scmds)
+                {
+                    auto i = std::find_if(
+                        scmds->begin(),
+                        scmds->end(),
+                        [&](const auto& c){ return c.get() == scmd.get(); }
+                    );
+                    if (i != scmds->end())
+                        scmds->erase(i);
+                }
+            }
+            std::weak_ptr<Command> cmd;
+            std::weak_ptr<CmdVec> cmds;
+        };
+        std::shared_ptr<Descriptor> descriptor;
+    };
+
+    // ********************************************************************
+
+    class Menu : public Command
+    {
+    public:
+        // disable value and move semantics
+        Menu(const Menu&) = delete;
+        Menu& operator = (const Menu&) = delete;
+        Menu(Menu&&) = delete;
+        Menu& operator = (Menu&&) = delete;
+
+        Menu() : Command({}), parent(nullptr), description(), cmds(std::make_shared<Cmds>()) {}
+
+        explicit Menu(const std::string& _name, std::string desc = "(menu)", const std::string& _prompt="") :
+            Command(_name),
+            parent(nullptr),
+            description(std::move(desc)),
+            prompt(_prompt.empty() ? _name : _prompt),
+            cmds(std::make_shared<Cmds>())
+        {}
+
+        template <typename R, typename ... Args>
+        CmdHandler Insert(const std::string& cmdName, R (*f)(std::ostream&, Args...), const std::string& help, const std::vector<std::string>& parDesc={});
+        
+        template <typename F>
+        CmdHandler Insert(const std::string& cmdName, F f, const std::string& help = "", const std::vector<std::string>& parDesc={})
+        {
+            // dispatch to private Insert methods
+            return Insert(cmdName, help, parDesc, f, &F::operator());
+        }
+
+        template <typename F>
+        CmdHandler Insert(const std::string& cmdName, const std::vector<std::string>& parDesc, F f, const std::string& help = "")
+        {
+            // dispatch to private Insert methods
+            return Insert(cmdName, help, parDesc, f, &F::operator());
+        }
+
+        CmdHandler Insert(std::unique_ptr<Command>&& cmd)
+        {
+            std::shared_ptr<Command> scmd(std::move(cmd));
+            CmdHandler c(scmd, cmds);
+            cmds->push_back(scmd);
+            return c;
+        }
+
+        CmdHandler Insert(std::unique_ptr<Menu>&& menu)
+        {
+            std::shared_ptr<Menu> smenu(std::move(menu));
+            CmdHandler c(smenu, cmds);
+            smenu->parent = this;
+            cmds->push_back(smenu);
+            return c;
+        }
+
+        bool Exec(const std::vector<std::string>& cmdLine, CliSession& session) override
+        {
+            if (!IsEnabled())
+                return false;
+            if (cmdLine[0] == Name())
+            {
+                if (cmdLine.size() == 1)
+                {
+                    session.Current(this);
+                    return true;
+                }
+                else
+                {
+                    // check also for subcommands
+                    std::vector<std::string > subCmdLine(cmdLine.begin()+1, cmdLine.end());
+                    for (auto& cmd: *cmds)
+                        if (cmd->Exec( subCmdLine, session )) return true;
+                }
+            }
+            return false;
+        }
+
+        bool ScanCmds(const std::vector<std::string>& cmdLine, CliSession& session)
+        {
+            if (!IsEnabled())
+                return false;
+            for (auto& cmd: *cmds)
+                if (cmd->Exec(cmdLine, session))
+                    return true;
+            return (parent && parent->Exec(cmdLine, session));
+        }
+
+        std::string Prompt() const
+        {
+            return prompt;
+        }
+
+        void MainHelp(std::ostream& out)
+        {
+            if (!IsEnabled()) return;
+            for (const auto& cmd: *cmds)
+                cmd->Help(out);
+            if (parent != nullptr)
+                parent->Help(out);
+        }
+
+        void Help(std::ostream& out) const override
+        {
+            if (!IsEnabled()) return;
+            out << " - " << Name() << "\n\t" << description << "\n";
+        }
+
+        // returns:
+        // - the completions of this menu command
+        // - the recursive completions of subcommands
+        // - the recursive completions of parent menu
+        std::vector<std::string> GetCompletions(const std::string& currentLine) const
+        {
+            auto result = cli::GetCompletions(cmds, currentLine);
+            if (parent != nullptr)
+            {
+                auto c = parent->GetCompletionRecursive(currentLine);
+                result.insert(result.end(), std::make_move_iterator(c.begin()), std::make_move_iterator(c.end()));
+            }
+            return result;
+        }
+
+        // returns:
+        // - the completion of this menu command
+        // - the recursive completions of the subcommands
+        std::vector<std::string> GetCompletionRecursive(const std::string& line) const override
+        {
+            if (line.rfind(Name(), 0) == 0) // line starts_with Name()
+            {
+                auto rest = line;
+                rest.erase(0, Name().size());
+                // trim_left(rest);
+                rest.erase(rest.begin(), std::find_if(rest.begin(), rest.end(), [](int ch) { return !std::isspace(ch); }));
+                std::vector<std::string> result;
+                for (const auto& cmd: *cmds)
+                {
+                    auto cs = cmd->GetCompletionRecursive(rest);
+                    for (const auto& c: cs)
+                        result.push_back(Name() + ' ' + c); // concat submenu with command
+                }
+                return result;
+            }
+            return Command::GetCompletionRecursive(line);
+        }
+
+    private:
+
+        template <typename F, typename R, typename ... Args>
+        CmdHandler Insert(const std::string& name, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, Args...) const);
+
+        template <typename F, typename R>
+        CmdHandler Insert(const std::string& name, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, const std::vector<std::string>&) const);
+
+        template <typename F, typename R>
+        CmdHandler Insert(const std::string& name, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, std::vector<std::string>) const);
+
+        Menu* parent{ nullptr };
+        const std::string description;
+        const std::string prompt;
+        // using shared_ptr instead of unique_ptr to get a weak_ptr
+        // for the CmdHandler::Descriptor
+        using Cmds = std::vector<std::shared_ptr<Command>>;
+        std::shared_ptr<Cmds> cmds;
+    };
+
+    // ********************************************************************
+
+    template <typename ... Args>
+    struct Select;
+
+    template <typename P, typename ... Args>
+    struct Select<P, Args...>
+    {
+        template <typename F, typename InputIt>
+        static void Exec(const F& f, InputIt first, InputIt last)
+        {
+            assert( first != last );
+            assert( std::distance(first, last) == 1+sizeof...(Args) );
+            const P p = detail::from_string<typename std::decay<P>::type>(*first);
+            auto g = [&](auto ... pars){ f(p, pars...); };
+            Select<Args...>::Exec(g, std::next(first), last);
+        }
+    };
+
+    template <>
+    struct Select<>
+    {
+        template <typename F, typename InputIt>
+        static void Exec(const F& f, InputIt first, InputIt last)
+        {
+            // silence the unused warning in release mode when assert is disabled
+            static_cast<void>(first);
+            static_cast<void>(last);
+
+            assert(first == last);
+            
+            f();
+        }
+    };
+
+    template <typename ... Args>
+    struct PrintDesc;
+
+    template <typename P, typename ... Args>
+    struct PrintDesc<P, Args...>
+    {
+        static void Dump(std::ostream& out)
+        {
+            out << " " << TypeDesc< typename std::decay<P>::type >::Name();
+            PrintDesc<Args...>::Dump(out);
+        }
+    };
+
+    template <>
+    struct PrintDesc<>
+    {
+        static void Dump(std::ostream& /*out*/) {}
+    };
+
+    // *******************************************
+
+    template <typename F, typename ... Args>
+    class VariadicFunctionCommand : public Command
+    {
+    public:
+        // disable value semantics
+        VariadicFunctionCommand(const VariadicFunctionCommand&) = delete;
+        VariadicFunctionCommand& operator = (const VariadicFunctionCommand&) = delete;
+
+        VariadicFunctionCommand(
+            const std::string& _name,
+            F fun,
+            std::string desc,
+            std::vector<std::string> parDesc
+        )
+            : Command(_name), func(std::move(fun)), description(std::move(desc)), parameterDesc(std::move(parDesc))
+        {
+        }
+
+        bool Exec(const std::vector< std::string >& cmdLine, CliSession& session) override
+        {
+            if (!IsEnabled()) return false;
+            const std::size_t paramSize = sizeof...(Args);
+            if (cmdLine.size() != paramSize+1) return false;
+            if (Name() == cmdLine[0])
+            {
+                try
+                {
+                    auto g = [&](auto ... pars){ func( session.OutStream(), pars... ); };
+                    Select<Args...>::Exec(g, std::next(cmdLine.begin()), cmdLine.end());
+                }
+                catch (std::bad_cast&)
+                {
+                    return false;
+                }
+                return true;
+            }
+            return false;
+        }
+
+        void Help(std::ostream& out) const override
+        {
+            if (!IsEnabled()) return;
+            out << " - " << Name();
+            if (parameterDesc.empty())
+                PrintDesc<Args...>::Dump(out);
+            for (auto& s: parameterDesc)
+                out << " <" << s << '>';
+            out << "\n\t" << description << "\n";
+        }
+
+    private:
+
+        const F func;
+        const std::string description;
+        const std::vector<std::string> parameterDesc;
+    };
+
+
+    template <typename F>
+    class FreeformCommand : public Command
+    {
+    public:
+        // disable value semantics
+        FreeformCommand(const FreeformCommand&) = delete;
+        FreeformCommand& operator = (const FreeformCommand&) = delete;
+
+        FreeformCommand(
+            const std::string& _name,
+            F fun,
+            std::string desc,
+            std::vector<std::string> parDesc
+        )
+            : Command(_name), func(std::move(fun)), description(std::move(desc)), parameterDesc(std::move(parDesc))
+        {
+        }
+
+        bool Exec(const std::vector< std::string >& cmdLine, CliSession& session) override
+        {
+            if (!IsEnabled()) return false;
+            assert(!cmdLine.empty());
+            if (Name() == cmdLine[0])
+            {
+                func(session.OutStream(), std::vector<std::string>(std::next(cmdLine.begin()), cmdLine.end()));
+                return true;
+            }
+            return false;
+        }
+        void Help(std::ostream& out) const override
+        {
+            if (!IsEnabled()) return;
+            out << " - " << Name();
+            if (parameterDesc.empty())
+                PrintDesc<std::vector<std::string>>::Dump(out);            
+            for (auto& s: parameterDesc)
+                out << " <" << s << '>';
+            out << "\n\t" << description << "\n";
+        }
+
+    private:
+
+        const F func;
+        const std::string description;
+        const std::vector<std::string> parameterDesc;
+    };
+
+
+    // ********************************************************************
+
+    // CliSession implementation
+
+    inline CliSession::CliSession(Cli& _cli, std::ostream& _out, std::size_t historySize) :
+            cli(_cli),
+            coutPtr(Cli::CoutPtr()),
+            current(cli.RootMenu()),
+            globalScopeMenu(std::make_unique< Menu >()),
+            out(_out),
+            history(historySize)
+        {
+            history.LoadCommands(cli.GetCommands());
+
+            coutPtr->Register(out);
+            globalScopeMenu->Insert(
+                "help",
+                [this](std::ostream&){ Help(); },
+                "This help message"
+            );
+            globalScopeMenu->Insert(
+                "exit",
+                [this](std::ostream&){ Exit(); },
+                "Quit the session"
+            );
+#ifdef CLI_HISTORY_CMD
+            globalScopeMenu->Insert(
+                "history",
+                [this](std::ostream&){ ShowHistory(); },
+                "Show the history"
+            );
+#endif
+        }
+
+    inline void CliSession::Feed(const std::string& cmd)
+    {
+        std::vector<std::string> strs;
+        detail::split(strs, cmd);
+        if (strs.empty()) return; // just hit enter
+
+        history.NewCommand(cmd); // add anyway to history
+
+        try
+        {
+
+            // global cmds check
+            bool found = globalScopeMenu->ScanCmds(strs, *this);
+
+            // root menu recursive cmds check
+            if (!found) found = current->ScanCmds(strs, *this);
+
+            if (!found) // error msg if not found
+                out << "wrong command: " << cmd << '\n';
+        }
+        catch(const std::exception& e)
+        {
+            cli.StdExceptionHandler(out, cmd, e);
+        }
+        catch(...)
+        {
+            out << "Cli. Unknown exception caught handling command line \""
+                << cmd
+                << "\"\n";
+        }
+    }
+
+    inline void CliSession::Prompt()
+    {
+        if (exit) return;
+        out << beforePrompt
+            << current->Prompt()
+            << afterPrompt
+            << "> "
+            << std::flush;
+    }
+
+    inline void CliSession::Help() const
+    {
+        out << "Commands available:\n";
+        globalScopeMenu->MainHelp(out);
+        current -> MainHelp( out );
+    }
+
+    inline std::vector<std::string> CliSession::GetCompletions(std::string currentLine) const
+    {
+        // trim_left(currentLine);
+        currentLine.erase(currentLine.begin(), std::find_if(currentLine.begin(), currentLine.end(), [](int ch) { return !std::isspace(ch); }));
+        auto v1 = globalScopeMenu->GetCompletions(currentLine);
+        auto v3 = current->GetCompletions(currentLine);
+        v1.insert(v1.end(), std::make_move_iterator(v3.begin()), std::make_move_iterator(v3.end()));
+
+        // removes duplicates (std::unique requires a sorted container)
+        std::sort(v1.begin(), v1.end());
+        auto ip = std::unique(v1.begin(), v1.end());
+        v1.resize(static_cast<std::size_t>(std::distance(v1.begin(), ip)));
+
+        return v1;
+    }
+
+    // Menu implementation
+
+    template <typename R, typename ... Args>
+    CmdHandler Menu::Insert(const std::string& cmdName, R (*f)(std::ostream&, Args...), const std::string& help, const std::vector<std::string>& parDesc)
+    {
+        using F = R (*)(std::ostream&, Args...);
+        return Insert(std::make_unique<VariadicFunctionCommand<F, Args ...>>(cmdName, f, help, parDesc));
+    }
+
+    template <typename F, typename R, typename ... Args>
+    CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, Args...) const )
+    {
+        return Insert(std::make_unique<VariadicFunctionCommand<F, Args ...>>(cmdName, f, help, parDesc));
+    }
+
+    template <typename F, typename R>
+    CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, const std::vector<std::string>& args) const )
+    {
+        return Insert(std::make_unique<FreeformCommand<F>>(cmdName, f, help, parDesc));
+    }
+
+    template <typename F, typename R>
+    CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, std::vector<std::string> args) const )
+    {
+        return Insert(std::make_unique<FreeformCommand<F>>(cmdName, f, help, parDesc));
+    }
+
+} // namespace cli
+
+#endif // CLI_CLI_H
diff --git a/external/cli/clifilesession.h b/external/cli/clifilesession.h
new file mode 100644 (file)
index 0000000..68177e5
--- /dev/null
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_CLIFILESESSION_H
+#define CLI_CLIFILESESSION_H
+
+#include <string>
+#include <iostream>
+#include <stdexcept> // std::invalid_argument
+#include "cli.h" // CliSession
+
+namespace cli
+{
+
+class CliFileSession : public CliSession
+{
+public:
+    /// @throw std::invalid_argument if @c _in or @c out are invalid streams
+    explicit CliFileSession(Cli& _cli, std::istream& _in=std::cin, std::ostream& _out=std::cout) :
+        CliSession(_cli, _out, 1),
+        exit(false),
+        in(_in)
+    {
+        if (!_in.good()) throw std::invalid_argument("istream invalid");
+        if (!_out.good()) throw std::invalid_argument("ostream invalid");
+        ExitAction(
+            [this](std::ostream&) noexcept
+            {
+                exit = true;
+            }
+        );
+    }
+    void Start()
+    {
+        Enter();
+
+        while(!exit)
+        {
+            Prompt();
+            std::string line;
+            if (!in.good())
+                Exit();
+            std::getline(in, line);
+            if (in.eof())
+                Exit();
+            else
+                Feed(line);
+        }
+    }
+
+private:
+    bool exit;
+    std::istream& in;
+};
+
+} // namespace cli
+
+#endif // CLI_CLIFILESESSION_H
+
diff --git a/external/cli/clilocalsession.h b/external/cli/clilocalsession.h
new file mode 100644 (file)
index 0000000..5528350
--- /dev/null
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_CLILOCALSESSION_H
+#define CLI_CLILOCALSESSION_H
+
+#include <ostream> // std::ostream
+#include "detail/inputhandler.h"
+#include "cli.h" // CliSession
+#include "detail/keyboard.h"
+
+namespace cli
+{
+
+class Scheduler; // forward declaration
+
+/**
+ * @brief CliLocalTerminalSession represents a local session.
+ * You should instantiate it to start an interactive prompt on the standard
+ * input/output of your application.
+ * The handlers of the commands will be invoked in the same thread the @c Scheduler runs. 
+ */
+class CliLocalTerminalSession : public CliSession
+{
+public:
+
+    /**
+     * @brief Construct a new Cli Local Terminal Session object that uses the specified @c std::ostream
+     * for output. You can also specify a size for the command history. 
+     * 
+     * @param _cli The cli object that defines the menu hierarchy for this session
+     * @param scheduler The scheduler that will process the command handlers
+     * @param _out the output stream where command output will be printed
+     * @param historySize the size of the command history
+     */
+    CliLocalTerminalSession(Cli& _cli, Scheduler& scheduler, std::ostream& _out, std::size_t historySize = 100) :
+        CliSession(_cli, _out, historySize),
+        kb(scheduler),
+        ih(*this, kb)
+    {
+        Enter();
+        Prompt();
+    }
+
+private:
+    detail::Keyboard kb;
+    detail::InputHandler ih;
+};
+
+using CliLocalSession = CliLocalTerminalSession;
+
+} // namespace cli
+
+#endif // CLI_CLILOCALSESSION_H
+
diff --git a/external/cli/colorprofile.h b/external/cli/colorprofile.h
new file mode 100644 (file)
index 0000000..518ce57
--- /dev/null
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_COLORPROFILE_H_
+#define CLI_COLORPROFILE_H_
+
+#include "detail/rang.h"
+
+namespace cli
+{
+
+inline bool& Color() { static bool color; return color; }
+
+inline void SetColor() { Color() = true; }
+inline void SetNoColor() { Color() = false; }
+
+enum BeforePrompt { beforePrompt };
+enum AfterPrompt { afterPrompt };
+enum BeforeInput { beforeInput };
+enum AfterInput { afterInput };
+
+inline std::ostream& operator<<(std::ostream& os, BeforePrompt)
+{
+    if ( Color() ) { os << detail::rang::control::forceColor << detail::rang::fg::green << detail::rang::style::bold; }
+    return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, AfterPrompt)
+{
+    os << detail::rang::style::reset;
+    return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, BeforeInput)
+{
+    if ( Color() ) { os << detail::rang::control::forceColor << detail::rang::fgB::gray; }
+    return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, AfterInput)
+{
+    os << detail::rang::style::reset;
+    return os;
+}
+
+} // namespace cli
+
+#endif // CLI_COLORPROFILE_H_
+
+
diff --git a/external/cli/detail/boostasiolib.h b/external/cli/detail/boostasiolib.h
new file mode 100644 (file)
index 0000000..a82c53c
--- /dev/null
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_BOOSTASIOLIB_H_
+#define CLI_DETAIL_BOOSTASIOLIB_H_
+
+/**
+ * This header file provides the class `cli::BoostAsioLib`, using the right
+ * implementation according to the version of boost libraries included.
+ */
+
+#include <boost/version.hpp>
+
+#if BOOST_VERSION < 106600
+    #include "oldboostasiolib.h"
+    namespace cli { namespace detail { using BoostAsioLib = OldBoostAsioLib; } }
+#else
+    #include "newboostasiolib.h"
+    namespace cli { namespace detail { using BoostAsioLib = NewBoostAsioLib; } }
+#endif
+
+#endif // CLI_DETAIL_BOOSTASIOLIB_H_
+
diff --git a/external/cli/detail/commonprefix.h b/external/cli/detail/commonprefix.h
new file mode 100644 (file)
index 0000000..626e089
--- /dev/null
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_COMMONPREFIX_H_
+#define CLI_DETAIL_COMMONPREFIX_H_
+
+#include <cassert>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+namespace cli
+{
+namespace detail
+{
+
+inline std::string CommonPrefix(const std::vector<std::string>& v)
+{
+    assert(!v.empty());
+    std::string prefix;
+
+    // find the shorter string
+    auto smin = std::min_element(v.begin(), v.end(),
+                [] (const std::string& s1, const std::string& s2)
+                {
+                    return s1.size() < s2.size();
+                });
+
+    for (std::size_t i = 0; i < smin->size(); ++i)
+    {
+        // check if i-th element is equal in each input string
+        const char c = (*smin)[i];
+        for (auto& x: v)
+            if (x[i] != c) return prefix;
+        prefix += c;
+    }
+
+    return prefix;
+}
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_COMMONPREFIX_H_
diff --git a/external/cli/detail/fromstring.h b/external/cli/detail/fromstring.h
new file mode 100644 (file)
index 0000000..e66fd85
--- /dev/null
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_FROMSTRING_H_
+#define CLI_DETAIL_FROMSTRING_H_
+
+// #define CLI_FROMSTRING_USE_BOOST
+
+#ifdef CLI_FROMSTRING_USE_BOOST
+
+#include <boost/lexical_cast.hpp>
+
+namespace cli
+{
+namespace detail
+{
+
+template <typename T>
+inline
+T from_string(const std::string& s)
+{
+    return boost::lexical_cast<T>(s);
+}
+
+} // namespace detail
+} // namespace cli
+
+#else
+
+#include <exception>
+#include <limits>
+#include <string>
+#include <sstream>
+
+namespace cli
+{
+
+    namespace detail
+    {
+        class bad_conversion : public std::bad_cast
+        {
+            public:
+                const char* what() const noexcept override {
+                    return "bad from_string conversion: "
+                        "source string value could not be interpreted as target";
+                }
+        };
+
+template <typename T>
+inline T from_string(const std::string& s);
+
+template <>
+inline std::string from_string(const std::string& s)
+{
+    return s;
+}
+
+template <>
+inline std::nullptr_t from_string(const std::string& /*s*/)
+{
+    return nullptr;
+}
+
+namespace detail
+{
+
+template <typename T>
+inline T unsigned_digits_from_string(const std::string& s)
+{
+    if (s.empty())
+        throw bad_conversion();
+    T result = 0;
+    for (char c: s)
+    {
+        if (!std::isdigit(c))
+            throw bad_conversion();
+        const T digit = static_cast<T>( c - '0' );
+        const T tmp = (result * 10) + digit;
+        if (result != ((tmp-digit)/10) || (tmp < result))
+            throw bad_conversion();
+        result = tmp;
+    }
+    return result;
+}
+
+template <typename T>
+inline T unsigned_from_string(std::string s)
+{
+    if (s.empty())
+        throw bad_conversion();
+    if (s[0] == '+')
+    {
+        s = s.substr(1);
+    }
+    return unsigned_digits_from_string<T>(s);
+}
+
+template <typename T>
+inline T signed_from_string(std::string s)
+{
+    if (s.empty())
+        throw bad_conversion();
+    using U = std::make_unsigned_t<T>;
+    if (s[0] == '-')
+    {
+        s = s.substr(1);
+        const U val = unsigned_digits_from_string<U>(s);
+        if ( val > static_cast<U>( - std::numeric_limits<T>::min() ) )
+            throw bad_conversion();
+        return (- static_cast<T>(val));
+    }
+    else if (s[0] == '+')
+    {
+        s = s.substr(1);
+    }
+    const U val = unsigned_digits_from_string<U>(s);
+    if (val > static_cast<U>( std::numeric_limits<T>::max() ))
+        throw bad_conversion();
+    return static_cast<T>(val);
+}
+
+} // namespace detail
+
+// signed
+
+template <> inline signed char 
+from_string(const std::string& s) { return detail::signed_from_string<signed char>(s); }
+
+template <> inline short int 
+from_string(const std::string& s) { return detail::signed_from_string<short int>(s); }
+
+template <> inline int
+from_string(const std::string& s) { return detail::signed_from_string<int>(s); }
+
+template <> inline long int
+from_string(const std::string& s) { return detail::signed_from_string<long int>(s); }
+
+template <> inline long long int
+from_string(const std::string& s) { return detail::signed_from_string<long long int>(s); }
+
+// unsigned
+
+template <> inline unsigned char
+from_string(const std::string& s) { return detail::unsigned_from_string<unsigned char>(s); }
+
+template <> inline unsigned short int
+from_string(const std::string& s) { return detail::unsigned_from_string<unsigned short int>(s); }
+
+template <> inline unsigned int
+from_string(const std::string& s) { return detail::unsigned_from_string<unsigned int>(s); }
+
+template <> inline unsigned long int
+from_string(const std::string& s) { return detail::unsigned_from_string<unsigned long int>(s); }
+
+template <> inline unsigned long long int
+from_string(const std::string& s) { return detail::unsigned_from_string<unsigned long long int>(s); }
+
+// bool
+
+template <>
+inline bool from_string(const std::string& s)
+{
+    if (s == "true") return true;
+    else if (s == "false") return false;
+    const auto value = detail::signed_from_string<long long int>(s);
+    if (value == 1) return true;
+    else if (value == 0) return false;
+    throw bad_conversion();            
+}
+
+// chars
+
+template <>
+inline char from_string(const std::string& s)
+{
+    if (s.size() != 1) throw bad_conversion();
+    return s[0];            
+}
+
+// floating points
+
+template <>
+inline float from_string(const std::string& s)
+{
+    if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) )
+        throw bad_conversion();
+    std::string::size_type sz;
+    float result = {};
+    try {
+        result = std::stof(s, &sz);
+    } catch (const std::exception&) {
+        throw bad_conversion();
+    }
+    if (sz != s.size())
+        throw bad_conversion();
+    return result;
+}
+
+template <>
+inline double from_string(const std::string& s)
+{
+    if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) )
+        throw bad_conversion();
+    std::string::size_type sz;
+    double result = {};
+    try {
+        result = std::stod(s, &sz);
+    } catch (const std::exception&) {
+        throw bad_conversion();
+    }
+    if (sz != s.size())
+        throw bad_conversion();
+    return result;
+}
+
+template <>
+inline long double from_string(const std::string& s)
+{
+    if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) )
+        throw bad_conversion();
+    std::string::size_type sz;
+    long double result = {};
+    try {
+        result = std::stold(s, &sz);
+    } catch (const std::exception&) {
+        throw bad_conversion();
+    }
+    if (sz != s.size())
+        throw bad_conversion();
+    return result;
+}
+
+// fallback: operator <<
+
+template <typename T>
+inline T from_string(const std::string& s)
+{
+    std::stringstream interpreter;
+    T result;
+
+    if(!(interpreter << s) ||
+        !(interpreter >> result) ||
+        !(interpreter >> std::ws).eof())
+        throw bad_conversion();
+
+    return result;
+}
+
+    } // namespace detail
+
+} // namespace cli
+
+
+#endif // CLI_FROMSTRING_USE_BOOST
+
+#endif // CLI_DETAIL_FROMSTRING_H_
diff --git a/external/cli/detail/genericasioremotecli.h b/external/cli/detail/genericasioremotecli.h
new file mode 100644 (file)
index 0000000..576520a
--- /dev/null
@@ -0,0 +1,590 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_GENERICASIOREMOTECLI_H_
+#define CLI_DETAIL_GENERICASIOREMOTECLI_H_
+
+#include <memory>
+#include "../cli.h"
+#include "inputhandler.h"
+#include "server.h"
+#include "inputdevice.h"
+#include "genericasioscheduler.h"
+
+namespace cli
+{
+namespace detail
+{
+
+// *******************************************************************************
+
+class TelnetSession : public Session
+{
+public:
+    explicit TelnetSession(asiolib::ip::tcp::socket _socket) :
+        Session(std::move(_socket))
+    {}
+
+protected:
+
+    std::string Encode(const std::string& _data) const override
+    {
+        std::string result;
+        for (char c: _data)
+        {
+            if (c == '\n') result += '\r';
+            result += c;
+        }
+        return result;
+    }
+
+    void OnConnect() override
+    {
+        // to specify hexadecimal value as chars we use
+        // the syntax \xVVV
+        // and the std::string ctor that takes the size,
+        // so that it's not null-terminated
+        //std::string msg{ "\x0FF\x0FD\x027", 3 };
+        //waitAck = true;
+        //std::string iacDoSuppressGoAhead{ "\x0FF\x0FD\x003", 3 };
+        //this -> OutStream() << iacDoSuppressGoAhead << std::flush;
+
+        // https://www.ibm.com/support/knowledgecenter/SSLTBW_1.13.0/com.ibm.zos.r13.hald001/telcmds.htm
+
+        static const std::string iacDoLineMode{ "\x0FF\x0FD\x022", 3 };
+        this -> OutStream() << iacDoLineMode << std::flush;
+
+        static const std::string iacSbLineMode0IacSe{ "\x0FF\x0FA\x022\x001\x000\x0FF\x0F0", 7 };
+        this -> OutStream() << iacSbLineMode0IacSe << std::flush;
+
+        static const std::string iacWillEcho{ "\x0FF\x0FB\x001", 3 };
+        this -> OutStream() << iacWillEcho << std::flush;
+
+/*
+        constexpr char IAC = '\x0FF'; // 255
+        constexpr char DO = '\x0FD'; // 253
+        constexpr char VT100 = '\x030'; // 48
+
+        this -> OutStream() << IAC << DO << VT100 << std::flush;
+*/
+        //cliSession.Prompt();
+    }
+    void OnDisconnect() override {}
+    void OnError() override {}
+#if 0
+    void OnDataReceived(const std::string& data) override
+    {
+        if (waitAck)
+        {
+            if ( data[0] == '\x0FF' )
+            {
+                // TODO
+                for (size_t i = 0; i < data.size(); ++i)
+                    std::cout << static_cast<int>( data[i] & 0xFF ) << ' ';
+                std::cout << std::endl;
+
+                for (size_t i = 0; i < data.size(); ++i)
+                    std::cout << "0x" << std::hex << static_cast<int>( data[i] & 0xFF ) << std::dec << ' ';
+                std::cout << std::endl;
+
+                for (size_t i = 0; i < data.size(); ++i)
+                    switch (static_cast<int>( data[i] & 0xFF ))
+                    {
+                        case 0xFF: std::cout << "IAC "; break;
+                        case 0xFE: std::cout << "DONT "; break;
+                        case 0xFD: std::cout << "DO "; break;
+                        case 0xFC: std::cout << "WONT "; break;
+                        case 0xFB: std::cout << "WILL "; break;
+                        case 0xFA: std::cout << "SB "; break;
+                        case 0xF9: std::cout << "GoAhead "; break;
+                        case 0xF8: std::cout << "EraseLine "; break;
+                        case 0xF7: std::cout << "EraseCharacter "; break;
+                        case 0xF6: std::cout << "AreYouThere "; break;
+                        case 0xF5: std::cout << "AbortOutput "; break;
+                        case 0xF4: std::cout << "InterruptProcess "; break;
+                        case 0xF3: std::cout << "Break "; break;
+                        case 0xF2: std::cout << "DataMark "; break;
+                        case 0xF1: std::cout << "NOP "; break;
+                        case 0xF0: std::cout << "SE "; break;
+                        default: std::cout << (static_cast<int>( data[i] & 0xFF )) << ' ';
+                    }
+                std::cout << std::endl;
+
+            }
+            waitAck = false;
+/*
+            std::string iacWillSuppressGoAhead{ "\x0FF\x0FB\x003", 3 };
+            if ( data == iacWillSuppressGoAhead )
+            {
+                waitAck = false;
+                cliSession.Prompt();
+            }
+
+            else
+                Disconnect();
+*/
+        }
+        else
+        {
+            for (size_t i = 0; i < data.size(); ++i)
+                Feed(data[i]);
+/*
+            auto str = data;
+            // trim trailing spaces
+            std::size_t endpos = str.find_last_not_of(" \t\r\n");
+            if( std::string::npos != endpos ) str = str.substr( 0, endpos+1 );
+
+            if ( cliSession.Feed( str ) ) cliSession.Prompt();
+            else Disconnect();
+*/
+        }
+    }
+#else
+
+    /*
+    See
+    https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
+    for a list of telnet options
+    */
+
+    enum
+    {
+        SE = '\x0F0',                  // End of subnegotiation parameters.
+        NOP = '\x0F1',                 // No operation.
+        DataMark = '\x0F2',            // The data stream portion of a Synch.
+                                       // This should always be accompanied
+                                       // by a TCP Urgent notification.
+        Break = '\x0F3',               // NVT character BRK.
+        InterruptProcess = '\x0F4',    // The function IP.
+        AbortOutput = '\x0F5',         // The function AO.
+        AreYouThere = '\x0F6',         // The function AYT.
+        EraseCharacter = '\x0F7',      // The function EC.
+        EraseLine = '\x0F8',           // The function EL.
+        GoAhead = '\x0F9',             // The GA signal.
+        SB = '\x0FA',                  // Indicates that what follows is
+                                       // subnegotiation of the indicated
+                                       // option.
+        WILL = '\x0FB',                // Indicates the desire to begin
+                                       // performing, or confirmation that
+                                       // you are now performing, the
+                                       // indicated option.
+        WONT = '\x0FC',                // Indicates the refusal to perform,
+                                       // or continue performing, the
+                                       // indicated option.
+        DO = '\x0FD',                  // Indicates the request that the
+                                       // other party perform, or
+                                       // confirmation that you are expecting
+                                       // the other party to perform, the
+                                       // indicated option.
+        DONT = '\x0FE',                // Indicates the demand that the
+                                       // other party stop performing,
+                                       // or confirmation that you are no
+                                       // longer expecting the other party
+                                       // to perform, the indicated option.
+        IAC = '\x0FF',                 // Data Byte 255.
+
+        _ECHO = '\x001',
+        SUPPRESS_GO_AHEAD = '\x003',
+        TERMINAL_TYPE = '\x018',
+        NEGOTIATE_ABOUT_WIN_SIZE = '\x01F',
+        TERMINAL_SPEED = '\x020',
+        NEW_ENV_OPTION = '\x027'
+    };
+
+    void OnDataReceived(const std::string& _data) override
+    {
+        for (auto c: _data)
+            Consume(c);
+    }
+
+private:
+
+    void Consume(signed char c)
+    {
+        if (escape)
+        {
+            if (c == IAC)
+                Data(c);
+            else
+                Command(c);
+            escape = false;
+        }
+        else
+        {
+            if (c == IAC)
+                escape = true;
+            else
+                Data(c);
+        }
+    }
+
+    void Data(signed char c)
+    {
+        switch(state)
+        {
+            case State::data:
+                Output(c);
+                break;
+            case State::sub:
+                RxSub(c);
+                break;
+            case State::wait_will:
+                RxWill(c);
+                state = State::data;
+                break;
+            case State::wait_wont:
+                RxWont(c);
+                state = State::data;
+                break;
+            case State::wait_do:
+                RxDo(c);
+                state = State::data;
+                break;
+            case State::wait_dont:
+                RxDont(c);
+                state = State::data;
+                break;
+        }
+    }
+
+    void Command(char c)
+    {
+/*
+        switch (static_cast<int>( c & 0xFF ))
+        {
+            case 0xFF: std::cout << "IAC" << std::endl; break;
+            case 0xFE: std::cout << "DONT" << std::endl; break;
+            case 0xFD: std::cout << "DO" << std::endl; break;
+            case 0xFC: std::cout << "WONT" << std::endl; break;
+            case 0xFB: std::cout << "WILL" << std::endl; break;
+            case 0xFA: std::cout << "SB" << std::endl; break;
+            case 0xF9: std::cout << "GoAhead" << std::endl; break;
+            case 0xF8: std::cout << "EraseLine" << std::endl; break;
+            case 0xF7: std::cout << "EraseCharacter" << std::endl; break;
+            case 0xF6: std::cout << "AreYouThere" << std::endl; break;
+            case 0xF5: std::cout << "AbortOutput" << std::endl; break;
+            case 0xF4: std::cout << "InterruptProcess" << std::endl; break;
+            case 0xF3: std::cout << "Break" << std::endl; break;
+            case 0xF2: std::cout << "DataMark" << std::endl; break;
+            case 0xF1: std::cout << "NOP" << std::endl; break;
+            case 0xF0: std::cout << "SE" << std::endl; break;
+            default: std::cout << (static_cast<int>( c & 0xFF )) << std::endl;;
+        }
+*/
+        switch(c)
+        {
+            case SE:
+                if (state == State::sub)
+                    state = State::data;
+                else
+                    std::cerr << "ERROR: received SE when not in sub state\n";
+                break;
+            case DataMark: // ?
+            case Break: // ?
+            case InterruptProcess:
+            case AbortOutput:
+            case AreYouThere:
+            case EraseCharacter:
+            case EraseLine:
+            case GoAhead:
+            case NOP:
+                state = State::data;
+                break;
+            case SB:
+                if (state != State::sub)
+                    state = State::sub;
+                else
+                    std::cout << "ERROR: received SB when already in sub state" << std::endl;
+                break;
+            case WILL:
+                state = State::wait_will;
+                break;
+            case WONT:
+                state = State::wait_wont;
+                break;
+            case DO:
+                state = State::wait_do;
+                break;
+            case DONT:
+                state = State::wait_dont;
+                break;
+            case IAC:
+                assert(false); // can't be here
+                state = State::data;
+                break;
+        }
+    }
+
+    void RxWill(char c)
+    { 
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "will " << static_cast<int>(c) << std::endl;
+        #endif
+        switch(c)
+        {
+            case SUPPRESS_GO_AHEAD:
+                SendIacCmd(WILL, SUPPRESS_GO_AHEAD);
+                break;
+            case NEGOTIATE_ABOUT_WIN_SIZE: 
+                SendIacCmd(DO, NEGOTIATE_ABOUT_WIN_SIZE);
+                break;
+            default:
+                SendIacCmd(DONT, c);
+        };
+    }
+    void RxWont(char c)
+    { 
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "wont " << static_cast<int>(c) << std::endl;
+        #else
+        (void)c;
+        #endif
+    }
+    void RxDo(char c)
+    { 
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "do " << static_cast<int>(c) << std::endl;
+        #endif
+        switch (c)
+        {
+            case _ECHO:
+                SendIacCmd(DO, _ECHO);
+                break;
+            case SUPPRESS_GO_AHEAD:
+                SendIacCmd(WILL, SUPPRESS_GO_AHEAD);
+                break;
+            default:
+                SendIacCmd(WONT, c);
+        };
+    }
+    void RxDont(char c)
+    {
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "dont " << static_cast<int>(c) << std::endl;
+        #else
+        (void)c;
+        #endif
+    }
+    void RxSub(char c)
+    { 
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "sub: " << static_cast<int>(c) << std::endl;
+        #else
+        (void)c;
+        #endif
+    }
+    void SendIacCmd(char action, char op)
+    {
+        std::string answer("\x0FF\x000\x000", 3);
+        answer[1] = action;
+        answer[2] = op;
+        this -> OutStream() << answer << std::flush;
+    }
+protected:
+    virtual void Output(signed char c)
+    {
+        #ifdef CLI_TELNET_TRACE
+        std::cout << "data: " << static_cast<int>(c) << std::endl;
+        #else
+        (void)c;
+        #endif
+    }
+private:
+    enum class State { data, sub, wait_will, wait_wont, wait_do, wait_dont };
+    State state = State::data;
+    bool escape = false;
+
+#endif
+
+private:
+    void Feed(char c)
+    {
+        if (std::isprint(c)) std::cout << c << std::endl;
+        else std::cout << "0x" << std::hex << static_cast<int>(c) << std::dec << std::endl;
+    }
+    std::string buffer;
+};
+
+template <typename ASIOLIB>
+class TelnetServer : public Server<ASIOLIB>
+{
+public:
+    TelnetServer(typename ASIOLIB::ContextType& ios, unsigned short port) :
+        Server<ASIOLIB>(ios, port)
+    {}
+    std::shared_ptr<Session> CreateSession(asiolib::ip::tcp::socket _socket) override
+    {
+        return std::make_shared<TelnetSession>(std::move(_socket));
+    }
+};
+
+//////////////
+
+class CliTelnetSession : public InputDevice, public TelnetSession, public CliSession
+{
+public:
+
+    CliTelnetSession(Scheduler& _scheduler, asiolib::ip::tcp::socket _socket, Cli& _cli, const std::function< void(std::ostream&)>& _exitAction, std::size_t historySize ) :
+        InputDevice(_scheduler),
+        TelnetSession(std::move(_socket)),
+        CliSession(_cli, TelnetSession::OutStream(), historySize),
+        poll(*this, *this)
+    {
+        ExitAction([this, _exitAction](std::ostream& _out){ _exitAction(_out), Disconnect(); } );
+    }
+protected:
+
+    void OnConnect() override
+    {
+        TelnetSession::OnConnect();
+        Enter();
+        Prompt();
+    }
+
+    void Output(signed char c) override // NB: C++ does not specify wether char is signed or unsigned
+    {
+        switch(step)
+        {
+            case Step::_1:
+                switch( c )
+                {
+                    case EOF:
+                    case 4:  // EOT
+                        Notify(std::make_pair(KeyType::eof,' ')); break;
+                    case 8: // Backspace
+                    case 127:  // Backspace or Delete
+                        Notify(std::make_pair(KeyType::backspace, ' ')); break;
+                    //case 10: Notify(std::make_pair(KeyType::ret,' ')); break;
+                    case 27: step = Step::_2; break;  // symbol
+                    case 13: step = Step::wait_0; break;  // wait for 0 (ENTER key)
+                    default: // ascii
+                    {
+                        const char ch = static_cast<char>(c);
+                        Notify(std::make_pair(KeyType::ascii,ch));
+                    }
+                }
+                break;
+
+            case Step::_2: // got 27 last time
+                if ( c == 91 )
+                {
+                    step = Step::_3;
+                    break;  // arrow keys
+                }
+                else
+                {
+                    step = Step::_1;
+                    Notify(std::make_pair(KeyType::ignored,' '));
+                    break; // unknown
+                }
+                break;
+
+            case Step::_3: // got 27 and 91
+                switch( c )
+                {
+                    case 65: step = Step::_1; Notify(std::make_pair(KeyType::up,' ')); break;
+                    case 66: step = Step::_1; Notify(std::make_pair(KeyType::down,' ')); break;
+                    case 68: step = Step::_1; Notify(std::make_pair(KeyType::left,' ')); break;
+                    case 67: step = Step::_1; Notify(std::make_pair(KeyType::right,' ')); break;
+                    case 70: step = Step::_1; Notify(std::make_pair(KeyType::end,' ')); break;
+                    case 72: step = Step::_1; Notify(std::make_pair(KeyType::home,' ')); break;
+                    default: step = Step::_4; break;  // not arrow keys
+                }
+                break;
+
+            case Step::_4:
+                if ( c == 126 ) Notify(std::make_pair(KeyType::canc,' '));
+                else Notify(std::make_pair(KeyType::ignored,' '));
+
+                step = Step::_1;
+
+                break;
+
+            case Step::wait_0:
+                if ( c == 0 /* linux */ || c == 10 /* win */ ) Notify(std::make_pair(KeyType::ret,' '));
+                else Notify(std::make_pair(KeyType::ignored,' '));
+
+                step = Step::_1;
+
+                break;
+
+        }
+    }
+
+private:
+
+    enum class Step { _1, _2, _3, _4, wait_0 };
+    Step step = Step::_1;
+    InputHandler poll;
+};
+
+template <typename ASIOLIB>
+class CliGenericTelnetServer : public Server<ASIOLIB>
+{
+public:
+    CliGenericTelnetServer(Cli& _cli, GenericAsioScheduler<ASIOLIB>& _scheduler, unsigned short port, std::size_t _historySize=100 ) :
+        Server<ASIOLIB>(_scheduler.AsioContext(), port),
+        scheduler(_scheduler),
+        cli(_cli),
+        historySize(_historySize)
+    {}
+    CliGenericTelnetServer(Cli& _cli, GenericAsioScheduler<ASIOLIB>& _scheduler, std::string address, unsigned short port, std::size_t _historySize=100 ) :
+        Server<ASIOLIB>(_scheduler.AsioContext(), address, port),
+        scheduler(_scheduler),
+        cli(_cli),
+        historySize(_historySize)
+    {}
+
+    void EnterAction(std::function< void(std::ostream&)> action)
+    {
+        enterAction = action;
+    }
+
+    void ExitAction( std::function< void(std::ostream&)> action )
+    {
+        exitAction = action;
+    }
+    std::shared_ptr<Session> CreateSession(asiolib::ip::tcp::socket _socket) override
+    {
+        return std::make_shared<CliTelnetSession>(scheduler, std::move(_socket), cli, exitAction, historySize);
+    }
+private:
+    Scheduler& scheduler;
+    Cli& cli;
+    std::function< void(std::ostream&)> enterAction;
+    std::function< void(std::ostream&)> exitAction;
+    std::size_t historySize;
+};
+
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_GENERICASIOREMOTECLI_H_
+
diff --git a/external/cli/detail/genericasioscheduler.h b/external/cli/detail/genericasioscheduler.h
new file mode 100644 (file)
index 0000000..7fb2979
--- /dev/null
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_GENERICASIOSCHEDULER_H_
+#define CLI_DETAIL_GENERICASIOSCHEDULER_H_
+
+#include "../scheduler.h"
+#include <memory> // unique_ptr
+
+namespace cli
+{
+namespace detail
+{
+
+template <typename ASIOLIB>
+class GenericAsioScheduler : public Scheduler
+{
+public:
+
+    using ContextType = typename ASIOLIB::ContextType;
+    using WorkGuard = typename ASIOLIB::WorkGuard;
+
+    GenericAsioScheduler() :
+        owned{true},
+        context{new ContextType()},
+        executor{*context},
+        work{std::make_unique<WorkGuard>(ASIOLIB::MakeWorkGuard(*context))}
+    {}
+
+    explicit GenericAsioScheduler(ContextType& _context) : context{&_context}, executor{*context} {}
+
+    ~GenericAsioScheduler() override
+    {
+        if (owned)
+        {
+            work.reset(); // work uses context, so it must be deleted before context
+            delete context;
+        }
+    }
+
+    // non copyable
+    GenericAsioScheduler(const GenericAsioScheduler&) = delete;
+    GenericAsioScheduler& operator=(const GenericAsioScheduler&) = delete;
+
+    void Stop()
+    {
+        if (work)
+            ASIOLIB::Reset(*work);
+        context->stop();
+    }
+
+    void Run()
+    {
+        context->run();
+    }
+
+    bool Stopped() const
+    {
+        return context->stopped();
+    }
+
+    void ExecOne() { context->run_one(); }
+
+    void PollOne() { context->poll_one(); }
+
+    void Post(const std::function<void()>& f) override
+    {
+        executor.Post(f);
+    }
+
+    ContextType& AsioContext() { return *context; }
+
+private:
+
+    using ExecutorType = typename ASIOLIB::Executor;
+
+    bool owned = false;
+    ContextType* context;
+    ExecutorType executor;
+    std::unique_ptr<WorkGuard> work;
+};
+
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_GENERICASIOSCHEDULER_H_
diff --git a/external/cli/detail/genericcliasyncsession.h b/external/cli/detail/genericcliasyncsession.h
new file mode 100644 (file)
index 0000000..2712276
--- /dev/null
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_GENERICCLIASYNCSESSION_H_
+#define CLI_DETAIL_GENERICCLIASYNCSESSION_H_
+
+#include <string>
+#include "../cli.h" // CliSession
+#include "genericasioscheduler.h"
+
+namespace cli
+{
+namespace detail
+{
+
+template <typename ASIOLIB>
+class GenericCliAsyncSession : public CliSession
+{
+public:
+    GenericCliAsyncSession(GenericAsioScheduler<ASIOLIB>& _scheduler, Cli& _cli) :
+        CliSession(_cli, std::cout, 1),
+        input(_scheduler.AsioContext(), ::dup(STDIN_FILENO))
+    {
+        Read();
+    }
+    ~GenericCliAsyncSession() noexcept override
+    {
+        try { input.close(); } catch (const std::exception&) { /* do nothing */ }
+    }
+
+private:
+
+    void Read()
+    {
+        Prompt();
+        // Read a line of input entered by the user.
+        asiolib::async_read_until(
+            input,
+            inputBuffer,
+            '\n',
+            std::bind( &GenericCliAsyncSession::NewLine, this,
+                       std::placeholders::_1,
+                       std::placeholders::_2 )
+        );
+    }
+
+    void NewLine(const asiolibec::error_code& error, std::size_t length )
+    {
+        if ( !error || error == asiolib::error::not_found )
+        {
+            auto bufs = inputBuffer.data();
+            auto size = static_cast<long>(length);
+            if ( !error ) --size; // remove \n
+            std::string s(asiolib::buffers_begin( bufs ), asiolib::buffers_begin( bufs ) + size);
+            inputBuffer.consume( length );
+
+            Feed( s );
+            Read();
+        }
+        else
+        {
+            input.close();
+        }
+    }
+
+    asiolib::streambuf inputBuffer;
+    asiolib::posix::stream_descriptor input;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_GENERICCLIASYNCSESSION_H_
+
diff --git a/external/cli/detail/history.h b/external/cli/detail/history.h
new file mode 100644 (file)
index 0000000..528109b
--- /dev/null
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_HISTORY_H_
+#define CLI_DETAIL_HISTORY_H_
+
+#include <deque>
+#include <limits>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <cassert>
+
+namespace cli
+{
+namespace detail
+{
+
+class History
+{
+public:
+
+    explicit History(std::size_t size) : maxSize(size) {}
+
+    // Insert a new item in the buffer, changing the current state to "inserting"
+    // If we're browsing the history (eg with arrow keys) the new item overwrites
+    // the current one.
+    // Otherwise, the item is added to the front of the container
+    void NewCommand(const std::string& item)
+    {
+        ++commands;
+        current = 0;
+        if (mode == Mode::browsing)
+        {
+            assert(!buffer.empty());
+            if (buffer.size() > 1 && buffer[1] == item) // try to insert an element identical to last one
+                buffer.pop_front();
+            else // the item was not identical
+                buffer[current] = item;
+        }
+        else // Mode::inserting
+        {
+            if (buffer.empty() || buffer[0] != item) // insert an element not equal to last one
+                Insert(item);
+        }
+        mode = Mode::inserting;
+    }
+
+    // Return the previous item of the history, updating the current item and
+    // changing the current state to "browsing"
+    // If we're already browsing the history (eg with arrow keys) the edit line is inserted
+    // to the front of the container.
+    // Otherwise, the line overwrites the current item.
+    std::string Previous(const std::string& line)
+    {
+        if (mode == Mode::inserting)
+        {
+            Insert(line);
+            mode = Mode::browsing;
+            current = (buffer.size() > 1) ? 1 : 0;
+        }
+        else // Mode::browsing
+        {
+            assert(!buffer.empty());
+            buffer[current] = line;
+            if (current != buffer.size()-1)
+                ++current;
+        }
+        assert(mode == Mode::browsing);
+        assert(current < buffer.size());
+        return buffer[current];
+    }
+
+    // Return the next item of the history, updating the current item.
+    std::string Next()
+    {
+        if (buffer.empty() || current == 0)
+            return {};
+        assert(current != 0);
+        --current;
+        assert(current < buffer.size());
+        return buffer[current];
+    }
+
+    // Show the whole history on the given ostream
+    void Show(std::ostream& out) const
+    {
+        out << '\n';
+        for (auto& item: buffer)
+            out << item << '\n';
+        out << '\n' << std::flush;
+    }
+
+    // cmds[0] is the oldest command, cmds[size-1] the newer
+    void LoadCommands(const std::vector<std::string>& cmds)
+    {
+        for (const auto& c: cmds)
+            Insert(c);
+    }
+
+    // result[0] is the oldest command, result[size-1] the newer
+    std::vector<std::string> GetCommands() const
+    {
+        auto numCmdsToReturn = std::min(commands, buffer.size());
+        auto start = buffer.begin();
+        if (mode == Mode::browsing)
+        {
+            numCmdsToReturn = std::min(commands, buffer.size()-1);
+            start = buffer.begin()+1;
+        }
+        std::vector<std::string> result(numCmdsToReturn);
+        assert(std::distance(start, buffer.end()) >= 0);
+        assert(numCmdsToReturn <= static_cast<std::size_t>(std::distance(buffer.end(), start)));
+        assert(numCmdsToReturn <= static_cast<unsigned long>(std::numeric_limits<long>::max()));
+        std::reverse_copy(start, start+static_cast<long>(numCmdsToReturn), result.begin());
+        return result;
+    }
+
+private:
+
+    void Insert(const std::string& item)
+    {
+        buffer.push_front(item);
+        if (buffer.size() > maxSize)
+            buffer.pop_back();
+    }
+
+    const std::size_t maxSize;
+    std::deque<std::string> buffer;
+    std::size_t current = 0;
+    std::size_t commands = 0; // number of commands issued
+    enum class Mode { inserting, browsing };
+    Mode mode = Mode::inserting;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_HISTORY_H_
diff --git a/external/cli/detail/inputdevice.h b/external/cli/detail/inputdevice.h
new file mode 100644 (file)
index 0000000..5655ed8
--- /dev/null
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_INPUTDEVICE_H_
+#define CLI_DETAIL_INPUTDEVICE_H_
+
+#include <functional>
+#include <string>
+#include "../scheduler.h"
+
+namespace cli
+{
+namespace detail
+{
+
+enum class KeyType { ascii, up, down, left, right, backspace, canc, home, end, ret, eof, ignored };
+
+class InputDevice
+{
+public:
+    using Handler = std::function< void( std::pair<KeyType,char> ) >;
+
+    explicit InputDevice(Scheduler& _scheduler) : scheduler(_scheduler) {}
+    virtual ~InputDevice() = default;
+
+    template <typename H>
+    void Register(H&& h) { handler = std::forward<H>(h); }
+
+protected:
+
+    void Notify(std::pair<KeyType,char> k)
+    {
+        scheduler.Post([this,k](){ if (handler) handler(k); });
+    }
+
+private:
+
+    Scheduler& scheduler;
+    Handler handler;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_INPUTDEVICE_H_
+
diff --git a/external/cli/detail/inputhandler.h b/external/cli/detail/inputhandler.h
new file mode 100644 (file)
index 0000000..ac9170e
--- /dev/null
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_INPUTHANDLER_H_
+#define CLI_DETAIL_INPUTHANDLER_H_
+
+#include <functional>
+#include <string>
+#include "terminal.h"
+#include "inputdevice.h"
+#include "../cli.h" // CliSession
+#include "commonprefix.h"
+
+namespace cli
+{
+namespace detail
+{
+
+class InputHandler
+{
+public:
+    InputHandler(CliSession& _session, InputDevice& kb) :
+        session(_session),
+        terminal(session.OutStream())
+    {
+        kb.Register( [this](auto key){ this->Keypressed(key); } );
+    }
+
+private:
+
+    void Keypressed(std::pair<KeyType, char> k)
+    {
+        const std::pair<Symbol,std::string> s = terminal.Keypressed(k);
+        NewCommand(s);
+    }
+
+    void NewCommand(const std::pair<Symbol, std::string>& s)
+    {
+        switch (s.first)
+        {
+            case Symbol::nothing:
+            {
+                break;
+            }
+            case Symbol::eof:
+            {
+                session.Exit();
+                break;
+            }
+            case Symbol::command:
+            {
+                session.Feed(s.second);
+                session.Prompt();
+                break;
+            }
+            case Symbol::down:
+            {
+                terminal.SetLine(session.NextCmd());
+                break;
+            }
+            case Symbol::up:
+            {
+                auto line = terminal.GetLine();
+                terminal.SetLine(session.PreviousCmd(line));
+                break;
+            }
+            case Symbol::tab:
+            {
+                auto line = terminal.GetLine();
+                auto completions = session.GetCompletions(line);
+
+                if (completions.empty())
+                    break;
+                if (completions.size() == 1)
+                {
+                    terminal.SetLine(completions[0]+' ');
+                    break;
+                }
+
+                auto commonPrefix = CommonPrefix(completions);
+                if (commonPrefix.size() > line.size())
+                {
+                    terminal.SetLine(commonPrefix);
+                    break;
+                }
+                session.OutStream() << '\n';
+                std::string items;
+                std::for_each( completions.begin(), completions.end(), [&items](auto& cmd){ items += '\t' + cmd; } );
+                session.OutStream() << items << '\n';
+                session.Prompt();
+                terminal.ResetCursor();
+                terminal.SetLine( line );
+                break;
+            }
+        }
+
+    }
+
+    CliSession& session;
+    Terminal terminal;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_INPUTHANDLER_H_
+
diff --git a/external/cli/detail/keyboard.h b/external/cli/detail/keyboard.h
new file mode 100644 (file)
index 0000000..e300d25
--- /dev/null
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_KEYBOARD_H_
+#define CLI_DETAIL_KEYBOARD_H_
+
+#if defined(__unix__) || defined(__unix) || defined(__linux__)
+    #define OS_LINUX
+#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
+    #define OS_WIN
+#elif defined(__APPLE__) || defined(__MACH__)
+    #define OS_MAC
+#else
+    #error "Platform not supported (yet)."
+#endif
+
+#if defined(OS_LINUX) || defined(OS_MAC)
+    #include "linuxkeyboard.h"
+    namespace cli { namespace detail { using Keyboard = LinuxKeyboard; } }
+#elif defined(OS_WIN)
+    #include "winkeyboard.h"
+    namespace cli { namespace detail { using Keyboard = WinKeyboard; } }
+#else
+    #error "Platform not supported (yet)."
+#endif
+
+#undef OS_LINUX
+#undef OS_WIN
+#undef OS_MAC
+
+#endif // CLI_DETAIL_KEYBOARD_H_
+
diff --git a/external/cli/detail/linuxkeyboard.h b/external/cli/detail/linuxkeyboard.h
new file mode 100644 (file)
index 0000000..72ec901
--- /dev/null
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_LINUXKEYBOARD_H_
+#define CLI_DETAIL_LINUXKEYBOARD_H_
+
+#include <thread>
+#include <memory>
+
+#include <cstdio>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <cassert>
+
+#include "inputdevice.h"
+
+
+namespace cli
+{
+namespace detail
+{
+
+class InputSource
+{
+public:
+
+    InputSource()
+    {
+        int pipes[2];
+        if (pipe(pipes) == 0)
+        {
+            shutdownPipe = pipes[1]; // we store the write end
+            readPipe = pipes[0]; // ... and the read end
+        }
+    }
+
+    void WaitKbHit()
+    {
+        fd_set rfds;
+        FD_ZERO(&rfds);
+        FD_SET(STDIN_FILENO, &rfds);
+        FD_SET(readPipe, &rfds);
+
+        while (select(readPipe + 1, &rfds, nullptr, nullptr, nullptr) == 0);
+
+        if (FD_ISSET(readPipe, &rfds)) // stop called
+        {
+            close(readPipe);
+            throw std::runtime_error("InputSource stop");
+        }
+
+        if (FD_ISSET(STDIN_FILENO, &rfds)) // char from stdinput
+        {
+            return;
+        }
+
+        // cannot reach this point
+        assert(false);
+    }
+
+    void Stop()
+    {
+        auto unused = write(shutdownPipe, " ", 1);
+        unused = close(shutdownPipe);
+        static_cast<void>(unused); // silence unused warn
+        shutdownPipe = -1;
+    }
+
+private:
+    int shutdownPipe;
+    int readPipe;
+};
+
+//
+
+
+class LinuxKeyboard : public InputDevice
+{
+public:
+    explicit LinuxKeyboard(Scheduler& _scheduler) :
+        InputDevice(_scheduler),
+        servant( [this]() noexcept { Read(); } )
+    {
+        ToManualMode();
+    }
+    ~LinuxKeyboard() override
+    {
+        ToStandardMode();
+        is.Stop();
+        servant.join();
+    }
+
+private:
+
+    void Read() noexcept
+    {
+        try
+        {
+            while (true)
+            {
+                auto k = Get();
+                Notify(k);
+            }
+        }
+        catch(const std::exception&)
+        {
+            // nothing to do: just exit
+        }
+    }
+
+    char GetChar()
+    {
+        char buffer = 0;
+        auto unused = read(0, &buffer, 1);
+        static_cast<void>(unused); // silence unused warn
+        return buffer;
+    }
+
+    std::pair<KeyType,char> Get()
+    {
+        is.WaitKbHit();
+
+        auto ch = GetChar();
+        switch(ch)
+        {
+            case EOF:
+            case 4:  // EOT
+                return std::make_pair(KeyType::eof,' ');
+                break;
+            case 127:
+            case 8:
+                return std::make_pair(KeyType::backspace,' '); break;
+            case 10: return std::make_pair(KeyType::ret,' '); break;
+            case 27: // symbol
+                ch = GetChar();
+                if ( ch == 91 ) // arrow keys
+                {
+                    ch = GetChar();
+                    switch( ch )
+                    {
+                        case 51:
+                            ch = GetChar();
+                            if ( ch == 126 ) return std::make_pair(KeyType::canc,' ');
+                            else return std::make_pair(KeyType::ignored,' ');
+                            break;
+                        case 65: return std::make_pair(KeyType::up,' ');
+                        case 66: return std::make_pair(KeyType::down,' ');
+                        case 68: return std::make_pair(KeyType::left,' ');
+                        case 67: return std::make_pair(KeyType::right,' ');
+                        case 70: return std::make_pair(KeyType::end,' ');
+                        case 72: return std::make_pair(KeyType::home,' ');
+                        default: return std::make_pair(KeyType::ignored,' ');
+                    }
+                }
+                break;
+            default: // ascii
+            {
+                const char c = static_cast<char>(ch);
+                return std::make_pair(KeyType::ascii,c);
+            }
+        }
+        return std::make_pair(KeyType::ignored,' ');
+    }
+
+    void ToManualMode()
+    {
+        constexpr tcflag_t ICANON_FLAG = ICANON;
+        constexpr tcflag_t ECHO_FLAG = ECHO;
+
+        tcgetattr(STDIN_FILENO, &oldt);
+        newt = oldt;
+        newt.c_lflag &= ~( ICANON_FLAG | ECHO_FLAG );
+        tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+    }
+
+    void ToStandardMode()
+    {
+        tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+    }
+
+    termios oldt;
+    termios newt;
+    InputSource is;
+    std::thread servant;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_LINUXKEYBOARD_H_
+
diff --git a/external/cli/detail/newboostasiolib.h b/external/cli/detail/newboostasiolib.h
new file mode 100644 (file)
index 0000000..2d7b4e6
--- /dev/null
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_NEWBOOSTASIOLIB_H_
+#define CLI_DETAIL_NEWBOOSTASIOLIB_H_
+
+#include <boost/version.hpp>
+#include <boost/asio.hpp>
+
+namespace cli
+{
+namespace detail
+{
+
+namespace asiolib = boost::asio;
+namespace asiolibec = boost::system;
+
+class NewBoostAsioLib
+{
+public:
+
+    using ContextType = boost::asio::io_context;
+    using WorkGuard = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
+
+    class Executor
+    {
+    public:
+        explicit Executor(ContextType& ios) :
+            executor(ios.get_executor()) {}
+        explicit Executor(boost::asio::ip::tcp::socket& socket) :
+            executor(socket.get_executor()) {}
+        template <typename T> void Post(T&& t) { boost::asio::post(executor, std::forward<T>(t)); }
+    private:
+#if BOOST_VERSION >= 107400
+    using AsioExecutor = boost::asio::any_io_executor;
+#else
+    using AsioExecutor = boost::asio::executor;
+#endif
+         AsioExecutor executor;
+    };
+
+    static boost::asio::ip::address IpAddressFromString(const std::string& address)
+    {
+        return boost::asio::ip::make_address(address);
+    }
+
+    static auto MakeWorkGuard(ContextType& context)
+    {
+        return boost::asio::make_work_guard(context);
+    }
+
+    static void Reset(WorkGuard& wg)
+    {
+        wg.reset();
+    }
+
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_NEWBOOSTASIOLIB_H_
+
diff --git a/external/cli/detail/newstandaloneasiolib.h b/external/cli/detail/newstandaloneasiolib.h
new file mode 100644 (file)
index 0000000..54d5aa7
--- /dev/null
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_NEWSTANDALONEASIOLIB_H_
+#define CLI_DETAIL_NEWSTANDALONEASIOLIB_H_
+
+#include <asio/version.hpp>
+#include <asio.hpp>
+
+namespace cli
+{
+namespace detail
+{
+
+namespace asiolib = asio;
+namespace asiolibec = asio;
+
+class NewStandaloneAsioLib
+{
+public:
+
+    using ContextType = asio::io_context;
+    using WorkGuard = asio::executor_work_guard<asio::io_context::executor_type>;
+
+    class Executor
+    {
+    public:
+        explicit Executor(ContextType& ios) :
+            executor(ios.get_executor()) {}
+        explicit Executor(asio::ip::tcp::socket& socket) :
+            executor(socket.get_executor()) {}
+        template <typename T> void Post(T&& t) { asio::post(executor, std::forward<T>(t)); }
+    private:
+#if ASIO_VERSION >= 101700
+    using AsioExecutor = asio::any_io_executor;
+#else
+    using AsioExecutor = asio::executor;
+#endif
+         AsioExecutor executor;
+    };
+
+    static asio::ip::address IpAddressFromString(const std::string& address)
+    {
+        return asio::ip::make_address(address);
+    }
+
+    static auto MakeWorkGuard(ContextType& context)
+    {
+        return asio::make_work_guard(context);
+    }
+
+    static void Reset(WorkGuard& wg)
+    {
+        wg.reset();
+    }
+
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_NEWSTANDALONEASIOLIB_H_
+
diff --git a/external/cli/detail/oldboostasiolib.h b/external/cli/detail/oldboostasiolib.h
new file mode 100644 (file)
index 0000000..a24173d
--- /dev/null
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_OLDBOOSTASIOLIB_H_
+#define CLI_DETAIL_OLDBOOSTASIOLIB_H_
+
+#include <boost/asio.hpp>
+
+namespace cli
+{
+namespace detail
+{
+
+namespace asiolib = boost::asio;
+namespace asiolibec = boost::system;
+
+class OldBoostAsioLib
+{
+public:
+    using ContextType = boost::asio::io_service;
+    using WorkGuard = boost::asio::io_service::work;
+
+    class Executor
+    {
+    public:
+        explicit Executor(ContextType& _ios) :
+            ios(_ios) {}
+        explicit Executor(boost::asio::ip::tcp::socket& socket) :
+            ios(socket.get_io_service()) {}
+        template <typename T> void Post(T&& t) { ios.post(std::forward<T>(t)); }
+    private:
+        ContextType& ios;
+    };
+
+    static boost::asio::ip::address IpAddressFromString(const std::string& address)
+    {
+        return boost::asio::ip::address::from_string(address);
+    }
+
+    static auto MakeWorkGuard(ContextType& context)
+    {
+        boost::asio::io_service::work work(context);
+        return work;
+    }
+
+    static void Reset(WorkGuard& /*wg*/)
+    {
+    }
+
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_OLDBOOSTASIOLIB_H_
+
diff --git a/external/cli/detail/oldstandaloneasiolib.h b/external/cli/detail/oldstandaloneasiolib.h
new file mode 100644 (file)
index 0000000..6c582e6
--- /dev/null
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_OLDSTANDALONEASIOLIB_H_
+#define CLI_DETAIL_OLDSTANDALONEASIOLIB_H_
+
+#define ASIO_STANDALONE 1
+
+#include <asio.hpp>
+
+namespace cli
+{
+namespace detail
+{
+
+namespace asiolib = asio;
+namespace asiolibec = asio;
+
+class OldStandaloneAsioLib
+{
+public:
+
+    using ContextType = asio::io_service;
+    using WorkGuard = asio::io_service::work;
+
+    class Executor
+    {
+    public:
+        explicit Executor(ContextType& _ios) :
+            ios(_ios) {}
+        explicit Executor(asio::ip::tcp::socket& socket) :
+            ios(socket.get_io_service()) {}
+        template <typename T> void Post(T&& t) { ios.post(std::forward<T>(t)); }
+    private:
+        ContextType& ios;
+    };
+
+    static asio::ip::address IpAddressFromString(const std::string& address)
+    {
+        return asio::ip::address::from_string(address);
+    }
+
+    static auto MakeWorkGuard(ContextType& context)
+    {
+        asio::io_service::work work(context);
+        return work;
+    }
+
+    static void Reset(WorkGuard& /*wg*/)
+    {
+    }
+
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_OLDSTANDALONEASIOLIB_H_
+
diff --git a/external/cli/detail/rang.h b/external/cli/detail/rang.h
new file mode 100644 (file)
index 0000000..79b5ae6
--- /dev/null
@@ -0,0 +1,303 @@
+#ifndef CLI_DETAIL_RANG_H
+#define CLI_DETAIL_RANG_H
+
+#if defined(__unix__) || defined(__unix) || defined(__linux__)
+#define OS_LINUX
+#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
+#define OS_WIN
+#elif defined(__APPLE__) || defined(__MACH__)
+#define OS_MAC
+#else
+#error Unknown Platform
+#endif
+
+#if defined(OS_LINUX) || defined(OS_MAC)
+    #include <unistd.h>
+    #include <cstring>
+#elif defined(OS_WIN)
+    #if !defined(NOMINMAX)
+        #define NOMINMAX 1
+    #endif // !defined(NOMINMAX)
+    #include <windows.h>
+    #include <io.h>
+    #include <VersionHelpers.h>
+#endif
+
+#include <algorithm>
+#include <cstdlib>
+#include <ios>
+#include <iostream>
+#include <iterator>
+#include <type_traits>
+
+namespace cli {
+namespace detail {
+namespace rang {
+
+enum class style {
+    reset     = 0,
+    bold      = 1,
+    dim       = 2,
+    italic    = 3,
+    underline = 4,
+    blink     = 5,
+    rblink    = 6,
+    reversed  = 7,
+    conceal   = 8,
+    crossed   = 9
+};
+
+enum class fg {
+    black   = 30,
+    red     = 31,
+    green   = 32,
+    yellow  = 33,
+    blue    = 34,
+    magenta = 35,
+    cyan    = 36,
+    gray    = 37,
+    reset   = 39
+};
+
+enum class bg {
+    black   = 40,
+    red     = 41,
+    green   = 42,
+    yellow  = 43,
+    blue    = 44,
+    magenta = 45,
+    cyan    = 46,
+    gray    = 47,
+    reset   = 49
+};
+
+enum class fgB {
+    black   = 90,
+    red     = 91,
+    green   = 92,
+    yellow  = 93,
+    blue    = 94,
+    magenta = 95,
+    cyan    = 96,
+    gray    = 97
+};
+
+enum class bgB {
+    black   = 100,
+    red     = 101,
+    green   = 102,
+    yellow  = 103,
+    blue    = 104,
+    magenta = 105,
+    cyan    = 106,
+    gray    = 107
+};
+
+enum class control { autoColor = 0, forceColor = 1 };
+
+
+namespace rang_implementation {
+
+    inline std::streambuf const *&RANG_coutbuf()
+    {
+        static std::streambuf const *pOutbuff = std::cout.rdbuf();
+        return pOutbuff;
+    }
+
+    inline std::streambuf const *&RANG_cerrbuf()
+    {
+        static std::streambuf const *pErrbuff = std::cerr.rdbuf();
+        return pErrbuff;
+    }
+
+    inline std::streambuf const *&RANG_clogbuf()
+    {
+        static std::streambuf const *pLogbuff = std::clog.rdbuf();
+        return pLogbuff;
+    }
+
+    inline int getIword()
+    {
+        static int i = std::ios_base::xalloc();
+        return i;
+    }
+
+
+    inline bool supportsColor()
+    {
+#if defined(OS_LINUX) || defined(OS_MAC)
+        static constexpr const char* Terms[] = {
+            "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
+            "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
+        };
+
+        const char *env_p = std::getenv("TERM");
+        if (env_p == nullptr) {
+            return false;
+        }
+
+        static const bool result = std::any_of(
+          std::begin(Terms), std::end(Terms), [&](const char* term) {
+              return std::strstr(env_p, term) != nullptr;
+          });
+
+#elif defined(OS_WIN)
+        static constexpr bool result = true;
+#endif
+        return result;
+    }
+
+
+    inline bool isTerminal(const std::streambuf *osbuf)
+    {
+        if (osbuf == RANG_coutbuf()) {
+#if defined(OS_LINUX) || defined(OS_MAC)
+            return isatty(fileno(stdout)) ? true : false;
+#elif defined(OS_WIN)
+            return _isatty(_fileno(stdout)) ? true : false;
+#endif
+        }
+
+        if (osbuf == RANG_cerrbuf() || osbuf == RANG_clogbuf()) {
+#if defined(OS_LINUX) || defined(OS_MAC)
+            return isatty(fileno(stderr)) ? true : false;
+#elif defined(OS_WIN)
+            return _isatty(_fileno(stderr)) ? true : false;
+#endif
+        }
+        return false;
+    }
+
+
+    template <typename T>
+    using enableStd =
+      typename std::enable_if<std::is_same<T, rang::style>::value
+          || std::is_same<T, rang::fg>::value
+          || std::is_same<T, rang::bg>::value
+          || std::is_same<T, rang::fgB>::value
+          || std::is_same<T, rang::bgB>::value,
+        std::ostream &>::type;
+
+
+#ifdef OS_WIN
+    inline HANDLE getVersionDependentHandle()
+    {
+        if (IsWindowsVersionOrGreater(10, 0, 0)) return nullptr;
+        return GetStdHandle(STD_OUTPUT_HANDLE);
+    }
+
+    inline HANDLE getConsoleHandle()
+    {
+        static HANDLE h = getVersionDependentHandle();
+        return h;
+    }
+
+    inline WORD reverseRGB(WORD rgb)
+    {
+        static const WORD rev[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+        return rev[rgb];
+    }
+
+    inline void setWinAttribute(rang::bg col, WORD &state)
+    {
+        state &= 0xFF0F;
+        state |= reverseRGB(static_cast<WORD>(col) - 40) << 4;
+    }
+
+    inline void setWinAttribute(rang::fg col, WORD &state)
+    {
+        state &= 0xFFF0;
+        state |= reverseRGB(static_cast<WORD>(col) - 30);
+    }
+
+    inline void setWinAttribute(rang::bgB col, WORD &state)
+    {
+        state &= 0xFF0F;
+        state |= (0x8 | reverseRGB(static_cast<WORD>(col) - 100)) << 4;
+    }
+
+    inline void setWinAttribute(rang::fgB col, WORD &state)
+    {
+        state &= 0xFFF0;
+        state |= (0x8 | reverseRGB(static_cast<WORD>(col) - 90));
+    }
+
+    inline void setWinAttribute(rang::style style, WORD &state)
+    {
+        if (style == rang::style::reset) {
+            state = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
+        }
+    }
+
+    inline WORD &current_state()
+    {
+        static WORD state
+          = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
+        return state;
+    }
+
+    template <typename T>
+    inline enableStd<T> setColor(std::ostream &os, T const value)
+    {
+        HANDLE h = getConsoleHandle();
+        if (h && isTerminal(os.rdbuf())) {
+            setWinAttribute(value, current_state());
+            SetConsoleTextAttribute(h, current_state());
+            return os;
+        }
+        return os << "\033[" << static_cast<int>(value) << "m";
+    }
+#else
+    template <typename T>
+    inline enableStd<T> setColor(std::ostream &os, T const value)
+    {
+        return os << "\033[" << static_cast<int>(value) << "m";
+    }
+#endif
+
+    template <typename T>
+    using enableControl =
+      typename std::enable_if<std::is_same<T, rang::control>::value,
+        std::ostream &>::type;
+} // namespace rang_implementation
+
+inline void init()
+{
+    rang_implementation::RANG_coutbuf();
+    rang_implementation::RANG_cerrbuf();
+    rang_implementation::RANG_clogbuf();
+}
+
+template <typename T>
+inline rang_implementation::enableStd<T> operator<<(
+  std::ostream &os, T const value)
+{
+    std::streambuf const *osbuf = os.rdbuf();
+    return (os.iword(rang_implementation::getIword())
+             || ((rang_implementation::supportsColor())
+             && (rang_implementation::isTerminal(osbuf))))
+      ? rang_implementation::setColor(os, value)
+      : os;
+}
+
+template <typename T>
+inline rang_implementation::enableControl<T> operator<<(
+  std::ostream &os, T const value)
+{
+    if (value == rang::control::forceColor) {
+        os.iword(rang_implementation::getIword()) = 1;
+    } else if (value == rang::control::autoColor) {
+        os.iword(rang_implementation::getIword()) = 0;
+    }
+    return os;
+}
+
+} // namespace rang
+} // namespace detail
+} // namespace cli
+
+#undef OS_LINUX
+#undef OS_WIN
+#undef OS_MAC
+
+#endif // CLI_DETAIL_RANG_H
diff --git a/external/cli/detail/server.h b/external/cli/detail/server.h
new file mode 100644 (file)
index 0000000..8013057
--- /dev/null
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_SERVER_H_
+#define CLI_DETAIL_SERVER_H_
+
+#include <memory>
+#include <queue>
+
+namespace cli
+{
+namespace detail
+{
+
+class Session : public std::enable_shared_from_this<Session>, public std::streambuf
+{
+public:
+    ~Session() override = default;
+    virtual void Start()
+    {
+        OnConnect();
+        Read();
+    }
+
+protected:
+
+    explicit Session(asiolib::ip::tcp::socket _socket) : socket(std::move(_socket)), outStream( this ) {}
+
+    virtual void Disconnect()
+    {
+        socket.shutdown(asiolib::ip::tcp::socket::shutdown_both);
+        socket.close();
+    }
+
+    virtual void Read()
+    {
+      auto self( shared_from_this() );
+      socket.async_read_some(asiolib::buffer( data, max_length ),
+          [ this, self ]( asiolibec::error_code ec, std::size_t length )
+          {
+              if ( !socket.is_open() || ( ec == asiolib::error::eof ) || ( ec == asiolib::error::connection_reset ) )
+                  OnDisconnect();
+              else if ( ec )
+                  OnError();
+              else
+              {
+                  OnDataReceived( std::string( data, length ));
+                  Read();
+              }
+          });
+    }
+
+    virtual void Send(const std::string& msg)
+    {
+        asiolibec::error_code ec;
+        asiolib::write(socket, asiolib::buffer(msg), ec);
+        if ((ec == asiolib::error::eof) || (ec == asiolib::error::connection_reset))
+            OnDisconnect();
+        else if (ec)
+            OnError();
+    }
+
+    virtual std::ostream& OutStream() { return outStream; }
+
+    virtual void OnConnect() = 0;
+    virtual void OnDisconnect() = 0;
+    virtual void OnError() = 0;
+    virtual void OnDataReceived(const std::string& _data) = 0;
+
+    virtual std::string Encode(const std::string& _data) const { return _data; }
+
+private:
+
+    // std::streambuf
+    std::streamsize xsputn( const char* s, std::streamsize n ) override
+    {
+        Send(Encode(std::string(s, s+n)));
+        return n;
+    }
+    int overflow( int c ) override
+    {
+        Send(Encode(std::string(1, static_cast< char >(c))));
+        return c;
+    }
+
+    asiolib::ip::tcp::socket socket;
+    enum { max_length = 1024 };
+    char data[ max_length ];
+    std::ostream outStream;
+};
+
+
+template <typename ASIOLIB>
+class Server
+{
+public:
+    // disable value semantics
+    Server( const Server& ) = delete;
+    Server& operator = ( const Server& ) = delete;
+
+    Server(typename ASIOLIB::ContextType& ios, unsigned short port) :
+        acceptor(ios, asiolib::ip::tcp::endpoint(asiolib::ip::tcp::v4(), port))
+    {
+        Accept();
+    }
+    Server(typename ASIOLIB::ContextType& ios, std::string address, unsigned short port) :
+        acceptor(ios, asiolib::ip::tcp::endpoint(ASIOLIB::IpAddressFromString(address), port))
+    {
+        Accept();
+    }
+    virtual ~Server() = default;
+    // returns shared_ptr instead of unique_ptr because Session needs to use enable_shared_from_this
+    virtual std::shared_ptr<Session> CreateSession(asiolib::ip::tcp::socket socket) = 0;
+private:
+    void Accept()
+    {
+        acceptor.async_accept([this](asiolibec::error_code ec, asiolib::ip::tcp::socket socket)
+            {
+                if (!ec) CreateSession(std::move(socket))->Start();
+                Accept();
+            });
+    }
+    asiolib::ip::tcp::acceptor acceptor;
+};
+
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_SERVER_H_
+
diff --git a/external/cli/detail/split.h b/external/cli/detail/split.h
new file mode 100644 (file)
index 0000000..fbd8686
--- /dev/null
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_SPLIT_H_
+#define CLI_DETAIL_SPLIT_H_
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+#include <cassert>
+
+namespace cli
+{
+namespace detail
+{
+
+class Text
+{
+public:
+    explicit Text(std::string _input) : input(std::move(_input))
+    {
+    }
+    void SplitInto(std::vector<std::string>& strs)
+    {
+        Reset();
+        for (char c: input)
+            Eval(c);
+        RemoveEmptyEntries();
+        splitResult.swap(strs); // puts the result back in strs
+    }
+private:
+    void Reset()
+    {
+        state = State::space;
+        prev_state = State::space;
+        sentence_type = SentenceType::double_quote;
+        splitResult.clear();
+    }
+
+    void Eval(char c)
+    {
+        switch(state)
+        {
+            case State::space:
+                EvalSpace(c);
+                break;
+            case State::word:
+                EvalWord(c);
+                break;
+            case State::sentence:
+                EvalSentence(c);
+                break;
+            case State::escape:
+                EvalEscape(c);
+                break;
+        }
+    }
+
+    void EvalSpace(char c)
+    {
+        if (c == ' ' || c == '\t' || c == '\n')
+        {
+            // do nothing
+        }
+        else if (c == '"' || c == '\'')
+        {
+            NewSentence(c);
+        }
+        else if (c == '\\')
+        {
+            // This is the case where the first character of a word is escaped.
+            // Should come back into the word state after this.
+            prev_state = State::word;
+            state = State::escape;
+            splitResult.emplace_back("");
+        }
+        else
+        {
+            state = State::word;
+            splitResult.emplace_back(1, c);
+        }
+    }
+
+    void EvalWord(char c)
+    {
+        if (c == ' ' || c == '\t' || c == '\n')
+        {
+            state = State::space;
+        }
+        else if (c == '"' || c == '\'')
+        {
+            NewSentence(c);
+        }
+        else if (c == '\\')
+        {
+            prev_state = state;
+            state = State::escape;
+        }
+        else
+        {
+            assert(!splitResult.empty());
+            splitResult.back() += c;
+        }
+    }
+
+    void EvalSentence(char c)
+    {
+        if (c == '"' || c == '\'')
+        {
+            auto new_type = c == '"' ? SentenceType::double_quote : SentenceType::quote;
+            if (new_type == sentence_type)
+                state = State::space;
+            else
+            {
+                assert(!splitResult.empty());
+                splitResult.back() += c;
+            }
+        }
+        else if (c == '\\')
+        {
+            prev_state = state;
+            state = State::escape;
+        }
+        else
+        {
+            assert(!splitResult.empty());
+            splitResult.back() += c;
+        }
+    }
+
+    void EvalEscape(char c)
+    {
+        assert(!splitResult.empty());
+        if (c != '"' && c != '\'' && c != '\\')
+            splitResult.back() += "\\";
+        splitResult.back() += c;
+        state = prev_state;
+    }
+
+    void NewSentence(char c)
+    {
+        state = State::sentence;
+        sentence_type = ( c == '"' ? SentenceType::double_quote : SentenceType::quote);
+        splitResult.emplace_back("");
+    }
+
+    void RemoveEmptyEntries()
+    {
+        // remove null entries from the vector:
+        splitResult.erase(
+            std::remove_if(
+                splitResult.begin(),
+                splitResult.end(),
+                [](const std::string& s){ return s.empty(); }
+            ),
+            splitResult.end()
+        );
+    }
+
+    enum class State { space, word, sentence, escape };
+    enum class SentenceType { quote, double_quote };
+    State state = State::space;
+    State prev_state = State::space;
+    SentenceType sentence_type = SentenceType::double_quote;
+    const std::string input;
+    std::vector<std::string> splitResult;
+};
+
+// Split the string input into a vector of strings.
+// The original string is split where there are spaces.
+// Quotes and double quotes can be used to indicate a substring that should not be splitted
+// (even if it contains spaces)
+
+//          split(strs, "");                => empty vector
+//          split(strs, " ");               => empty vector
+//          split(strs, "  ");              => empty vector
+//          split(strs, "\t");              => empty vector
+//          split(strs, "  \t \t     ");    => empty vector
+
+//          split(strs, "1234567890");      => <"1234567890">
+//          split(strs, "  foo ");          => <"foo">
+//          split(strs, "  foo \t \t bar \t");  => <"foo","bar">
+
+//          split(strs, "\"\"");            => empty vector
+//          split(strs, "\"foo bar\"");     => <"foo bar">
+//          split(strs, "    \t\t \"foo \tbar\"     \t");   => <"foo \tbar">
+//          split(strs, " first   \t\t \"foo \tbar\"     \t last"); => <"first","foo \tbar","last">
+//          split(strs, "first\"foo \tbar\"");  => <"first","foo \tbar">
+//          split(strs, "first \"'second' 'thirdh'\""); => <"first","'second' 'thirdh'">
+
+//          split(strs, "''");      => empty vector
+//          split(strs, "'foo bar'");   => <"foo bar">
+//          split(strs, "    \t\t 'foo \tbar'     \t"); => <"foo \tbar">
+//          split(strs, " first   \t\t 'foo \tbar'     \t last"); => <"first","foo \tbar","last">
+//          split(strs, "first'foo \tbar'"); => <"first","foo \tbar">
+//          split(strs, "first '\"second\" \"thirdh\"'"); => <"first","\"second\" \"thirdh\"">
+
+//          split(strs, R"("foo\"bar")"); // "foo\"bar" => <"foo"bar">
+//          split(strs, R"('foo\'bar')"); // 'foo\'bar' => <"foo'bar">
+//          split(strs, R"("foo\bar")"); // "foo\bar" => <"foo\bar">
+//          split(strs, R"("foo\\"bar")"); // "foo\\"bar" => <"foo\"bar">
+
+inline void split(std::vector<std::string>& strs, const std::string& input)
+{
+    Text sentence(input);
+    sentence.SplitInto(strs);
+}
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_SPLIT_H_
diff --git a/external/cli/detail/standaloneasiolib.h b/external/cli/detail/standaloneasiolib.h
new file mode 100644 (file)
index 0000000..adb747e
--- /dev/null
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_STANDALONEASIOLIB_H_
+#define CLI_DETAIL_STANDALONEASIOLIB_H_
+
+/**
+ * This header file provides the class `cli::StandaloneAsioLib`, using the right
+ * implementation according to the version of asio libraries included.
+ */
+
+#include <asio/version.hpp>
+
+#if ASIO_VERSION < 101300
+    #include "oldstandaloneasiolib.h"
+    namespace cli { namespace detail { using StandaloneAsioLib = OldStandaloneAsioLib; } }
+#else
+    #include "newstandaloneasiolib.h"
+    namespace cli { namespace detail { using StandaloneAsioLib = NewStandaloneAsioLib; } }
+#endif
+
+#endif // CLI_DETAIL_STANDALONEASIOLIB_H_
+
diff --git a/external/cli/detail/terminal.h b/external/cli/detail/terminal.h
new file mode 100644 (file)
index 0000000..e9a4f6d
--- /dev/null
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_TERMINAL_H_
+#define CLI_DETAIL_TERMINAL_H_
+
+#include <string>
+#include "../colorprofile.h"
+#include "inputdevice.h"
+
+namespace cli
+{
+namespace detail
+{
+
+enum class Symbol
+{
+    nothing,
+    command,
+    up,
+    down,
+    tab,
+    eof
+};
+
+class Terminal
+{
+  public:
+    explicit Terminal(std::ostream &_out) : out(_out) {}
+
+    void ResetCursor() { position = 0; }
+
+    void SetLine(const std::string &newLine)
+    {
+        out << beforeInput
+            << std::string(position, '\b') << newLine
+            << afterInput << std::flush;
+
+        // if newLine is shorter than currentLine, we have
+        // to clear the rest of the string
+        if (newLine.size() < currentLine.size())
+        {
+            out << std::string(currentLine.size() - newLine.size(), ' ');
+            // and go back
+            out << std::string(currentLine.size() - newLine.size(), '\b') << std::flush;
+        }
+
+        currentLine = newLine;
+        position = currentLine.size();
+    }
+
+    std::string GetLine() const { return currentLine; }
+
+    std::pair<Symbol, std::string> Keypressed(std::pair<KeyType, char> k)
+    {
+        switch (k.first)
+        {
+            case KeyType::eof:
+                return std::make_pair(Symbol::eof, std::string{});
+                break;
+            case KeyType::backspace:
+            {
+                if (position == 0)
+                    break;
+
+                --position;
+
+                const auto pos = static_cast<std::string::difference_type>(position);
+                // remove the char from buffer
+                currentLine.erase(currentLine.begin() + pos);
+                // go back to the previous char
+                out << '\b';
+                // output the rest of the line
+                out << std::string(currentLine.begin() + pos, currentLine.end());
+                // remove last char
+                out << ' ';
+                // go back to the original position
+                out << std::string(currentLine.size() - position + 1, '\b') << std::flush;
+                break;
+            }
+            case KeyType::up:
+                return std::make_pair(Symbol::up, std::string{});
+                break;
+            case KeyType::down:
+                return std::make_pair(Symbol::down, std::string{});
+                break;
+            case KeyType::left:
+                if (position > 0)
+                {
+                    out << '\b' << std::flush;
+                    --position;
+                }
+                break;
+            case KeyType::right:
+                if (position < currentLine.size())
+                {
+                    out << beforeInput
+                        << currentLine[position]
+                        << afterInput << std::flush;
+                    ++position;
+                }
+                break;
+            case KeyType::ret:
+            {
+                out << "\r\n";
+                auto cmd = currentLine;
+                currentLine.clear();
+                position = 0;
+                return std::make_pair(Symbol::command, cmd);
+            }
+            break;
+            case KeyType::ascii:
+            {
+                const char c = static_cast<char>(k.second);
+                if (c == '\t')
+                    return std::make_pair(Symbol::tab, std::string());
+                else
+                {
+                    const auto pos = static_cast<std::string::difference_type>(position);
+
+                    // output the new char:
+                    out << beforeInput << c;
+                    // and the rest of the string:
+                    out << std::string(currentLine.begin() + pos, currentLine.end())
+                        << afterInput;
+
+                    // go back to the original position
+                    out << std::string(currentLine.size() - position, '\b') << std::flush;
+
+                    // update the buffer and cursor position:
+                    currentLine.insert(currentLine.begin() + pos, c);
+                    ++position;
+                }
+
+                break;
+            }
+            case KeyType::canc:
+            {
+                if (position == currentLine.size())
+                    break;
+
+                const auto pos = static_cast<std::string::difference_type>(position);
+
+                // output the rest of the line
+                out << std::string(currentLine.begin() + pos + 1, currentLine.end());
+                // remove last char
+                out << ' ';
+                // go back to the original position
+                out << std::string(currentLine.size() - position, '\b') << std::flush;
+                // remove the char from buffer
+                currentLine.erase(currentLine.begin() + pos);
+                break;
+            }
+            case KeyType::end:
+            {
+                const auto pos = static_cast<std::string::difference_type>(position);
+
+                out << beforeInput
+                    << std::string(currentLine.begin() + pos, currentLine.end())
+                    << afterInput << std::flush;
+                position = currentLine.size();
+                break;
+            }
+            case KeyType::home:
+            {
+                out << std::string(position, '\b') << std::flush;
+                position = 0;
+                break;
+            }
+            case KeyType::ignored:
+                // TODO
+                break;
+        }
+
+        return std::make_pair(Symbol::nothing, std::string());
+    }
+
+  private:
+    std::string currentLine;
+    std::size_t position = 0; // next writing position in currentLine
+    std::ostream &out;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_TERMINAL_H_
diff --git a/external/cli/detail/winkeyboard.h b/external/cli/detail/winkeyboard.h
new file mode 100644 (file)
index 0000000..0ed159d
--- /dev/null
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_DETAIL_WINKEYBOARD_H_
+#define CLI_DETAIL_WINKEYBOARD_H_
+
+#include <functional>
+#include <string>
+#include <thread>
+#include <memory>
+#include <conio.h>
+#include <cassert>
+
+#include "inputdevice.h"
+
+#if !defined(NOMINMAX)
+#define NOMINMAX 1 // prevent windows from defining min and max macros
+#endif // !defined(NOMINMAX)
+#include <windows.h>
+
+namespace cli
+{
+namespace detail
+{
+
+class InputSource
+{
+public:
+
+    InputSource()
+    {
+        events[0] = CreateEvent(nullptr, FALSE, FALSE, nullptr); // Obtain a Windows handle to use to stop
+        events[1] = GetStdHandle(STD_INPUT_HANDLE); // Get a Windows handle to the keyboard input
+    }
+
+    void WaitKbHit()
+    {
+        // Wait for either the timer to expire or a key press event
+        DWORD dwResult = WaitForMultipleObjects(2, events, false, INFINITE);
+
+        if (dwResult == WAIT_FAILED)
+        {
+            // TODO
+            assert(false);
+        }
+        else
+        {
+            if (dwResult == WAIT_OBJECT_0) // WAIT_OBJECT_0 corresponds to the stop event
+            {
+                throw std::runtime_error("InputSource stop");
+            }
+            else
+            {
+                return;
+            }
+        }
+
+        // we can't reach this point
+        assert(false);
+    }
+
+    void Stop()
+    {
+        SetEvent(events[0]);
+    }
+
+private:
+    HANDLE events[2];
+};
+
+//
+
+class WinKeyboard : public InputDevice
+{
+public:
+    explicit WinKeyboard(Scheduler& _scheduler) :
+        InputDevice(_scheduler),
+        servant([this]() noexcept { Read(); })
+    {
+    }
+    ~WinKeyboard() override
+    {
+        is.Stop();
+        servant.join();
+    }
+
+private:
+
+    void Read() noexcept
+    {
+        try
+        {
+            while (true)
+            {
+                auto k = Get();
+                Notify(k);
+            }
+        }
+        catch (const std::exception&)
+        {
+            // nothing to do: just exit
+        }
+    }
+
+    std::pair<KeyType, char> Get()
+    {
+        is.WaitKbHit();
+
+        int c = _getch();
+        switch (c)
+        {
+            case EOF:
+            case 4:  // EOT ie CTRL-D
+            case 26: // CTRL-Z
+            case 3:  // CTRL-C
+                return std::make_pair(KeyType::eof, ' ');
+                break;
+
+            case 224: // symbol
+            {
+                c = _getch();
+                switch (c)
+                {
+                    case 72: return std::make_pair(KeyType::up, ' ');
+                    case 80: return std::make_pair(KeyType::down, ' ');
+                    case 75: return std::make_pair(KeyType::left, ' ');
+                    case 77: return std::make_pair(KeyType::right, ' ');
+                    case 71: return std::make_pair(KeyType::home, ' ');
+                    case 79: return std::make_pair(KeyType::end, ' ');
+                    case 83: return std::make_pair(KeyType::canc, ' ');
+                    default: return std::make_pair(KeyType::ignored, ' ');
+                }
+            }
+            case 8:
+                return std::make_pair(KeyType::backspace, c);
+                break;
+            case 13:
+                return std::make_pair(KeyType::ret, c);
+                break;
+            default: // hopefully ascii
+            {
+                const char ch = static_cast<char>(c);
+                return std::make_pair(KeyType::ascii, ch);
+            }
+        }
+        return std::make_pair(KeyType::ignored, ' ');
+    }
+
+    InputSource is;
+    std::thread servant;
+};
+
+} // namespace detail
+} // namespace cli
+
+#endif // CLI_DETAIL_WINKEYBOARD_H_
diff --git a/external/cli/filehistorystorage.h b/external/cli/filehistorystorage.h
new file mode 100644 (file)
index 0000000..1a59f95
--- /dev/null
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_FILEHISTORYSTORAGE_H_
+#define CLI_FILEHISTORYSTORAGE_H_
+
+#include "historystorage.h"
+#include <fstream>
+#include <utility>
+
+namespace cli
+{
+
+class FileHistoryStorage : public HistoryStorage
+{
+public:
+    explicit FileHistoryStorage(std::string _fileName, std::size_t size = 1000) : 
+        maxSize(size),
+        fileName(std::move(_fileName))
+    {
+    }
+    void Store(const std::vector<std::string>& cmds) override
+    {
+        using dt = std::vector<std::string>::difference_type;
+        auto commands = Commands();
+        commands.insert(commands.end(), cmds.begin(), cmds.end());
+        if (commands.size() > maxSize)
+            commands.erase(
+                commands.begin(), 
+                commands.begin() + static_cast<dt>(commands.size() - maxSize)
+            );
+        std::ofstream f(fileName, std::ios_base::out);
+            for (const auto& line: commands)
+                f << line << '\n';
+    }
+    std::vector<std::string> Commands() const override
+    {
+        std::vector<std::string> commands;
+        std::ifstream in(fileName);
+        if (in)
+        {
+            std::string line;
+            while (std::getline(in, line))
+                commands.push_back(line);
+        }
+        return commands;
+    }
+    void Clear() override
+    {
+        std::ofstream f(fileName, std::ios_base::out | std::ios_base::trunc);
+    }
+
+private:
+    const std::size_t maxSize;
+    const std::string fileName;
+};
+
+} // namespace cli
+
+#endif // CLI_FILEHISTORYSTORAGE_H_
diff --git a/external/cli/historystorage.h b/external/cli/historystorage.h
new file mode 100644 (file)
index 0000000..f5af2b1
--- /dev/null
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_HISTORYSTORAGE_H_
+#define CLI_HISTORYSTORAGE_H_
+
+#include <vector>
+#include <string>
+
+namespace cli
+{
+
+class HistoryStorage
+{
+public:
+    virtual ~HistoryStorage() = default;
+    // Store a vector of commands in the history storage
+    virtual void Store(const std::vector<std::string>& commands) = 0;
+    // Returns all the commands stored
+    virtual std::vector<std::string> Commands() const = 0;
+    // Clear the whole content of the storage
+    // After calling this method, Commands() returns the empty vector
+    virtual void Clear() = 0;
+};
+
+} // namespace cli
+
+#endif // CLI_HISTORYSTORAGE_H_
diff --git a/external/cli/loopscheduler.h b/external/cli/loopscheduler.h
new file mode 100644 (file)
index 0000000..7bc3169
--- /dev/null
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_LOOPSCHEDULER_H_
+#define CLI_LOOPSCHEDULER_H_
+
+#include <queue>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include "scheduler.h"
+
+namespace cli
+{
+
+/**
+ * @brief The LoopScheduler is a simple thread-safe scheduler
+ * 
+ */
+class LoopScheduler : public Scheduler
+{
+public:
+    LoopScheduler() = default;
+    ~LoopScheduler() override
+    {
+        Stop();
+    }
+
+    // non copyable
+    LoopScheduler(const LoopScheduler&) = delete;
+    LoopScheduler& operator=(const LoopScheduler&) = delete;
+
+    void Stop()
+    {
+        std::lock_guard<std::mutex> lck (mtx);
+        running = false;
+        cv.notify_all();
+    }
+
+    void Run()
+    {
+        while( ExecOne() ) {};
+    }
+
+    bool Stopped() const
+    {
+        std::lock_guard<std::mutex> lck (mtx);
+        return !running;
+    }
+
+    void Post(const std::function<void()>& f) override
+    {
+        std::lock_guard<std::mutex> lck (mtx);
+        tasks.push(f);
+        cv.notify_all();
+    }
+
+    bool ExecOne()
+    {
+        std::function<void()> task;
+        {
+            std::unique_lock<std::mutex> lck(mtx);
+            cv.wait(lck, [this](){ return !running || !tasks.empty(); });
+            if (!running)
+                return false;
+            task = tasks.front();
+            tasks.pop();
+        }
+
+        if (task)
+            task();
+
+        return true;
+    }
+
+    bool PollOne()
+    {
+        std::function<void()> task;
+        {
+            std::lock_guard<std::mutex> lck(mtx);
+            if (!running || tasks.empty())
+                return false;
+            task = tasks.front();
+            tasks.pop();
+        }
+
+        if (task)
+            task();
+
+        return true;
+    }
+
+private:
+    std::queue<std::function<void()>> tasks;
+    bool running{ true };
+    mutable std::mutex mtx;
+    std::condition_variable cv;
+};
+
+} // namespace cli
+
+#endif // CLI_LOOPSCHEDULER_H_
diff --git a/external/cli/scheduler.h b/external/cli/scheduler.h
new file mode 100644 (file)
index 0000000..6c74ec8
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_SCHEDULER_H_
+#define CLI_SCHEDULER_H_
+
+#include <functional>
+
+namespace cli
+{
+
+/**
+ * A `Scheduler` represents an engine capable of running a task.
+ * Its method `Post` can be safely called from any thread to submit the task
+ * that will execute in an unspecified thread of execution as soon as possible
+ * (but in any case after the call to `Post` is terminated).
+ */
+class Scheduler
+{
+public:
+    virtual ~Scheduler() = default;
+
+    /// Submits a completion token or function object for execution.
+    virtual void Post(const std::function<void()>& f) = 0;
+};
+
+} // namespace cli
+
+#endif // CLI_SCHEDULER_H_
diff --git a/external/cli/standaloneasiocliasyncsession.h b/external/cli/standaloneasiocliasyncsession.h
new file mode 100644 (file)
index 0000000..c5eb680
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_STANDALONEASIOCLIASYNCSESSION_H_
+#define CLI_STANDALONEASIOCLIASYNCSESSION_H_
+
+#include "detail/genericcliasyncsession.h"
+#include "detail/standaloneasiolib.h"
+
+
+namespace cli { using StandaloneAsioCliAsyncSession = detail::GenericCliAsyncSession<detail::StandaloneAsioLib>; }
+
+#endif // CLI_STANDALONEASIOCLIASYNCSESSION_H_
+
diff --git a/external/cli/standaloneasioremotecli.h b/external/cli/standaloneasioremotecli.h
new file mode 100644 (file)
index 0000000..2ac50d9
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_STANDALONEASIOREMOTECLI_H_
+#define CLI_STANDALONEASIOREMOTECLI_H_
+
+#include "detail/standaloneasiolib.h"
+#include "detail/genericasioremotecli.h"
+
+namespace cli { using StandaloneAsioCliTelnetServer = detail::CliGenericTelnetServer<detail::StandaloneAsioLib>; }
+
+
+#endif // CLI_STANDALONEASIOREMOTECLI_H_
+
diff --git a/external/cli/standaloneasioscheduler.h b/external/cli/standaloneasioscheduler.h
new file mode 100644 (file)
index 0000000..fade453
--- /dev/null
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_STANDALONEASIOSCHEDULER_H_
+#define CLI_STANDALONEASIOSCHEDULER_H_
+
+#include "detail/genericasioscheduler.h"
+#include "detail/standaloneasiolib.h"
+
+namespace cli { using StandaloneAsioScheduler = detail::GenericAsioScheduler<detail::StandaloneAsioLib>; }
+
+#endif // CLI_STANDALONEASIOSCHEDULER_H_
diff --git a/external/cli/volatilehistorystorage.h b/external/cli/volatilehistorystorage.h
new file mode 100644 (file)
index 0000000..d357c01
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * CLI - A simple command line interface.
+ * Copyright (C) 2016-2021 Daniele Pallastrelli
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef CLI_VOLATILEHISTORYSTORAGE_H_
+#define CLI_VOLATILEHISTORYSTORAGE_H_
+
+#include "historystorage.h"
+#include <deque>
+
+namespace cli
+{
+
+class VolatileHistoryStorage : public HistoryStorage
+{
+    public:
+        explicit VolatileHistoryStorage(std::size_t size = 1000) : maxSize(size) {}
+        void Store(const std::vector<std::string>& cmds) override
+        {
+            using dt = std::deque<std::string>::difference_type;
+            commands.insert(commands.end(), cmds.begin(), cmds.end());
+            if (commands.size() > maxSize)
+                commands.erase(
+                    commands.begin(),
+                    commands.begin()+static_cast<dt>(commands.size()-maxSize)
+                );
+        }
+        std::vector<std::string> Commands() const override
+        {
+            return std::vector<std::string>(commands.begin(), commands.end());
+        }
+        void Clear() override
+        {
+            commands.clear();
+        }
+    private:
+        const std::size_t maxSize;
+        std::deque<std::string> commands;
+};
+
+} // namespace cli
+
+#endif // CLI_VOLATILEHISTORYSTORAGE_H_
index 54ac780..7818836 100644 (file)
@@ -1,17 +1,21 @@
 project(
        'mmi',
        ['c', 'cpp'],
-       version : '1.0.2',
-       license : 'MIT',
-       default_options : ['b_pie=true']
+       version : '2.0.0',
+       license : 'Apache-2.0',
+       default_options : ['b_pie=true', 'cpp_std=c++17']
 )
 
 mmi_version = meson.project_version().split('.')
 mmi_prefix = get_option('prefix')
 mmi_prefix_bindir = join_paths(mmi_prefix, get_option('bindir'))
 mmi_prefix_libdir = join_paths(mmi_prefix, get_option('libdir'))
+mmi_prefix_plugindir = join_paths(mmi_prefix, get_option('datadir'), 'mmi', 'plugins')
+mmi_prefix_scriptdir = join_paths(mmi_prefix, get_option('datadir'), 'mmi', 'scripts')
 
 pkgconfig = import('pkgconfig')
 
+subdir('capi')
 subdir('src')
+subdir('plugins')
 subdir('tests')
diff --git a/meson_options.txt b/meson_options.txt
deleted file mode 100644 (file)
index e69de29..0000000
index e00ddf9..84f31e4 100644 (file)
@@ -1,9 +1,9 @@
 %define USE_GCOV 0
 
 Name:          mmi
-Version:       1.0.2
+Version:       2.0.1
 Release:       0
-Summary:       Multi-modal Interaction Framework Library
+Summary:       Multi-modal Interaction Framework
 License:       MIT
 URL:           http://www.tizen.org
 Source:                %{name}-%{version}.tar.xz
@@ -11,30 +11,50 @@ Source1004: %{name}.manifest
 
 BuildRequires: meson
 BuildRequires: tidl
-BuildRequires: pkgconfig(libtzplatform-config)
-BuildRequires: pkgconfig(bundle)
-BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: pkgconfig(bundle)
+BuildRequires: pkgconfig(gio-2.0)
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(dlog)
 BuildRequires: pkgconfig(rpc-port)
 BuildRequires: pkgconfig(ecore)
-
+BuildRequires: pkgconfig(capi-media-audio-io)
+BuildRequires: pkgconfig(capi-media-sound-manager)
+BuildRequires: pkgconfig(capi-media-camera)
+BuildRequires: pkgconfig(libaurum)
+BuildRequires: pkgconfig(vconf)
+BuildRequires:  pkgconfig(json-glib-1.0)
+BuildRequires: pkgconfig(capi-media-vision)
+BuildRequires: pkgconfig(capi-media-image-util)
+BuildRequires: pkgconfig(opencv)
 #Build dependencies for tests
 BuildRequires: pkgconfig(gmock)
 
 %description
-MMI(Multi-modal Interaction) Framework Library
+MMI(Multi-modal Interaction) Framework
+
+%package plugins
+Summary:       Plugins for MMI Framework
+Group:         System/Libraries
+%description plugins
+Plugins for MMI Framework
+
+%package cli
+Summary:       CLI for MMI Framework
+Group:         System/Libraries
+%description cli
+CLI for MMI Framework
 
 %package devel
-Summary:       Development package for MMI Framework Library
+Summary:       Development package for MMI Framework
 Group:         Development/Libraries
 Requires:      %{name} = %{version}-%{release}
 Requires:      pkgconfig(rpc-port)
 %description devel
-Development package for MMI Framework Library
+Development package for MMI Framework
 
 %package tests
-Summary:       Testcases for MMI Framework Library
+Summary:       Testcases for MMI Framework
 Group:         System/Libraries
 %description tests
 Testcases for testing MMI Framework APIs
@@ -45,7 +65,9 @@ cp %{SOURCE1004} .
 
 #generate mmi-proxy using TIDL Compiler
 tidlc -p -l C -i tidl/mmi.tidl -o mmi_proxy
-mv mmi_proxy.* src
+mv mmi_proxy.* src/mmi
+tidlc -s -l C -i tidl/mmi.tidl -o mmi_stub
+mv mmi_stub.* src/mmi-manager
 
 %build
 %if "%{USE_GCOV}" == "1"
@@ -53,15 +75,22 @@ CFLAGS+=" -fprofile-arcs -ftest-coverage -DTIZEN_TEST_GCOV"
 CXXFLAGS+=" -fprofile-arcs -ftest-coverage -DTIZEN_TEST_GCOV"
 LDFLAGS+=" -lgcov"
 %endif
+
 meson setup --prefix=/usr \
        --bindir %{_bindir} \
        --libdir %{_libdir} \
+       --datadir %{_datadir} \
        builddir
 ninja -C builddir all
 
 %install
 DESTDIR=%{buildroot} ninja -C builddir install
 
+%check
+%if "%{_mmi_test_enable}" == "true"
+ninja -C builddir test
+%endif
+
 %post -p /sbin/ldconfig
 %postun -p /sbin/ldconfig
 
@@ -70,6 +99,23 @@ DESTDIR=%{buildroot} ninja -C builddir install
 %defattr(-,root,root,-)
 %license COPYING
 %{_libdir}/*.so*
+%{_bindir}/mmi-manager
+%{_datadir}/packages/mmi-manager.xml
+
+%files plugins
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license COPYING
+%{_datadir}/mmi/plugins/*.so*
+%{_datadir}/mmi/scripts/*.mws
+%{_datadir}/face_recognition/*
+
+%files cli
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license COPYING
+%{_bindir}/mmi-cli
+%{_bindir}/mmi-cli-node-tester
 
 %files devel
 %manifest %{name}.manifest
@@ -82,4 +128,5 @@ DESTDIR=%{buildroot} ninja -C builddir install
 %defattr(-,root,root,-)
 %license COPYING
 %{_bindir}/mmi-tests
+%{_bindir}/mmi-manager-tests
 
diff --git a/plugins/meson.build b/plugins/meson.build
new file mode 100644 (file)
index 0000000..c7ea24c
--- /dev/null
@@ -0,0 +1,2 @@
+subdir('nodes')
+subdir('workflows')
diff --git a/plugins/nodes/camera/meson.build b/plugins/nodes/camera/meson.build
new file mode 100644 (file)
index 0000000..add475c
--- /dev/null
@@ -0,0 +1,27 @@
+mmi_module_camera_library_srcs = [
+       'mmi-module-camera.cpp',
+       'mmi-module-camera-preview.cpp',
+       'mmi-module-camera-preview.h',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+camera_dep = dependency('capi-media-camera', method : 'pkg-config')
+
+mmi_module_camera_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       camera_dep,
+       ]
+
+mmi_module_camera_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_camera_library = library('mmi_module_camera',
+       mmi_module_camera_library_srcs,
+       include_directories : [ mmi_module_camera_include_dirs ],
+       dependencies : [mmi_module_camera_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/nodes/camera/mmi-module-camera-preview.cpp b/plugins/nodes/camera/mmi-module-camera-preview.cpp
new file mode 100644 (file)
index 0000000..bdc4a30
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-node.h>
+#include "mmi-module-camera-preview.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-CAMERA"
+
+int Camera::frame_num = 0;
+
+Camera::Camera()
+{
+}
+
+Camera::~Camera()
+{
+}
+
+int Camera::initialize()
+{
+    if (CAMERA_ERROR_NONE != camera_create(CAMERA_DEVICE_CAMERA0, &m_camera)) {
+        _E("[Camera] Fail to create camera");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (CAMERA_ERROR_NONE != camera_set_display(m_camera, CAMERA_DISPLAY_TYPE_NONE, NULL)){
+        _E("[Camera] Fail to set display");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    camera_set_preview_format(m_camera, CAMERA_PIXEL_FORMAT_I420);
+    camera_set_preview_resolution(m_camera, 640, 480);
+    camera_attr_set_preview_fps(m_camera, CAMERA_ATTR_FPS_15);
+    camera_set_state_changed_cb(m_camera, _camera_state_changed_cb, this);
+    camera_set_preview_cb(m_camera, _camera_preview_cb, this);
+
+    return MMI_ERROR_NONE;
+}
+
+int Camera::deinitialize()
+{
+
+    camera_unset_preview_cb(m_camera);
+
+    if (CAMERA_ERROR_NONE != camera_destroy(m_camera)) {
+        _E("[Camera] Fail to distroy camera");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+    return MMI_ERROR_NONE;
+}
+
+int Camera::start()
+{
+    int width = 0;
+    int height = 0;
+    camera_attr_fps_e fps;
+
+    _D("Camera::start()");
+
+    if (CAMERA_ERROR_NONE != camera_start_preview(m_camera)) {
+        _E("[Camera] Fail to start preview");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    camera_attr_get_preview_fps(m_camera, &fps);
+    camera_get_preview_resolution(m_camera, &width, &height);
+
+    _I("camera info -> fps :%d, width : %d, height : %d", fps, width, height);
+
+    return MMI_ERROR_NONE;
+}
+
+int Camera::stop()
+{
+    _D("Camera::stop()");
+
+    if (CAMERA_ERROR_NONE != camera_stop_preview(m_camera)) {
+        _E("[Camera] Fail to stop preview");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+void Camera::set_preview_callback(camera_data_callback callback)
+{
+    m_camera_callback = callback;
+}
+
+void Camera::set_node_instance(mmi_node_instance_h node_instance)
+{
+    m_node_instance = node_instance;
+}
+
+void Camera::_camera_preview_cb(camera_preview_data_s *frame, void *user_data)
+{
+    int size = 0;
+    unsigned long timestamp = 0;
+    unsigned char *pData = nullptr;
+
+    Camera *camera = static_cast<Camera *>(user_data);
+    camera_attr_get_preview_frame_timestamp(camera->m_camera, &timestamp);
+
+    switch (frame->format) {
+    case CAMERA_PIXEL_FORMAT_NV12:
+        _D("NV12 p[%p,%p], size[%u,%u]", frame->data.double_plane.y, frame->data.double_plane.uv,
+            frame->data.double_plane.y_size, frame->data.double_plane.uv_size);
+        pData = frame->data.double_plane.y;
+        size = frame->data.double_plane.y_size + frame->data.double_plane.uv_size;
+
+        break;
+    case CAMERA_PIXEL_FORMAT_I420:
+        _D("I420 p[%p,%p,%p], size[%u,%u,%u]", frame->data.triple_plane.y, frame->data.triple_plane.u, frame->data.triple_plane.v,
+            frame->data.triple_plane.y_size, frame->data.triple_plane.u_size, frame->data.triple_plane.v_size);
+        pData = frame->data.triple_plane.y;
+        size = frame->data.triple_plane.y_size + frame->data.triple_plane.u_size + frame->data.triple_plane.v_size;
+
+        break;
+    default:
+        _E("invalid format : %u", frame->format);
+        break;
+
+    }
+    Camera::frame_num++;
+
+    if (camera->m_camera_callback && (Camera::frame_num % 3 == 0) && pData) {
+        camera->m_camera_callback(pData, size, timestamp, camera->m_node_instance);
+    }
+}
+
diff --git a/plugins/nodes/camera/mmi-module-camera-preview.h b/plugins/nodes/camera/mmi-module-camera-preview.h
new file mode 100644 (file)
index 0000000..17ba9d1
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <mmi-log.h>
+#include <functional>
+
+#include <mmi-node.h>
+#include <camera_internal.h>
+
+using camera_data_callback = std::function<void(void *, size_t, unsigned long, void *)>;
+
+class Camera {
+public:
+    static int frame_num;
+
+    Camera();
+    ~Camera();
+
+    int initialize();
+    int deinitialize();
+    int start();
+    int stop();
+
+    void set_preview_callback(camera_data_callback callback);
+    void set_node_instance(mmi_node_instance_h node_instance);
+
+    static void _camera_state_changed_cb(camera_state_e previous, camera_state_e current, bool by_policy, void *user_data)
+    {
+        _D("camera state changed %d -> %d", previous, current);
+    }
+
+    static void _camera_interrupted_cb(camera_policy_e policy, camera_state_e previous, camera_state_e current,
+         void *user_data)
+    {
+        _D("camera interrupted callback called[state %d -> %d, policy %d]", previous, current, policy);
+    }
+
+private:
+    camera_h m_camera{nullptr};
+    camera_data_callback m_camera_callback{nullptr};
+    mmi_node_instance_h m_node_instance{nullptr};
+    static void _camera_preview_cb(camera_preview_data_s *frame, void *user_data);
+};
diff --git a/plugins/nodes/camera/mmi-module-camera.cpp b/plugins/nodes/camera/mmi-module-camera.cpp
new file mode 100644 (file)
index 0000000..9ecea3a
--- /dev/null
@@ -0,0 +1,185 @@
+#include <mmi.h>
+#include <mmi-plugin-storage.h>
+#include <mmi-node.h>
+#include <mmi-log.h>
+
+#include <string>
+#include <map>
+#include <camera_internal.h>
+#include "mmi-module-camera-preview.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-CAMERA"
+
+static std::map<mmi_node_instance_h, Camera *> g_camera_map;
+
+void feed_preview_callback(void *data, size_t size, unsigned long timestamp, void *user_data)
+{
+    mmi_node_instance_h instance = (mmi_node_instance_h)user_data;
+    mmi_port_instance_h port_instance = nullptr;
+    mmi_data_h output_data = nullptr;
+
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, "VIDEO", &port_instance);
+    mmi_data_create_video(data, size, &output_data);
+    mmi_port_instance_generate_output(port_instance, output_data);
+    mmi_data_destroy(output_data);
+    _D("frame num : %d, timestamp : %lu", Camera::frame_num, timestamp);
+}
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+    _D("[Camera] Node initialize callback is called for %p", instance);
+
+    if (nullptr == instance) {
+        _E("[Camera] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    Camera *camera = new(std::nothrow) Camera();
+    if (nullptr == camera) {
+        _E("[ERROR] Failed to create instance");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    camera->initialize();
+    camera->set_node_instance(instance);
+    camera->set_preview_callback(feed_preview_callback);
+    g_camera_map.insert(std::pair<mmi_node_instance_h, Camera *>(instance, camera));
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+    _D("[Camera] Node deinitialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[Camera] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Camera *camera = g_camera_map[instance];
+        camera->deinitialize();
+        g_camera_map.erase(instance);
+        delete camera;
+    } catch (std::out_of_range &e) {
+        _E("[Camera] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+    _D("Node attribute set callback is called for %p", instance);
+
+    if (nullptr == instance) {
+        _E("[CAMERA] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (nullptr == attribute) {
+        _E("[Camera] attribute is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    // TODO: set attribute "UNFOCUSED_ONLY"
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+    _D("[Camera] Node activate callback is called for %p", instance);
+
+    if (nullptr == instance) {
+        _E("[Camera] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Camera *camera = g_camera_map[instance];
+        camera->start();
+    } catch (std::out_of_range &e) {
+        _E("[Camera] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+    _D("[Camera] Node deactivate callback is called for %p", instance);
+
+    if (nullptr == instance) {
+        _E("[Camera] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Camera *camera = g_camera_map[instance];
+        camera->stop();
+    } catch (std::out_of_range &e) {
+        _E("[Camera] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal)
+{
+    _D("Node signal received callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) {
+    _D("Port output format request callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+// TODO: It must be determined how to receive input data from the client
+static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) {
+    _D("Port input data callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_node_list()
+    {
+        mmi_node_callbacks node_callbacks {
+            node_initialized_cb,
+            node_deinitialized_cb,
+            node_attribute_set_cb,
+            node_activated_cb,
+            node_deactivated_cb,
+            node_signal_received_cb
+        };
+
+        mmi_port_callbacks camera_port_callbacks {
+            port_output_format_requested_cb,
+            port_input_data_received_cb
+        };
+
+        mmi_port_h camera_port = nullptr;
+        mmi_port_create(&camera_port);
+        mmi_port_set_name(camera_port, "VIDEO");
+        mmi_port_set_type(camera_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(camera_port, MMI_DATA_TYPE_VIDEO);
+        mmi_port_set_callbacks(camera_port, camera_port_callbacks);
+
+        mmi_node_h camera_node = nullptr;
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA, &camera_node);
+        mmi_node_add_port(camera_node, camera_port);
+        mmi_node_set_callbacks(camera_node, node_callbacks);
+
+        mmi_node_register(camera_node);
+
+        mmi_node_destroy(camera_node);
+    }
+
+} // extern "C"
diff --git a/plugins/nodes/face-recognition/data/recognizerInfo.json b/plugins/nodes/face-recognition/data/recognizerInfo.json
new file mode 100644 (file)
index 0000000..dc7ba76
--- /dev/null
@@ -0,0 +1,15 @@
+{
+    "maxFaceCount": 2,
+    "faceInfo": [
+        {
+            "faceId": "test4",
+            "faceName": "test4",
+            "enrollFilePath": "/usr/share/face_recognition/enroll/soo_1.jpg"
+        },
+        {
+            "faceId": "test2",
+            "faceName": "test2",
+            "enrollFilePath": "/usr/share/face_recognition/enroll/soo_4.jpg"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/plugins/nodes/face-recognition/meson.build b/plugins/nodes/face-recognition/meson.build
new file mode 100644 (file)
index 0000000..6e48678
--- /dev/null
@@ -0,0 +1,37 @@
+mmi_module_fr_library_srcs = [
+       'mmi-module-fr.cpp',
+       'mmi-module-fr-data-manager.h',
+       'mmi-module-fr-data-manager.cpp'
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+json_dep = dependency('json-glib-1.0', method : 'pkg-config')
+media_vision_dep = dependency('capi-media-vision', method : 'pkg-config')
+image_util_dep = dependency('capi-media-image-util', method : 'pkg-config')
+opencv_dep = dependency('opencv', method : 'pkg-config')
+
+mmi_module_fr_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       json_dep,
+       media_vision_dep,
+       image_util_dep,
+       opencv_dep,
+       ]
+
+mmi_module_fr_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_fr_library = library('mmi_module_fr',
+       mmi_module_fr_library_srcs,
+       include_directories : [ mmi_module_fr_include_dirs ],
+       dependencies : [mmi_module_fr_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
+
+install_data('data/recognizerInfo.json', install_dir : '/usr/share/face_recognition/')
diff --git a/plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp
new file mode 100644 (file)
index 0000000..10b5f8c
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// FIXME: temporary module for testing
+// FIXME: remove this module after register/recognize fw/app is ready
+#include <json-glib-1.0/json-glib/json-glib.h>
+#include <mmi-log.h>
+
+#include "mmi-module-fr-data-manager.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-FACE-RECOGNITION"
+
+DataManager::DataManager()
+{
+       m_info_file_path = std::string("/usr/share/face_recognition/recognizerInfo.json");
+       m_mutex = new(std::nothrow) std::mutex();
+       if (nullptr == m_mutex) {
+               _E("[FR DATAMGR] Failed to create mutex");
+               return;
+       }
+       load_info();
+}
+
+DataManager::~DataManager()
+{
+       delete m_mutex;
+       m_mutex = nullptr;
+}
+
+void DataManager::load_info()
+{
+       _D("[FR DATAMGR] load info ");
+       std::lock_guard <std::mutex> lock(*m_mutex);
+
+       JsonParser *parser = nullptr;
+       parser = json_parser_new();
+       if (nullptr == parser) {
+               _E("[FR DATAMGR] Failed to create json parser");
+               return;
+       }
+
+       GError *error = nullptr;
+       if (!json_parser_load_from_file(parser, m_info_file_path.c_str(), &error)) {
+               _E("[FR DATAMGR] Failed to load json file: %s", error->message);
+               g_error_free(error);
+               return;
+       }
+
+       JsonObject *recognizer_info = json_node_get_object(json_parser_get_root(parser));
+       if (nullptr == recognizer_info) {
+               _E("[FR DATAMGR] Failed to get root object");
+               g_object_unref(parser);
+               parser = nullptr;
+               return;
+       }
+
+       m_max_face_count = json_object_get_int_member(recognizer_info, "maxFaceCount");
+       _D("[FR DATAMGR] max face count is %d", m_max_face_count);
+
+       JsonArray *face_info = json_object_get_array_member(recognizer_info, "faceInfo");
+       if (nullptr == face_info) {
+               _E("[FR DATAMGR] Failed to get faceInfo object");
+               g_object_unref(parser);
+               parser = nullptr;
+               return;
+       }
+
+       for (int i = 0; i < json_array_get_length(face_info); i++) {
+               JsonObject *face = json_array_get_object_element(face_info, i);
+               if (nullptr == face) {
+                       _E("[FR DATAMGR] Failed to get face object");
+                       g_object_unref(parser);
+                       parser = nullptr;
+                       return;
+               }
+
+               const gchar *id = json_object_get_string_member(face, "faceId");
+               const gchar *name = json_object_get_string_member(face, "faceName");
+               const gchar *file_path = json_object_get_string_member(face, "enrollFilePath");
+
+               FaceInfo info;
+               info.id = std::string(id);
+               info.name = std::string(name);
+               info.file_path = std::string(file_path);
+               _D("[FR DATAMGR] face info: id: %s, name: %s, file_path: %s", info.id.c_str(), info.name.c_str(), info.file_path.c_str());
+               m_face_list.push_back(info);
+       }
+
+       g_object_unref(parser);
+       parser = nullptr;
+}
+
+void DataManager::save_info()
+{
+       _D("[FR DATAMGR] save info");
+       std::lock_guard <std::mutex> lock(*m_mutex);
+       JsonBuilder *builder = json_builder_new();
+
+       json_builder_begin_object(builder);
+
+       json_builder_set_member_name(builder, "maxFaceCount");
+       json_builder_add_int_value(builder, m_max_face_count);
+
+       json_builder_set_member_name(builder, "faceInfo");
+
+       json_builder_begin_array(builder);
+       for (int i = 0; i < m_face_list.size(); i++) {
+               json_builder_begin_object(builder);
+               json_builder_set_member_name(builder, "faceId");
+               json_builder_add_string_value(builder, m_face_list[i].id.c_str());
+               json_builder_set_member_name(builder, "faceName");
+               json_builder_add_string_value(builder, m_face_list[i].name.c_str());
+               json_builder_set_member_name(builder, "enrollFilePath");
+               json_builder_add_string_value(builder, m_face_list[i].file_path.c_str());
+               json_builder_end_object(builder);
+       }
+       json_builder_end_array(builder);
+
+       json_builder_end_object(builder);
+
+       JsonGenerator *generator = json_generator_new();
+       json_generator_set_root(generator, json_builder_get_root(builder));
+       json_generator_set_pretty(generator, TRUE);
+
+       // save file and check return value
+       gboolean ret = json_generator_to_file(generator, m_info_file_path.c_str(), NULL);
+       if (FALSE == ret) {
+               _E("[FR DATAMGR] Failed to save json file");
+       }
+
+       if (builder) {
+               g_object_unref(builder);
+               builder = nullptr;
+       }
+       if (generator) {
+               g_object_unref(generator);
+               generator = nullptr;
+       }
+}
+
+void DataManager::add_face(FaceInfo info)
+{
+       _D("[FR DATAMGR] add face info");
+       {
+               std::lock_guard <std::mutex> lock(*m_mutex);
+               m_face_list.push_back(info);
+               for (auto &i : m_face_list) {
+                       _D("[FR DATAMGR] face info: id: %s, name: %s, model: %s", i.id.c_str(), i.name.c_str(), i.file_path.c_str());
+               }
+       }
+
+       save_info();
+}
+
+void DataManager::delete_face(std::string id)
+{
+       _D("[FR DATAMGR] delete face info");
+       {
+               std::lock_guard <std::mutex> lock(*m_mutex);
+               for (int i = 0; i < m_face_list.size(); i++) {
+                       if (m_face_list[i].id == id) {
+                               m_face_list.erase(m_face_list.begin() + i);
+                               break;
+                       }
+               }
+       }
+       save_info();
+}
+
+void DataManager::update_face(FaceInfo info)
+{
+       _D("[FR DATAMGR] update face info");
+       {
+               std::lock_guard <std::mutex> lock(*m_mutex);
+               for (int i = 0; i < m_face_list.size(); i++) {
+                       if (m_face_list[i].id == info.id) {
+                               m_face_list[i].name = info.name;
+                               m_face_list[i].file_path = info.file_path;
+                               break;
+                       }
+               }
+       }
+
+       save_info();
+}
+
+int DataManager::get_face_count()
+{
+       _D("[FR DATAMGR] get face count");
+       std::lock_guard <std::mutex> lock(*m_mutex);
+       int count = m_face_list.size();
+       return count;
+}
+
+FaceInfo DataManager::get_face_info(std::string id)
+{
+       _D("[FR DATAMGR] get face info");
+       std::lock_guard <std::mutex> lock(*m_mutex);
+       FaceInfo info;
+       for (int i = 0; i < m_face_list.size(); i++) {
+               if (m_face_list[i].id == id) {
+                       info = m_face_list[i];
+                       break;
+               }
+       }
+
+       return info;
+}
+
+std::vector<FaceInfo> DataManager::get_face_list()
+{
+       _D("[FR DATAMGR] get face list");
+       std::lock_guard <std::mutex> lock(*m_mutex);
+
+       std::vector<FaceInfo> list = m_face_list;
+       return list;
+}
diff --git a/plugins/nodes/face-recognition/mmi-module-fr-data-manager.h b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.h
new file mode 100644 (file)
index 0000000..000de19
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+struct FaceInfo {
+       std::string id;
+       std::string name;
+       std::string file_path;
+};
+
+class DataManager {
+private:
+       void load_info();
+       void save_info();
+
+public:
+       DataManager();
+       ~DataManager();
+       static DataManager& get_instance() {
+               static DataManager instance;
+               return instance;
+       }
+
+       std::vector<FaceInfo> get_face_list();
+       FaceInfo get_face_info(std::string id);
+       int get_face_count();
+       void add_face(FaceInfo info);
+       void delete_face(std::string id);
+       void update_face(FaceInfo info);
+
+private:
+       static DataManager m_instance;
+       std::vector<FaceInfo> m_face_list;
+       int m_max_face_count{0};
+       std::string m_info_file_path{};
+       std::mutex* m_mutex{nullptr};
+};
diff --git a/plugins/nodes/face-recognition/mmi-module-fr.cpp b/plugins/nodes/face-recognition/mmi-module-fr.cpp
new file mode 100644 (file)
index 0000000..0266f6a
--- /dev/null
@@ -0,0 +1,595 @@
+#include <mmi.h>
+#include <mmi-attribute.h>
+#include <mmi-node.h>
+#include <mmi-node-processor.h>
+#include <mmi-log.h>
+#include <mmi-error.h>
+
+#include <mv_face.h>
+#include <mv_common.h>
+#include <mv_inference.h>
+#include <mv_face_recognition.h>
+#include "mv_face_recognition_internal.h"
+#include <image_util.h>
+
+#include <string>
+#include <map>
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <dlog.h>
+#include <Ecore.h>
+
+#include "mmi-module-fr-data-manager.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-FACE-RECOGNITION"
+
+#define FRAME_WIDTH            640
+#define FRAME_HEIGHT   480
+
+static const char *INPUT_PORT_NAME_VIDEO = "VIDEO";
+static const char *OUTPUT_PORT_NAME_MATCHED_FACES = "MATCHED_FACES";
+static const char *OUTPUT_RESULT_ELEMENT_NODE = "fromNode";
+static const char *OUTPUT_RESULT_NODE_NAME = "FACE_RECOGNITION";
+static const char *OUTPUT_RESULT_ELEMENT_CANDIDATE = "recognizedCandidates";
+
+static mv_face_recognition_h g_face_recog_h = nullptr;
+static DataManager *g_data_manager = nullptr;
+
+struct _facedata_s
+{
+       unsigned char *id;
+       unsigned int width;
+       unsigned int height;
+       unsigned int channel;
+       unsigned int size;
+       unsigned char *data;
+};
+
+typedef struct _facedata_s facedata_s;
+static std::map<char *, facedata_s *> g_registered_faces_map;
+static std::map<mmi_port_instance_h, mmi_node_instance_h> g_input_port_to_instance_map;
+
+int register_face_from_file(const char *file_path, const char *file_name)
+{
+       unsigned char *data_buffer = NULL;
+       size_t buffer_size = 0;
+       unsigned int width = FRAME_WIDTH;
+       unsigned int height = FRAME_HEIGHT;
+       image_util_decode_h img_decoder = NULL;
+       image_util_image_h decoded_img = NULL;
+       mv_source_h mv_src = NULL;
+       image_util_colorspace_e colorspace;
+
+       int ret = -1;
+
+       /* Get image from filepath, Decode image and Fill the image data to mv_source handle */
+       ret = image_util_decode_create(&img_decoder);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to cretae image decoder");
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+
+       ret = image_util_decode_set_input_path(img_decoder, file_path);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to set input path");
+               image_util_decode_destroy(img_decoder);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_decode_set_colorspace(img_decoder, IMAGE_UTIL_COLORSPACE_RGB888);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to image_util_decode_set_colorspace");
+               image_util_decode_destroy(img_decoder);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_decode_set_output_buffer(img_decoder, &data_buffer);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to set output buffer");
+               image_util_decode_destroy(img_decoder);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_decode_run2(img_decoder, &decoded_img);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to run decoder");
+               image_util_decode_destroy(img_decoder);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_get_image(decoded_img, &width, &height, &colorspace, &data_buffer, &buffer_size);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to get image (%d)", ret);
+               image_util_decode_destroy(img_decoder);
+               image_util_destroy_image(decoded_img);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_destroy_image(decoded_img);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to destroy image (%d)", ret);
+               image_util_decode_destroy(img_decoder);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_decode_destroy(img_decoder);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to destroy decoder");
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = mv_create_source(&mv_src);
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               _E("[FR] Fail to create mv source");
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+       ret = mv_source_fill_by_buffer(mv_src, data_buffer, (unsigned int)buffer_size,
+                                                               (unsigned int)width, (unsigned int)height, MEDIA_VISION_COLORSPACE_RGB888);
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               free(data_buffer);
+               _E("[FR] Fail to fill mv source by buffer");
+               mv_destroy_source(mv_src);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = mv_face_recognition_register(g_face_recog_h, mv_src, file_name);
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               _E("Fail to register the detected face = %d", ret);
+               if (ret == MEDIA_VISION_ERROR_INVALID_PARAMETER)
+                       _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INVALID_PARAMETER)", ret);
+               else if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION)
+                       _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INVALID_OPERATION)", ret);
+               else if (ret == MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)
+                       _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)", ret);
+               else if (ret == MEDIA_VISION_ERROR_OUT_OF_MEMORY)
+                       _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_OUT_OF_MEMORY)", ret);
+               else if (ret == MEDIA_VISION_ERROR_INTERNAL)
+                       _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INTERNAL)", ret);
+
+               mv_destroy_source(mv_src);
+
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       mv_destroy_source(mv_src);
+
+       return MMI_ERROR_NONE;
+}
+
+int register_faces(void)
+{
+       std::vector<FaceInfo> face_list = g_data_manager->get_face_list();
+       for (auto it = face_list.begin(); it != face_list.end(); it++) {
+               std::string face_id = it->id;
+               std::string face_name = it->name;
+               std::string face_file_path = it->file_path;
+
+               register_face_from_file(face_file_path.c_str(), face_name.c_str());
+       }
+
+       return MMI_ERROR_NONE;
+}
+
+int unregister_faces(void)
+{
+       int ret = -1;
+       std::vector<FaceInfo> face_list = g_data_manager->get_face_list();
+       for (auto it = face_list.begin(); it != face_list.end(); it++) {
+               std::string face_name = it->name;
+
+               ret = mv_face_recognition_unregister(g_face_recog_h, face_name.c_str());
+               if (ret != MEDIA_VISION_ERROR_NONE) {
+                       _E("Fail to unregister the detected face = %d", ret);
+                       if (ret == MEDIA_VISION_ERROR_INVALID_PARAMETER)
+                               _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INVALID_PARAMETER)", ret);
+                       else if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION)
+                               _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INVALID_OPERATION)", ret);
+                       else if (ret == MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)
+                               _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)", ret);
+                       else if (ret == MEDIA_VISION_ERROR_OUT_OF_MEMORY)
+                               _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_OUT_OF_MEMORY)", ret);
+                       else if (ret == MEDIA_VISION_ERROR_INTERNAL)
+                               _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INTERNAL)", ret);
+               }
+       }
+
+       _D("[FR] Finish to unregister faces");
+
+       return MMI_ERROR_NONE;
+}
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+       _D("[FR] Node initialize callback is called for %p", instance);
+       if (nullptr == instance) {
+               _E("[FR] Instance is null. %p", instance);
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       mmi_port_instance_h input_port = nullptr;
+       mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_VIDEO, &input_port);
+       if (nullptr == input_port) {
+               _E("[FR] Input port(video) is null. %p", input_port);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       g_input_port_to_instance_map[input_port] = instance;
+       _D("[FR] Input port is found. %p", input_port);
+
+       try {
+               /* TODO: initialize face recognition */
+               int ret = -1;
+               if (nullptr == g_face_recog_h) {
+                       ret = mv_face_recognition_create(&g_face_recog_h);
+                       if (MEDIA_VISION_ERROR_NONE != ret || nullptr == g_face_recog_h) {
+                               _E("[FR] Fail to create face recognition handle");
+                               throw ret;
+                       }
+               }
+
+               ret = mv_face_recognition_prepare(g_face_recog_h);
+               if (MEDIA_VISION_ERROR_NONE != ret) {
+                       _E("[FR] Fail to prepare face recognition handle");
+                       throw ret;
+               }
+
+               /* register facial images */
+               g_data_manager = new DataManager();
+               ret = register_faces();
+       } catch (std::bad_alloc &e) {
+               _E("[FR] Fail to allocate memory. %s", e.what());
+               return MMI_ERROR_OUT_OF_MEMORY;
+       } catch (std::exception &e) {
+               _E("[FR] Fail to initialize face recognition. %s", e.what());
+               return MMI_ERROR_OPERATION_FAILED;
+       } catch (int e) {
+               _E("[FR] Fail to initialize face recognition (%d)", e);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       return MMI_ERROR_NONE;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+       _D("[FR] Node deinitialize callback is called for %p", instance);
+       if (nullptr == instance) {
+               _E("[FR] Instance is null. %p", instance);
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       mmi_port_instance_h input_port = nullptr;
+       mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_VIDEO, &input_port);
+       if (nullptr == input_port) {
+               _E("[FR] Input port is null. %p", input_port);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       g_input_port_to_instance_map.erase(input_port);
+
+       /* TODO: deinitialize face recognition */
+       unregister_faces();
+
+       delete g_data_manager;
+
+       if (nullptr != g_face_recog_h) {
+               mv_face_recognition_destroy(g_face_recog_h);
+               g_face_recog_h = nullptr;
+       }
+
+
+       return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+       _D("[FR] Node attribute set callback is called for %p", instance);
+       return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+       _D("[FR] Node activate callback is called for %p", instance);
+       if (nullptr == instance) {
+               _E("[FR] Instance is null. %p", instance);
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       _D("[FR] Node activate callback is finished for %p", instance);
+
+
+       return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+       _D("[FR] Node deactivate callback is called for %p", instance);
+       if (nullptr == instance) {
+               _E("[FR] Instance is null. %p", instance);
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       _D("[FR] Node deactivate callback is finished for %p", instance);
+
+
+       return MMI_ERROR_NONE;
+}
+
+static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal)
+{
+       _D("[FR] Node signal received callback is called for %p", instance);
+       return MMI_ERROR_NONE;
+}
+
+static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+       _D("[FR] Port output format request callback is called for %p", instance);
+       return MMI_ERROR_NONE;
+}
+
+static void set_float_to_struct(mmi_data_h struct_data, const char *name, float value)
+{
+       mmi_data_h data = nullptr;
+       mmi_data_create_float(value, &data);
+       mmi_data_set_struct_element(struct_data, name, data);
+}
+
+static void set_text_to_struct(mmi_data_h struct_data, const char *name, const char *value)
+{
+       mmi_data_h data = nullptr;
+       mmi_data_create_text(value, &data);
+       mmi_data_set_struct_element(struct_data, name, data);
+}
+
+static void add_array_element(mmi_data_h array_data, const char *id, const char *name, float confidence)
+{
+       mmi_data_h struct_data = nullptr;
+       mmi_data_create_struct(&struct_data);
+       if (nullptr == struct_data) {
+               _E("[FR] Fail to create struct data");
+               return ;
+       }
+
+       set_text_to_struct(struct_data, "id", id);
+       set_text_to_struct(struct_data, "name", name);
+       set_float_to_struct(struct_data, "confidence", confidence);
+
+       mmi_data_add_array_element(array_data, struct_data);
+}
+
+static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+       /* Video input will be received. Recognize input data. */
+       _D("[FR] Port input data callback is called for %p", instance);
+
+       auto iterator = g_input_port_to_instance_map.find(instance);
+       if (g_input_port_to_instance_map.end() == iterator) {
+               _E("[FR] Fail to find proper node instance. %p", instance);
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       auto node_instance = iterator->second;  // a value of a map (first = a key of a map)
+       _D("[FR] Node instance is %p", node_instance);
+
+
+       const void *ptr = nullptr;
+       size_t len = 0;
+       int ret = 0;
+
+       if (mmi_data_get_video(data, &ptr, &len) != MMI_ERROR_NONE) {
+               _E("[FR] Failed to get video data");
+               return MMI_ERROR_INVALID_PARAMETER;
+       }
+
+       _D("[FR] Video input data callback is called: %p %zu", node_instance, len);
+
+       /* TODO: check video format... currently, use fixed values */
+       /* current video format : I420 */
+       unsigned char *data_buffer = NULL;
+       size_t buffer_size = 0;
+       image_util_image_h img_src = NULL;
+       image_util_image_h img_dst = NULL;
+       unsigned int width = FRAME_WIDTH;
+       unsigned int height = FRAME_HEIGHT;
+       transformation_h transform_h;
+
+       ret = image_util_create_image(width, height, IMAGE_UTIL_COLORSPACE_I420, (const unsigned char *)ptr, len, &img_src);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to create image (%d)", ret);
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+
+       ret = image_util_transform_create(&transform_h);
+       if (IMAGE_UTIL_ERROR_NONE != ret) {
+               _E("[FR] Fail to create image util transform handle");
+               image_util_destroy_image(img_src);
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+
+       ret = image_util_transform_set_colorspace(transform_h, IMAGE_UTIL_COLORSPACE_RGB888);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to set colorspace(%d)", ret);
+               image_util_destroy_image(img_src);
+               image_util_transform_destroy(transform_h);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_transform_run2(transform_h, img_src, &img_dst); // img_src: YUYV image, img_dst: RGB888 image
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to transform image (%d)", ret);
+               image_util_destroy_image(img_src);
+               image_util_transform_destroy(transform_h);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       if (nullptr != transform_h) {
+               image_util_transform_destroy(transform_h);
+               transform_h = nullptr;
+       }
+
+       /* get image data */
+       image_util_colorspace_e colorspace;
+       unsigned int converted_width;
+       unsigned int converted_height;
+       unsigned char *image; // RGB888로 변환된 image
+       size_t image_size;
+
+       ret = image_util_get_image(img_dst, &converted_width, &converted_height, &colorspace, &image, &image_size);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to get image (%d)", ret);
+               image_util_destroy_image(img_src);
+               image_util_destroy_image(img_dst);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = image_util_destroy_image(img_src);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to destroy image(src) (%d)", ret);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+       ret = image_util_destroy_image(img_dst);
+       if (ret != IMAGE_UTIL_ERROR_NONE) {
+               _E("[FR] Fail to destroy image(dst) (%d)", ret);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+
+       mv_source_h source;
+       mv_colorspace_e src_color = MEDIA_VISION_COLORSPACE_RGB888;
+
+       /* TODO: recognize face */
+       ret = mv_create_source(&source);
+       if (MEDIA_VISION_ERROR_NONE != ret) {
+               _E("[FR ERROR] Fail to create source");
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+
+       ret = mv_source_fill_by_buffer(source, (unsigned char *)image, image_size, converted_width, converted_height, src_color);
+       if (MEDIA_VISION_ERROR_NONE != ret) {
+               _E("[FR ERROR] Fail to fill source by buffer");
+               mv_destroy_source(source);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       ret = mv_face_recognition_inference(g_face_recog_h, source);
+       if (MEDIA_VISION_ERROR_NONE != ret) {
+               _W("[FR ERROR] Fail to inference. ret(%d)", ret);
+               // mv_destroy_source(source);
+               // return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       const char *out_label = NULL;
+       ret = mv_face_recognition_get_label(g_face_recog_h, &out_label);
+       if (MEDIA_VISION_ERROR_NONE != ret) {
+               _E("[FR ERROR] Fail to get label");
+               mv_destroy_source(source);
+               return MMI_ERROR_OPERATION_FAILED;
+       } else {
+               _I("[FR Result] recognized label(%s)", out_label);
+       }
+
+
+       /* TODO: generate result struct to output port */
+       mmi_port_instance_h output_port_instance = nullptr;
+       mmi_node_instance_find_port(node_instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_MATCHED_FACES, &output_port_instance);
+       if (nullptr == output_port_instance) {
+               _E("[FR] Fail to get matched faces port from node. %p", node_instance);
+               return MMI_ERROR_OPERATION_FAILED;
+       }
+
+       mmi_data_h result_data = nullptr;
+       mmi_data_create_struct(&result_data);
+       if (nullptr == result_data) {
+               _E("[FR] Fail to create result data");
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+       mmi_data_h elem_data = nullptr;
+       mmi_data_create_text(OUTPUT_RESULT_NODE_NAME, &elem_data);
+       mmi_data_set_struct_element(result_data, OUTPUT_RESULT_ELEMENT_NODE, elem_data);
+
+       mmi_data_h result_array_data = nullptr;
+       mmi_data_create_array(&result_array_data);
+       if (nullptr == result_array_data) {
+               _E("[FR] Fail to create result array data");
+               mmi_data_destroy(result_data);
+               return MMI_ERROR_OUT_OF_MEMORY;
+       }
+
+       const float out_confidence = 0.0;
+       size_t num_of_confidences = 0;
+       const float *out_confidences = nullptr;
+       ret = mv_face_recognition_get_confidence(g_face_recog_h, &out_confidences, &num_of_confidences);
+       if (MEDIA_VISION_ERROR_NONE == ret) {
+               _D("[FR] Success to get confidences");
+               const char *label;
+               for (int i = 0 ; i < num_of_confidences ; i++) {
+                       mv_face_recognition_get_label_with_index(g_face_recog_h, i, &label);
+                       _D("[%d] label(%s), confidence(%lf)", i, label, out_confidences[i] + 1.35f);
+                       add_array_element(result_array_data, label, label, out_confidences[i] + 1.35f);
+               }
+       }
+
+       mmi_data_set_struct_element(result_data, OUTPUT_RESULT_ELEMENT_CANDIDATE, result_array_data);
+
+       // TODO: call "mmi_data_add_array_element()" ??
+
+       ret = mmi_port_instance_generate_output(output_port_instance, result_data);
+       if (MMI_ERROR_NONE != ret) {
+               _E("[FR] Fail to generate output. %d", ret);
+       }
+
+       mmi_data_destroy(result_data);
+       mv_destroy_source(source);
+
+       return MMI_ERROR_NONE;
+}
+
+extern "C" {
+
+       EXPORT_API void mmi_plugin_module_get_node_list()
+       {
+               mmi_node_callbacks node_callbacks {
+                       node_initialized_cb,
+                       node_deinitialized_cb,
+                       node_attribute_set_cb,
+                       node_activated_cb,
+                       node_deactivated_cb,
+                       node_signal_received_cb
+               };
+
+               mmi_port_callbacks video_port_callbacks {
+                       port_output_format_requested_cb,
+                       port_input_data_received_cb
+               };
+
+               mmi_port_h fr_video_port = nullptr;
+               mmi_port_create(&fr_video_port);
+               mmi_port_set_name(fr_video_port, INPUT_PORT_NAME_VIDEO);
+               mmi_port_set_type(fr_video_port, MMI_PORT_TYPE_IN);
+               mmi_port_set_data_type(fr_video_port, MMI_DATA_TYPE_VIDEO);
+               mmi_port_set_callbacks(fr_video_port, video_port_callbacks);
+
+               mmi_port_h fr_matched_faces_port = nullptr;
+               mmi_port_create(&fr_matched_faces_port);
+               mmi_port_set_name(fr_matched_faces_port, OUTPUT_PORT_NAME_MATCHED_FACES);
+               mmi_port_set_type(fr_matched_faces_port, MMI_PORT_TYPE_OUT);
+               mmi_port_set_data_type(fr_matched_faces_port, MMI_DATA_TYPE_STRUCT);
+               // mmi_port_set_data_type(fr_matched_faces_port, MMI_DATA_TYPE_BOUNDING_BOX);  // bounding box type?
+
+               /* Callbacks for OUT ports are not needed for now */
+
+               mmi_node_h fr_node = nullptr;
+               mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION, &fr_node);
+               mmi_node_add_port(fr_node, fr_video_port);
+               mmi_node_add_port(fr_node, fr_matched_faces_port);
+               mmi_node_set_callbacks(fr_node, node_callbacks);
+               mmi_node_register(fr_node);
+
+               mmi_node_destroy(fr_node);
+       }
+
+} // extern "C"
diff --git a/plugins/nodes/face-recognition/mv_face_recognition_internal.h b/plugins/nodes/face-recognition/mv_face_recognition_internal.h
new file mode 100644 (file)
index 0000000..bf0f362
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__
+#define __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__
+
+#include <mv_common.h>
+#include <mv_face_recognition_type.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @internal
+ * @brief Gets confidences after a given face image is recognized.
+ * @details Use this function to get the confidences calling @ref mv_face_recognition_inference().
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks The @a num_of_confidences and @a confidences must NOT be released using free()
+ *
+ * @param[in] handle              The handle to the face recognition object.
+ * @param[out] confidences        The array pointer to the confidence table which contains a confidence value of each class.
+ *                                This function returns memory pointer containing actual confidence values to @a confidences.
+ *                                And please note that @a confidences is valid only while handle is alive.
+ * @param[out] num_of_confidences A number of confidences to the classes registered.
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_VISION_ERROR_NONE Successful
+ * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_VISION_ERROR_INVALID_OPERATION Invalid operation
+ *
+ * @pre Request an inference by calling @ref mv_face_recognition_inference()
+ */
+int mv_face_recognition_get_confidence(mv_face_recognition_h handle, const float **confidences,
+                                                                          size_t *num_of_confidences);
+
+/**
+ * @internal
+ * @brief Gets a label name corresponding to a given index.
+ * @details Use this function to get the label name calling @ref mv_face_recognition_get_confidence().
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks The @a num_of_confidences and @a confidences must NOT be released using free()
+ *
+ * @param[in] handle   The handle to the face recognition object.
+ * @param[in] index    A label index pointing to the offset of label file.
+ * @param[out] label   The array pointer for the label name to be stored.
+ *                     This function returns memory pointer containing actual label string to @a label.
+ *                     So do not free @a label. And please note that @a label is valid only while handle is alive.
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_VISION_ERROR_NONE Successful
+ * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_VISION_ERROR_INVALID_OPERATION Invalid operation
+ *
+ * @pre Request an inference by calling @ref mv_face_recognition_get_confidence()
+ */
+int mv_face_recognition_get_label_with_index(mv_face_recognition_h handle, const unsigned int index,
+                                                                                        const char **label);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__ */
diff --git a/plugins/nodes/match/meson.build b/plugins/nodes/match/meson.build
new file mode 100644 (file)
index 0000000..8331078
--- /dev/null
@@ -0,0 +1,27 @@
+mmi_module_match_library_srcs = [
+       'mmi-module-match.cpp',
+       'mmi-module-match-text-candidates.h',
+       'mmi-module-match-text-candidates.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_match_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_match_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_match_library = library('mmi_module_match',
+       mmi_module_match_library_srcs,
+       include_directories : [ mmi_module_match_include_dirs ],
+       dependencies : [mmi_module_match_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/nodes/match/mmi-module-match-text-candidates.cpp b/plugins/nodes/match/mmi-module-match-text-candidates.cpp
new file mode 100644 (file)
index 0000000..db19c9c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-log.h>
+
+#include <algorithm>
+
+#include "mmi-module-match-text-candidates.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-MATCH"
+
+
+using namespace std;
+
+
+TextCandidates::TextCandidates()
+{
+    _I("[MATCH] Construct TextCandidates");
+    m_candidates.clear();
+}
+
+TextCandidates::~TextCandidates()
+{
+    _I("[MATCH] Destruct TextCandidates");
+    m_candidates.clear();
+}
+
+void TextCandidates::append_candidate(const char *candidate)
+{
+    if (nullptr == candidate) {
+        _W("[MATCH] Candidate text is null. Skip appending");
+        return;
+    }
+
+    _I("[MATCH] Append candidate(%s)", candidate);
+    m_candidates.push_back(candidate);
+    _D("[MATCH] The current number of candidates (%zu)", m_candidates.size());
+}
+
+bool TextCandidates::is_exist(const char *candidate)
+{
+    _I("[MATCH] Is candidate exist. candidate(%s)", candidate);
+
+    auto element = std::find(m_candidates.begin(), m_candidates.end(), candidate);
+    if (element == m_candidates.end()) {
+        _E("[MATCH] There no matched command");
+        return false;
+    }
+
+    _I("[MATCH] Success To find candidate(%s)", candidate);
+    return true;
+}
+
+void TextCandidates::clear_candidates()
+{
+    _I("[MATCH] Clear current candidates");
+    m_candidates.clear();
+}
+
diff --git a/plugins/nodes/match/mmi-module-match-text-candidates.h b/plugins/nodes/match/mmi-module-match-text-candidates.h
new file mode 100644 (file)
index 0000000..a9b015f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__
+#define __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__
+
+
+#include <vector>
+#include <string>
+
+
+class TextCandidates {
+public:
+    TextCandidates();
+    ~TextCandidates();
+
+    void append_candidate(const char *candidate);
+    bool is_exist(const char *candidate);
+    void clear_candidates();
+
+private:
+    std::vector<std::string> m_candidates;
+};
+
+#endif /* __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__ */
diff --git a/plugins/nodes/match/mmi-module-match.cpp b/plugins/nodes/match/mmi-module-match.cpp
new file mode 100644 (file)
index 0000000..bd76346
--- /dev/null
@@ -0,0 +1,302 @@
+#include <mmi-attribute.h>
+#include <mmi-node.h>
+#include <mmi-node-logic.h>
+#include <mmi-log.h>
+
+#include <map>
+#include <memory>
+
+#include "mmi-module-match-text-candidates.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-MATCH"
+
+
+static const char *INPUT_PORT_NAME_TEXT = "TEXT";
+static const char *OUTPUT_PORT_NAME_MATCHED_CANDIDATE = "MATCHED_CANDIDATE";
+static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED";
+static const char *ATTRIBUTE_NAME_CANDIDATES = "CANDIDATES";
+
+
+static std::map<mmi_node_instance_h, std::shared_ptr<TextCandidates>> g_node_to_text_candidates_map;
+static std::map<mmi_port_instance_h, mmi_node_instance_h> g_input_port_to_node_map;
+
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+    _D("[MATCH] Node initialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MATCH] Instance is null. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_instance_h input_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port);
+    if (nullptr == input_port) {
+        _E("[MATCH] Fail to get port from node. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    g_input_port_to_node_map[input_port] = instance;
+
+    try {
+        auto text_candidates = std::make_shared<TextCandidates>();
+        g_node_to_text_candidates_map[instance] = text_candidates;
+    } catch (std::bad_alloc &e) {
+        _E("[MATCH] Fail to allocate memory for node instance. %p", instance);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static bool is_node_instance_valid(mmi_node_instance_h instance)
+{
+    if (nullptr == instance) {
+        _E("[MATCH] Node instance is null %p", instance);
+        return false;
+    }
+
+    auto iterator = g_node_to_text_candidates_map.find(instance);
+    if (iterator == g_node_to_text_candidates_map.end()) {
+        _E("[MATCH] No instance %p", instance);
+        return false;
+    }
+
+    return true;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+    _D("[MATCH] Node deinitialize callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_instance_h input_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port);
+    if (nullptr == input_port) {
+        _E("[MATCH] Fail to get port from node. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    g_input_port_to_node_map.erase(input_port);
+    g_node_to_text_candidates_map.erase(instance);
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+    _D("[MATCH] Node attribute set callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    char *name = nullptr;
+    mmi_attribute_get_name(attribute, &name);
+    if (nullptr == name) {
+        _E("[MATCH] Fail to get attribute name");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string attribute_name(name);
+    free(name);
+    name = nullptr;
+
+    if (0 != attribute_name.compare(ATTRIBUTE_NAME_CANDIDATES)) {
+        _E("[MATCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str());
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_primitive_value_h value = nullptr;
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_INT;
+    mmi_attribute_get_value(attribute, &value);
+    mmi_primitive_value_get_type(value, &type);
+    if (MMI_PRIMITIVE_VALUE_TYPE_ARRAY != type) {
+        mmi_primitive_value_destroy(value);
+        _E("[MATCH] Invalid type of attribute : %d", type);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = 0;
+    mmi_primitive_value_get_array_count(value, &count);
+
+    auto text_candidates = g_node_to_text_candidates_map[instance];
+    text_candidates->clear_candidates();
+
+    for (size_t i = 0; i < count; i++) {
+        mmi_primitive_value_h element = nullptr;
+        mmi_primitive_value_get_array_element(value, i, &element);
+
+        mmi_primitive_value_type_e element_type;
+        mmi_primitive_value_get_type(element, &element_type);
+
+        if (MMI_PRIMITIVE_VALUE_TYPE_STRING != element_type) {
+            mmi_primitive_value_destroy(value);
+            _E("[MATCH] Invalid element type of attribute : %d", element_type);
+            return MMI_ERROR_INVALID_PARAMETER;
+        }
+
+        const char *element_value = nullptr;
+        if (MMI_ERROR_NONE == mmi_primitive_value_get_string(element, &element_value)) {
+            _D("[MATCH] Attribute value : %zu %s", i, element_value);
+            text_candidates->append_candidate(element_value);
+        }
+    }
+    mmi_primitive_value_destroy(value);
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+    _D("[MATCH] Node activate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+    _D("[MATCH] Node deactivate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal)
+{
+    _D("[MATCH] Node signal received callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+    _D("[MATCH] Port output format request callback is called for %p", instance);
+
+    return MMI_ERROR_NONE;
+}
+
+static int generate_output(mmi_node_instance_h instance, const char *output_name, mmi_data_h data)
+{
+    mmi_port_instance_h output_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, output_name, &output_port);
+    if (nullptr == output_port) {
+        _E("[MATCH] Fail to get output port. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    int ret = mmi_port_instance_generate_output(output_port, data);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[MATCH] Fail to generate output. error(%d / %s)", ret, get_error_message(ret));
+        return ret;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+    _D("[MATCH] Text input data callback is called: %p", instance);
+    auto iterator = g_input_port_to_node_map.find(instance);
+    if (iterator == g_input_port_to_node_map.end()) {
+        _E("[MATCH] Fail to find proper node instance. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto node_instance = iterator->second;
+    if (false == is_node_instance_valid(node_instance)) {
+        _E("[MATCH] Node instance is not valid. %p", node_instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const char *text = nullptr;
+    mmi_data_get_text(data, &text);
+    if (nullptr == text) {
+        _E("[MATCH] Fail to get text from data");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = MMI_ERROR_NONE;
+    auto text_candidates = g_node_to_text_candidates_map[node_instance];
+    if (text_candidates->is_exist(text)) {
+        ret = generate_output(node_instance, OUTPUT_PORT_NAME_MATCHED_CANDIDATE, data);
+    } else {
+        ret = generate_output(node_instance, OUTPUT_PORT_NAME_REJECTED, data);
+    }
+
+    if (MMI_ERROR_NONE != ret) {
+        _E("[MATCH] Fail to generate output");
+        return ret;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_node_list()
+    {
+        mmi_node_callbacks node_callbacks {
+            node_initialized_cb,
+            node_deinitialized_cb,
+            node_attribute_set_cb,
+            node_activated_cb,
+            node_deactivated_cb,
+            node_signal_received_cb
+        };
+
+        mmi_port_callbacks text_port_callbacks {
+            port_output_format_requested_cb,
+            port_input_data_received_cb
+        };
+
+        mmi_port_h match_text_port = nullptr;
+        mmi_port_create(&match_text_port);
+        mmi_port_set_name(match_text_port, INPUT_PORT_NAME_TEXT);
+        mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN);
+        mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT);
+        mmi_port_set_callbacks(match_text_port, text_port_callbacks);
+
+        mmi_port_h match_candidate_port = nullptr;
+        mmi_port_create(&match_candidate_port);
+        mmi_port_set_name(match_candidate_port, OUTPUT_PORT_NAME_MATCHED_CANDIDATE);
+        mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT);
+
+        mmi_port_h match_rejected_port = nullptr;
+        mmi_port_create(&match_rejected_port);
+        mmi_port_set_name(match_rejected_port, OUTPUT_PORT_NAME_REJECTED);
+        mmi_port_set_type(match_rejected_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(match_rejected_port, MMI_DATA_TYPE_TEXT);
+        /* Callbacks for OUT ports are not needed for now */
+
+        mmi_node_h match_node = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &match_node);
+        mmi_node_add_port(match_node, match_text_port);
+        mmi_node_add_port(match_node, match_candidate_port);
+        mmi_node_add_port(match_node, match_rejected_port);
+        mmi_node_set_callbacks(match_node, node_callbacks);
+        mmi_node_register(match_node);
+
+        mmi_node_destroy(match_node);
+    }
+
+} // extern "C"
diff --git a/plugins/nodes/meson.build b/plugins/nodes/meson.build
new file mode 100755 (executable)
index 0000000..9e13383
--- /dev/null
@@ -0,0 +1,6 @@
+subdir('mic')
+subdir('camera')
+subdir('match')
+subdir('regex-match')
+subdir('face-recognition')
+subdir('voice-touch')
diff --git a/plugins/nodes/mic/meson.build b/plugins/nodes/mic/meson.build
new file mode 100644 (file)
index 0000000..b01fe78
--- /dev/null
@@ -0,0 +1,31 @@
+mmi_module_mic_library_srcs = [
+       'mmi-module-mic.cpp',
+       'mmi-module-mic-recorder.cpp',
+       'mmi-module-mic-recorder.h',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+audio_dep = dependency('capi-media-audio-io', method : 'pkg-config')
+sound_manager_dep = dependency('capi-media-sound-manager', method : 'pkg-config')
+
+mmi_module_mic_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       audio_dep,
+       sound_manager_dep,
+       ]
+
+mmi_module_mic_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_mic_library = library('mmi_module_mic',
+       mmi_module_mic_library_srcs,
+       include_directories : [ mmi_module_mic_include_dirs ],
+       dependencies : [mmi_module_mic_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/nodes/mic/mmi-module-mic-recorder.cpp b/plugins/nodes/mic/mmi-module-mic-recorder.cpp
new file mode 100644 (file)
index 0000000..800bf7a
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-module-mic-recorder.h>
+#include <mmi-node.h>
+#include <mmi-log.h>
+
+#include "mmi-module-mic-recorder.h"
+
+#define BUFFER_LENGTH 640
+
+Recorder::Recorder()
+{
+}
+
+Recorder::~Recorder()
+{
+}
+
+int Recorder::initialize()
+{
+    if (0 != sound_manager_create_stream_information(SOUND_STREAM_TYPE_VOICE_RECOGNITION, NULL, NULL, &m_stream_info)) {
+        _E("[Recorder] Fail to create stream info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+    
+    int ret = AUDIO_IO_ERROR_NONE;
+    ret = audio_in_create(16000, AUDIO_CHANNEL_MONO, AUDIO_SAMPLE_TYPE_S16_LE, &m_audio_in);
+    if (ret != AUDIO_IO_ERROR_NONE) {
+        _E("[Recorder] audio_in_create() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int Recorder::deinitialize()
+{
+    int ret = AUDIO_IO_ERROR_NONE;
+    ret = audio_in_destroy(m_audio_in);
+    if (AUDIO_IO_ERROR_NONE != ret) {
+        _E("[Recorder] audio_in_destroy() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (0 != sound_manager_destroy_stream_information(m_stream_info)) {
+        _E("[Recorder] Fail to destroy stream info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int Recorder::start()
+{
+    _D("Recorder::start()");
+
+    if (nullptr == m_audio_callback) {
+        _E("[Recorder] callback not set");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    int ret = AUDIO_IO_ERROR_NONE;
+
+    ret = audio_in_set_sound_stream_info(m_audio_in, m_stream_info);
+    if (AUDIO_IO_ERROR_NONE != ret) {
+        _E("[Recorder] audio_in_set_sound_stream_info() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    ret = audio_in_prepare(m_audio_in);
+    if (AUDIO_IO_ERROR_NONE != ret) {
+        _E("[Recorder] audio_in_prepare() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (nullptr != m_timer) {
+        ecore_timer_del(m_timer);
+        m_timer = nullptr;
+    }
+
+    m_timer = ecore_timer_add(0.0, audio_in_callback, this);
+    if (nullptr == m_timer) {
+        _E("[Recorder] ecore_timer_add() failed");
+        stop();
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int Recorder::stop()
+{
+    _D("Recorder::stop()");
+
+    int ret = AUDIO_IO_ERROR_NONE;
+
+    ret = audio_in_flush(m_audio_in);
+    if (AUDIO_IO_ERROR_NONE != ret) {
+        _E("[Recorder] audio_in_flush() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    ret = audio_in_unprepare(m_audio_in);
+    if (AUDIO_IO_ERROR_NONE != ret) {
+        _E("[Recorder] audio_in_unprepare() failed with error code %d", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (nullptr != m_timer) {
+        ecore_timer_del(m_timer);
+        m_timer = nullptr;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+void Recorder::set_feed_audio_callback(audio_data_callback callback)
+{
+    m_audio_callback = callback;
+}
+
+void Recorder::set_node_instance(mmi_node_instance_h node_instance)
+{
+    m_node_instance = node_instance;
+}
+
+Eina_Bool Recorder::audio_in_callback(void *user_data)
+{
+    Recorder *recorder = static_cast<Recorder*>(user_data);
+    int read_byte = -1;
+    static char buffer[BUFFER_LENGTH];
+
+    read_byte = audio_in_read(recorder->m_audio_in, buffer, BUFFER_LENGTH);
+
+    if (0 > read_byte) {
+        _E("audio_in_read() failed with error code %d", read_byte);
+        return EINA_FALSE;
+    }
+
+    if (recorder->m_audio_callback) {
+        recorder->m_audio_callback(buffer, (size_t)read_byte, recorder->m_node_instance);
+    }
+
+    return EINA_TRUE;
+}
diff --git a/plugins/nodes/mic/mmi-module-mic-recorder.h b/plugins/nodes/mic/mmi-module-mic-recorder.h
new file mode 100644 (file)
index 0000000..44fd953
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <functional>
+
+#include <Ecore.h>
+#include <sound_manager.h>
+#include <audio_io.h>
+
+#include <mmi-node.h>
+
+using audio_data_callback = std::function<void(void *, size_t, void *)>;
+
+class Recorder {
+public:
+    Recorder();
+    ~Recorder();
+
+    int initialize();
+    int deinitialize();
+    int start();
+    int stop();
+
+    void set_feed_audio_callback(audio_data_callback callback);
+    void set_node_instance(mmi_node_instance_h node_instance);
+
+    static Eina_Bool audio_in_callback(void *data);
+
+private:
+    audio_in_h m_audio_in{nullptr};
+    sound_stream_info_h m_stream_info{nullptr};
+    audio_data_callback m_audio_callback{nullptr};
+    mmi_node_instance_h m_node_instance{nullptr};
+
+    Ecore_Timer *m_timer{nullptr};
+};
diff --git a/plugins/nodes/mic/mmi-module-mic.cpp b/plugins/nodes/mic/mmi-module-mic.cpp
new file mode 100644 (file)
index 0000000..a81ae8a
--- /dev/null
@@ -0,0 +1,210 @@
+#include <mmi.h>
+#include <mmi-plugin-storage.h>
+#include <mmi-node.h>
+#include <mmi-log.h>
+
+#include <string>
+#include <map>
+
+#include "mmi-module-mic-recorder.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-MIC"
+
+static const char *OUTPUT_PORT_NAME_AUDIO = "AUDIO";
+
+static std::map<mmi_node_instance_h, Recorder *> g_recorder_map;
+
+static char pcm_dump_file_name[128] = {'\0',};
+static FILE* pcm_dump_fp = nullptr;
+static int g_cnt = 0;
+static size_t buffer_size = 0;
+
+void feed_audio_callback(void *data, size_t len, void *user_data)
+{
+    _D("[MIC] feed_audio_callback is called, size: %zu", len);
+
+    mmi_node_instance_h instance = (mmi_node_instance_h)user_data;
+
+    mmi_port_instance_h port_instance = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_AUDIO, &port_instance);
+
+    mmi_data_h output_data = nullptr;
+
+
+    if (pcm_dump_fp) {
+        buffer_size += len;
+        fwrite(data, 1, len, pcm_dump_fp);
+    }
+
+    mmi_data_create_audio(data, len, &output_data);
+
+    mmi_port_instance_generate_output(port_instance, output_data);
+
+    mmi_data_destroy(output_data);
+}
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+    _D("[MIC] Node initialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MIC] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    Recorder *recorder = new Recorder();
+    recorder->initialize();
+    recorder->set_node_instance(instance);
+    recorder->set_feed_audio_callback(feed_audio_callback);
+    g_recorder_map.insert(std::pair<mmi_node_instance_h, Recorder *>(instance, recorder));
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+    _D("[MIC] Node deinitialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MIC] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Recorder *recorder = g_recorder_map[instance];
+        recorder->deinitialize();
+        g_recorder_map.erase(instance);
+        delete recorder;        
+    } catch (std::out_of_range &e) {
+        _E("[MIC] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+    _D("Node attribute set callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MIC] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (nullptr == attribute) {
+        _E("[MIC] attribute is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    // TODO: set attribute "UNFOCUSED_ONLY"
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+    _D("[MIC] Node activate callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MIC] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Recorder *recorder = g_recorder_map[instance];
+        recorder->start();
+    } catch (std::out_of_range &e) {
+        _E("[MIC] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    snprintf(pcm_dump_file_name, sizeof(pcm_dump_file_name), "/tmp/mmi_mic_node_%d_%d", getpid(), g_cnt);
+    g_cnt++;
+
+    pcm_dump_fp = fopen(pcm_dump_file_name, "wb+x");
+    if (!pcm_dump_fp) {
+        _E("[SPEAKER RECOGNITION] File not found!");
+        return -1;
+    }
+    buffer_size = 0;
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+    _D("[MIC] Node deactivate callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[MIC] node instance is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        Recorder *recorder = g_recorder_map[instance];
+        recorder->stop();
+    } catch (std::out_of_range &e) {
+        _E("[MIC] failed to find recorder instance");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (pcm_dump_fp)
+        fclose(pcm_dump_fp);
+    pcm_dump_fp = nullptr;
+    buffer_size = 0;
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal)
+{
+    _D("Node signal received callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) {
+    _D("Port output format request callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+// TODO: It must be determined how to receive input data from the client 
+static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) {
+    _D("Port input data callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_node_list()
+    {
+        mmi_node_callbacks node_callbacks {
+            node_initialized_cb,
+            node_deinitialized_cb,
+            node_attribute_set_cb,
+            node_activated_cb,
+            node_deactivated_cb,
+            node_signal_received_cb
+        };
+
+        mmi_port_callbacks audio_port_callbacks {
+            port_output_format_requested_cb,
+            port_input_data_received_cb
+        };
+
+        mmi_port_h mic_audio_port = nullptr;
+        mmi_port_create(&mic_audio_port);
+        mmi_port_set_name(mic_audio_port, "AUDIO");
+        mmi_port_set_type(mic_audio_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(mic_audio_port, MMI_DATA_TYPE_AUDIO);
+        mmi_port_set_callbacks(mic_audio_port, audio_port_callbacks);
+
+        mmi_node_h mic_node = nullptr;
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &mic_node);
+        mmi_node_add_port(mic_node, mic_audio_port);
+        mmi_node_set_callbacks(mic_node, node_callbacks);
+
+        mmi_node_register(mic_node);
+
+        mmi_node_destroy(mic_node);
+    }
+
+} // extern "C"
diff --git a/plugins/nodes/regex-match/meson.build b/plugins/nodes/regex-match/meson.build
new file mode 100644 (file)
index 0000000..f214608
--- /dev/null
@@ -0,0 +1,27 @@
+mmi_module_regex_match_library_srcs = [
+       'mmi-module-regex-match.cpp',
+       'mmi-module-regex-match-regex-candidates.h',
+       'mmi-module-regex-match-regex-candidates.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_regex_match_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_regex_match_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_regex_match_library = library('mmi_module_regex_match',
+       mmi_module_regex_match_library_srcs,
+       include_directories : [ mmi_module_regex_match_include_dirs ],
+       dependencies : [mmi_module_regex_match_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp
new file mode 100644 (file)
index 0000000..ada0376
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-log.h>
+
+#include "mmi-module-regex-match-regex-candidates.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-REGEX-MATCH"
+
+
+RegExCandidates::RegExCandidates()
+{
+    _I("[REGEX MATCH] Construct RegExCandidates");
+    m_candidates.clear();
+}
+
+RegExCandidates::~RegExCandidates()
+{
+    _I("[REGEX MATCH] Destruct RegExCandidates");
+    m_candidates.clear();
+}
+
+void RegExCandidates::append_candidate(const char *regex_name, const char *regex_pattern)
+{
+    if (nullptr == regex_name ||nullptr == regex_pattern) {
+        _W("[REGEX MATCH] Regex name or pattern is null. Skip appending");
+        return;
+    }
+
+    _I("[REGEX MATCH] Append candidate(%s/%s)", regex_pattern, regex_name);
+    m_candidates.push_back(std::make_pair(std::regex(regex_pattern), std::string(regex_name)));
+    _D("[REGEX MATCH] The current number of candidates (%zu)", m_candidates.size());
+}
+
+std::optional<std::string> RegExCandidates::find_matched_candidate(const char *text)
+{
+    if (nullptr == text) {
+        _E("[REGEX MATCH] text is null.");
+        return std::nullopt;
+    }
+
+    _I("[REGEX MATCH] Find matched candidate. text(%s)", text);
+
+    try {
+        for (auto &candidate : m_candidates) {
+            auto &regex = candidate.first;
+            if (std::regex_match(text, regex)) {
+                _I("Result : text(%s), result(%s)", text, candidate.second.c_str());
+                return candidate.second;
+            }
+        }
+    } catch (std::exception &e) {
+        _E("Exception occurs. (%s)", e.what());
+        return std::nullopt;
+    }
+
+    _E("[REGEX MATCH] There no matched candidate");
+    return std::nullopt;
+}
+
+void RegExCandidates::clear_candidates()
+{
+    _I("[REGEX MATCH] Clear current candidates");
+    m_candidates.clear();
+}
+
diff --git a/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h
new file mode 100644 (file)
index 0000000..9eed260
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__
+#define __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__
+
+
+#include <vector>
+#include <string>
+#include <regex>
+#include <optional>
+
+class RegExCandidates {
+public:
+    RegExCandidates();
+    ~RegExCandidates();
+
+    void append_candidate(const char *regex_name, const char *regex_pattern);
+    std::optional<std::string> find_matched_candidate(const char *text);
+    void clear_candidates();
+
+private:
+    std::vector<std::pair<std::regex, std::string>> m_candidates;
+};
+
+#endif /* __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__ */
diff --git a/plugins/nodes/regex-match/mmi-module-regex-match.cpp b/plugins/nodes/regex-match/mmi-module-regex-match.cpp
new file mode 100644 (file)
index 0000000..e110148
--- /dev/null
@@ -0,0 +1,308 @@
+#include <mmi-attribute.h>
+#include <mmi-node.h>
+#include <mmi-node-logic.h>
+#include <mmi-log.h>
+
+#include <map>
+#include <memory>
+
+#include "mmi-module-regex-match-regex-candidates.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-REGEX-MATCH"
+
+
+static const char *INPUT_PORT_NAME_TEXT = "TEXT";
+static const char *OUTPUT_PORT_NAME_MATCHED_CANDIDATE = "MATCHED_CANDIDATE";
+static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED";
+static const char *ATTRIBUTE_NAME_CANDIDATES = "CANDIDATES";
+
+
+static std::map<mmi_node_instance_h, std::shared_ptr<RegExCandidates>> g_node_to_regex_candidates_map;
+static std::map<mmi_port_instance_h, mmi_node_instance_h> g_input_port_to_node_map;
+
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+    _D("[REGEX MATCH] Node initialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[REGEX MATCH] Instance is null. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_instance_h input_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port);
+    if (nullptr == input_port) {
+        _E("[REGEX MATCH] Fail to get port from node. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    g_input_port_to_node_map[input_port] = instance;
+
+    try {
+        auto text_candidates = std::make_shared<RegExCandidates>();
+        g_node_to_regex_candidates_map[instance] = text_candidates;
+    } catch (std::bad_alloc &e) {
+        _E("[REGEX MATCH] Fail to allocate memory for node instance. %p", instance);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static bool is_node_instance_valid(mmi_node_instance_h instance)
+{
+    if (nullptr == instance) {
+        _E("[REGEX MATCH] Node instance is null %p", instance);
+        return false;
+    }
+
+    auto iterator = g_node_to_regex_candidates_map.find(instance);
+    if (iterator == g_node_to_regex_candidates_map.end()) {
+        _E("[REGEX MATCH] No instance %p", instance);
+        return false;
+    }
+
+    return true;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+    _D("[REGEX MATCH] Node deinitialize callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[REGEX MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_instance_h input_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port);
+    if (nullptr == input_port) {
+        _E("[REGEX MATCH] Fail to get port from node. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    g_input_port_to_node_map.erase(input_port);
+    g_node_to_regex_candidates_map.erase(instance);
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+    _D("[REGEX MATCH] Node attribute set callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[REGEX MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    char *name = nullptr;
+    mmi_attribute_get_name(attribute, &name);
+    if (nullptr == name) {
+        _E("[REGEX MATCH] Fail to get attribute name");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string attribute_name(name);
+    free(name);
+    name = nullptr;
+
+    if (0 != attribute_name.compare(ATTRIBUTE_NAME_CANDIDATES)) {
+        _E("[REGEX MATCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str());
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_primitive_value_h value = nullptr;
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_INT;
+    mmi_attribute_get_value(attribute, &value);
+    mmi_primitive_value_get_type(value, &type);
+    if (MMI_PRIMITIVE_VALUE_TYPE_STRING != type) {
+        mmi_primitive_value_destroy(value);
+        _E("[REGEX MATCH] Invalid type of attribute : %d", type);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    // TODO: Check the efficient way to set attribute for regex candidates.
+    size_t count = 0;
+    mmi_primitive_value_get_array_count(value, &count);
+
+    auto regex_candidates = g_node_to_regex_candidates_map[instance];
+    regex_candidates->clear_candidates();
+
+    for (size_t i = 0; i < count; i += 2) {
+        mmi_primitive_value_h regex_name = nullptr;
+        mmi_primitive_value_get_array_element(value, i, &regex_name);
+
+        mmi_primitive_value_h regex_pattern = nullptr;
+        mmi_primitive_value_get_array_element(value, i + 1, &regex_pattern);
+
+        const char *regex_name_value = nullptr;
+        mmi_primitive_value_get_string(regex_name, &regex_name_value);
+
+        const char *regex_pattern_value = nullptr;
+        mmi_primitive_value_get_string(regex_pattern, &regex_pattern_value);
+
+        if (nullptr != regex_name_value && nullptr != regex_pattern_value) {
+            _D("[REGEX MATCH] Attribute value : %zu, %s, %s", i, regex_name_value, regex_pattern_value);
+            regex_candidates->append_candidate(regex_name_value, regex_pattern_value);
+        } else {
+            _W("[REGEX MATCH] Fail to get string. Please check error. index(%zu)", i);
+        }
+    }
+    mmi_primitive_value_destroy(value);
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+    _D("[REGEX MATCH] Node activate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[REGEX MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+    _D("[REGEX MATCH] Node deactivate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[REGEX MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal)
+{
+    _D("[REGEX MATCH] Node signal received callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[REGEX MATCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+    _D("[REGEX MATCH] Port output format request callback is called for %p", instance);
+
+    return MMI_ERROR_NONE;
+}
+
+static int generate_output(mmi_node_instance_h instance, const char *output_name, mmi_data_h data)
+{
+    mmi_port_instance_h output_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, output_name, &output_port);
+    if (nullptr == output_port) {
+        _E("[REGEX MATCH] Fail to get output port. %p", instance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    int ret = mmi_port_instance_generate_output(output_port, data);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[REGEX MATCH] Fail to generate output. error(%d / %s)", ret, get_error_message(ret));
+        return ret;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+    _D("[REGEX MATCH] Text input data callback is called: %p", instance);
+    auto iterator = g_input_port_to_node_map.find(instance);
+    if (iterator == g_input_port_to_node_map.end()) {
+        _E("[REGEX MATCH] Fail to find proper node instance. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto node_instance = iterator->second;
+    if (false == is_node_instance_valid(node_instance)) {
+        _E("[REGEX MATCH] Node instance is not valid. %p", node_instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const char *text = nullptr;
+    mmi_data_get_text(data, &text);
+    if (nullptr == text) {
+        _E("[REGEX MATCH] Fail to get text from data");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = MMI_ERROR_NONE;
+    auto text_candidates = g_node_to_regex_candidates_map[node_instance];
+    auto result = text_candidates->find_matched_candidate(text);
+    if (result) {
+        mmi_data_h matched_candidate = nullptr;
+        mmi_data_create_text(result->c_str(), &matched_candidate);
+        ret = generate_output(node_instance, OUTPUT_PORT_NAME_MATCHED_CANDIDATE, matched_candidate);
+        mmi_data_destroy(matched_candidate);
+    } else {
+        ret = generate_output(node_instance, OUTPUT_PORT_NAME_REJECTED, data);
+    }
+
+    if (MMI_ERROR_NONE != ret) {
+        _E("[REGEX MATCH] Fail to generate output");
+        return ret;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_node_list()
+    {
+        mmi_node_callbacks node_callbacks {
+            node_initialized_cb,
+            node_deinitialized_cb,
+            node_attribute_set_cb,
+            node_activated_cb,
+            node_deactivated_cb,
+            node_signal_received_cb
+        };
+
+        mmi_port_callbacks text_port_callbacks {
+            port_output_format_requested_cb,
+            port_input_data_received_cb
+        };
+
+        mmi_port_h match_text_port = nullptr;
+        mmi_port_create(&match_text_port);
+        mmi_port_set_name(match_text_port, INPUT_PORT_NAME_TEXT);
+        mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN);
+        mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT);
+        mmi_port_set_callbacks(match_text_port, text_port_callbacks);
+
+        mmi_port_h match_candidate_port = nullptr;
+        mmi_port_create(&match_candidate_port);
+        mmi_port_set_name(match_candidate_port, OUTPUT_PORT_NAME_MATCHED_CANDIDATE);
+        mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT);
+
+        mmi_port_h match_rejected_port = nullptr;
+        mmi_port_create(&match_rejected_port);
+        mmi_port_set_name(match_rejected_port, OUTPUT_PORT_NAME_REJECTED);
+        mmi_port_set_type(match_rejected_port, MMI_PORT_TYPE_OUT);
+        mmi_port_set_data_type(match_rejected_port, MMI_DATA_TYPE_TEXT);
+        /* Callbacks for OUT ports are not needed for now */
+
+        mmi_node_h match_node = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH, &match_node);
+        mmi_node_add_port(match_node, match_text_port);
+        mmi_node_add_port(match_node, match_candidate_port);
+        mmi_node_add_port(match_node, match_rejected_port);
+        mmi_node_set_callbacks(match_node, node_callbacks);
+        mmi_node_register(match_node);
+
+        mmi_node_destroy(match_node);
+    }
+
+} // extern "C"
diff --git a/plugins/nodes/voice-touch/meson.build b/plugins/nodes/voice-touch/meson.build
new file mode 100644 (file)
index 0000000..1564037
--- /dev/null
@@ -0,0 +1,36 @@
+mmi_module_node_voice_touch_library_srcs = [
+       'mmi-module-voice-touch.cpp',
+       'mmi-module-voice-touch-common.h',
+       'mmi-module-voice-touch-candidates.h',
+       'mmi-module-voice-touch-candidates.cpp',
+       'mmi-module-voice-touch-string-utility.h',
+       'mmi-module-voice-touch-string-utility.cpp',
+       'mmi-module-voice-touch-screen-info.h',
+       'mmi-module-voice-touch-screen-info.cpp',
+       'mmi-module-voice-touch-data-utility.h',
+       'mmi-module-voice-touch-data-utility.cpp',
+       'mmi-module-voice-touch-primitive-utility.h',
+       'mmi-module-voice-touch-primitive-utility.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_node_voice_touch_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_node_voice_touch_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_node_voice_touch_library = library('mmi_module_node_voice_touch',
+       mmi_module_node_voice_touch_library_srcs,
+       include_directories : [ mmi_module_node_voice_touch_include_dirs ],
+       dependencies : [mmi_module_node_voice_touch_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp
new file mode 100644 (file)
index 0000000..07e08bc
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-log.h>
+
+#include <algorithm>
+#include <numeric>
+
+#include "mmi-module-voice-touch-candidates.h"
+#include "mmi-module-voice-touch-string-utility.h"
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+
+static constexpr double WORD_MATCHING_RATE = 0.3;
+static constexpr double CHAR_MATCHING_RATE = 0.35;
+static constexpr size_t MIN_WORD_SIZE = 3;
+
+
+VoiceTouchCandidates::VoiceTouchCandidates()
+{
+    _I("[VOICE TOUCH] Construct VoiceTouchCandidates");
+    m_candidates.clear();
+}
+
+VoiceTouchCandidates::~VoiceTouchCandidates()
+{
+    _I("[VOICE TOUCH] Destruct VoiceTouchCandidates");
+    m_candidates.clear();
+}
+
+void VoiceTouchCandidates::set_candidates(const std::vector<voice_touch_candidate_s> &candidates)
+{
+    _I("[VOICE TOUCH] Set candidates(%zu)", candidates.size());
+    m_candidates.clear();
+    m_candidates = candidates;
+    _D("[VOICE TOUCH] The current number of candidates (%zu)", m_candidates.size());
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate(size_t index)
+{
+    _I("[VOICE TOUCH] Find matched candidate. index(%zu)", index);
+    std::optional<voice_touch_candidate_s> result = find_matched_candidate_by_index(index);
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate(%zu)", index);
+        return result;
+    }
+
+    return std::nullopt;
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate_by_index(size_t index)
+{
+    _I("[VOICE TOUCH] Find matched candidate by index (%zu)", index);
+
+    if (index > m_candidates.size()) {
+        _E("[VOICE TOUCH] Invalid index. %zu", index);
+        return std::nullopt;
+    }
+
+    for (auto &candidate : m_candidates) {
+        if (candidate.index == index) {
+            _I("[VOICE TOUCH] result candidate(%s)", candidate.label.c_str());
+            return candidate;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate(const char *text)
+{
+    _I("[VOICE TOUCH] Find matched candidate. text(%s)", text);
+    auto lower_cased_text = StringUtility::make_lower_case(std::string(text));
+    auto splited_text = StringUtility::split_text(lower_cased_text, ' ');
+
+    std::optional<voice_touch_candidate_s> result;
+    result = find_matched_candidate_by_text(splited_text);
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text);
+        return result;
+    }
+
+    result = find_matched_candidate_by_word(splited_text);
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text);
+        return result;
+    }
+
+    result = find_matched_candidate_by_char(splited_text);
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text);
+        return result;
+    }
+
+    _I("[VOICE TOUCH] There is no matched candidate by text. Try to find by index. text(%s)", text);
+    size_t index = 0;
+    try {
+        index = std::stoull(lower_cased_text);
+    } catch (std::exception &e) {
+        _E("[VOICE TOUCH] Fail string to int conversion. (%s)", e.what());
+        return std::nullopt;
+    }
+
+    result = find_matched_candidate_by_index(index);
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate(%zu)", index);
+        return result;
+    }
+
+    return std::nullopt;
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate_by_text(const std::vector<std::string> &splited_text)
+{
+    _I("[VOICE TOUCH] Find matched candidate by full text.");
+
+    for (auto &candidate : m_candidates) {
+        std::string label = StringUtility::make_lower_case(candidate.label);
+
+        bool is_valid = true;
+        for (auto &word : splited_text) {
+            if (label.find(word) == std::string::npos) {
+                is_valid = false;
+                break;
+            }
+        }
+
+        if (is_valid) {
+            _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", candidate.label.c_str());
+            return candidate;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate_by_word(const std::vector<std::string> &splited_text)
+{
+    _I("[VOICE TOUCH] Find matched candidate by word.");
+
+    size_t max_matching_word_num = static_cast<size_t>(splited_text.size() * WORD_MATCHING_RATE);
+    if (0 == max_matching_word_num) {
+        max_matching_word_num = 1;
+    }
+    _I("[VOICE TOUCH] Threshold of matching word number (%zu)", max_matching_word_num);
+
+    std::optional<voice_touch_candidate_s> result;
+    for (auto &candidate : m_candidates) {
+        std::string label = StringUtility::make_lower_case(candidate.label);
+
+        size_t matching_word_num = 0;
+        for (auto &word : splited_text) {
+            if (label.find(word) != std::string::npos) {
+                matching_word_num++;
+            }
+        }
+
+        if (matching_word_num >= max_matching_word_num) {
+            max_matching_word_num = matching_word_num;
+            result = candidate;
+        }
+    }
+
+    if (result) {
+        _I("[VOICE TOUCH] Success To find voice touch candidate which has highest matching rate(%s)", result->label.c_str());
+        return result;
+    }
+
+    return std::nullopt;
+}
+
+std::optional<voice_touch_candidate_s> VoiceTouchCandidates::find_matched_candidate_by_char(const std::vector<std::string> &splited_text)
+{
+    _I("[VOICE TOUCH] Find matched candidate by character.");
+
+    auto filter_splited_text = [](std::string x) -> bool {
+        return x.size() >= MIN_WORD_SIZE;
+    };
+    std::vector<std::string> filtered_words;
+    std::copy_if(splited_text.begin(), splited_text.end(), std::back_inserter(filtered_words), filter_splited_text);
+
+    // TODO: Current algorithm doesn't consider the original order of the words. It can affect the total score.
+    std::optional<voice_touch_candidate_s> result;
+    size_t result_score = std::numeric_limits<size_t>::max();
+    for (auto &candidate : m_candidates) {
+        auto candidate_label = StringUtility::make_lower_case(candidate.label);
+        auto candidate_words = StringUtility::split_text(candidate_label, ' ');
+
+        std::vector<std::string> filtered_candidate_words;
+        std::copy_if(candidate_words.begin(), candidate_words.end(), std::back_inserter(filtered_candidate_words), filter_splited_text);
+        _D("[VOICE TOUCH] candidate(%s)", candidate_label.c_str());
+
+        size_t score = 0;
+        for (auto &word : filtered_words) {
+            const size_t MIN_THRESHOLD = static_cast<size_t>(word.size() * CHAR_MATCHING_RATE) + 1;
+            size_t min_score = MIN_THRESHOLD;
+            _D("[VOICE TOUCH] Minimum matching character number (%zu)", MIN_THRESHOLD);
+
+            for (auto &candidate_word : filtered_candidate_words) {
+                size_t edit_distance = calcualte_edit_distance(candidate_word, word);
+
+                if (min_score > edit_distance) {
+                    min_score = edit_distance;
+                }
+            }
+
+            score += (min_score < MIN_THRESHOLD ? min_score : word.size());
+            _D("[VOICE TOUCH] word(%s), label word(%s), minscore(%zu), score(%zu)", word.c_str(), candidate_label.c_str(), min_score, score);
+        }
+
+        _D("[VOICE TOUCH] result score(%zu), score(%zu)", result_score, score);
+        if (result_score > score) {
+            result_score = score;
+            result = candidate;
+        }
+    }
+
+    auto accumulate_size = [](size_t sum, std::string word) -> size_t
+    {
+        return sum + word.size();
+    };
+    size_t threshold = std::accumulate(filtered_words.begin(), filtered_words.end(), static_cast<size_t>(0), accumulate_size);
+    if (threshold > result_score) {
+        _I("[VOICE TOUCH] result candidate(%s)", result->label.c_str());
+        return result;
+    }
+
+    return std::nullopt;
+}
+
+size_t VoiceTouchCandidates::calcualte_edit_distance(const std::string &text1, const std::string &text2)
+{
+    // INFO: This is for calculating Levenshtein distance(a.k.a edit distance) as similarity matric.
+    std::string long_str;
+    std::string short_str;
+    if (text1.size() > text2.size()) {
+        long_str = text1;
+        short_str = text2;
+    } else {
+        long_str = text2;
+        short_str = text1;
+    }
+
+    size_t long_str_size = long_str.size();
+    size_t short_str_size = short_str.size();
+
+    size_t cost_array1[long_str_size + 1]{};
+    size_t cost_array2[long_str_size + 1]{};
+
+    size_t *cost = cost_array1;
+    size_t *ncost = cost_array2;
+
+    for (size_t l = 0; l <= long_str_size; l++) {
+        cost[l] = l;
+    }
+
+    for (size_t s = 1; s <= short_str_size; s++) {
+        ncost[0] = s;
+
+        for (size_t l = 1; l <= long_str_size; l++) {
+            size_t match = (short_str[s - 1] == long_str[l - 1] ? 0 : 1);
+
+            size_t rep = cost[l - 1] + match;
+            size_t ins = cost[l] + 1;
+            size_t del = ncost[l - 1] + 1;
+
+            ncost[l] = std::min({rep, ins, del});
+        }
+
+        std::swap(ncost, cost);
+    }
+
+    return cost[long_str_size];
+}
+
+void VoiceTouchCandidates::clear_candidates()
+{
+    _I("[VOICE TOUCH] Clear current candidates");
+    m_candidates.clear();
+}
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h
new file mode 100644 (file)
index 0000000..29b6f6a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__
+#define __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__
+
+
+#include <vector>
+#include <string>
+#include <functional>
+#include <optional>
+
+#include "mmi-module-voice-touch-common.h"
+
+
+class VoiceTouchCandidates {
+public:
+    VoiceTouchCandidates();
+    ~VoiceTouchCandidates();
+
+    void set_candidates(const std::vector<voice_touch_candidate_s> &candidates);
+    std::optional<voice_touch_candidate_s> find_matched_candidate(size_t index);
+    std::optional<voice_touch_candidate_s> find_matched_candidate(const char *text);
+    void clear_candidates();
+
+private:
+    std::optional<voice_touch_candidate_s> find_matched_candidate_by_index(size_t index);
+    std::optional<voice_touch_candidate_s> find_matched_candidate_by_text(const std::vector<std::string> &splited_text);
+    std::optional<voice_touch_candidate_s> find_matched_candidate_by_word(const std::vector<std::string> &splited_text);
+    std::optional<voice_touch_candidate_s> find_matched_candidate_by_char(const std::vector<std::string> &splited_text);
+    size_t calcualte_edit_distance(const std::string &text1, const std::string &text2);
+
+private:
+    std::vector<voice_touch_candidate_s> m_candidates;
+};
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__ */
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-common.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-common.h
new file mode 100644 (file)
index 0000000..18e0a7e
--- /dev/null
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_COMMON_H__
+#define __MMI_MODULE_VOICE_TOUCH_COMMON_H__
+
+
+#include <string>
+
+
+enum voice_touch_mode_e {
+       VOICE_TOUCH_MODE_TEXT,
+       VOICE_TOUCH_MODE_INDEX,
+       VOICE_TOUCH_MODE_GRID
+};
+
+struct area_coordinates_s {
+    int x{};
+    int y{};
+    int w{};
+    int h{};
+};
+
+struct voice_touch_candidate_s {
+    voice_touch_mode_e mode{VOICE_TOUCH_MODE_TEXT};
+    area_coordinates_s candidate_area{};
+    size_t index{};
+    std::string object_id{};
+    std::string label{};
+};
+
+struct screen_element_s {
+    area_coordinates_s element_area{};
+    std::string object_id{};
+    std::string label{};
+    std::string role{};
+};
+
+struct grid_information_s {
+    size_t col_count{};
+    size_t row_count{};
+};
+
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_COMMON_H__ */
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp
new file mode 100644 (file)
index 0000000..24c43a7
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-error.h>
+#include <mmi-log.h>
+
+#include "mmi-module-voice-touch-data-utility.h"
+
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+#define NAME_OF_VARIABLE(x) (#x)
+
+
+MmiDataHelper::MmiDataHelper(mmi_data_h data, bool ownership) noexcept
+{
+    _I("[VOICE TOUCH] Construct helper class for wrapping existing data(%p). ownership(%s)", data, ownership ? "True" : "False");
+    m_handle = data;
+    m_ownership = ownership;
+}
+
+MmiDataHelper::MmiDataHelper(const std::vector<voice_touch_candidate_s> &candidates) noexcept
+{
+    _I("[VOICE TOUCH] Construct helper class for candidates output data");
+    m_handle = make_internal_mmi_data(candidates);
+    m_ownership = true;
+}
+
+MmiDataHelper::MmiDataHelper(const voice_touch_candidate_s &candidate, const screen_element_s &element) noexcept
+{
+    _I("[VOICE TOUCH] Construct helper class for matched result output data");
+    m_handle = make_internal_mmi_data(candidate, element);
+    m_ownership = true;
+}
+
+MmiDataHelper::MmiDataHelper(const char *text) noexcept
+{
+    _I("[VOICE TOUCH] Construct helper class for rejected output data");
+    m_handle = make_internal_mmi_data(text);
+    m_ownership = true;
+}
+
+MmiDataHelper::~MmiDataHelper()
+{
+    if (m_ownership) {
+        mmi_data_destroy(m_handle);
+    }
+}
+
+mmi_data_h MmiDataHelper::get_handle() const
+{
+    return m_handle;
+}
+
+mmi_data_type_e MmiDataHelper::get_type() const
+{
+    mmi_data_type_e type = MMI_DATA_TYPE_ANY;
+    int ret = mmi_data_get_type(m_handle, &type);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[VOICE TOUCH] Fail to get type. ret(%d)", ret);
+        throw ret;
+    }
+
+    return type;
+}
+
+int MmiDataHelper::get_integer_struct_member(const char *name) const
+{
+    mmi_data_h member_data = nullptr;
+    mmi_data_get_struct_element(m_handle, name, &member_data);
+
+    int value = 0;
+    int ret = mmi_data_get_int(member_data, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return value;
+}
+
+const char *MmiDataHelper::get_text_struct_member(const char *name) const
+{
+    mmi_data_h member_data = nullptr;
+    mmi_data_get_struct_element(m_handle, name, &member_data);
+
+    const char *value = nullptr;
+    int ret = mmi_data_get_text(member_data, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return value;
+}
+
+size_t MmiDataHelper::get_array_count() const
+{
+    _I("[VOICE TOUCH] Get integer value from handle.");
+    size_t value = 0;
+    int ret = mmi_data_get_array_count(m_handle, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return value;
+}
+
+MmiDataHelper MmiDataHelper::get_array_element(size_t index) const
+{
+    _I("[VOICE TOUCH] Get array element on index(%zu) from handle.", index);
+    mmi_data_h value = nullptr;
+    int ret = mmi_data_get_array_element(m_handle, index, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    if (nullptr == value) {
+        throw MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MmiDataHelper(value);
+}
+
+mmi_data_h MmiDataHelper::make_internal_mmi_data(const std::vector<voice_touch_candidate_s> &candidates)
+{
+    mmi_data_h output_data = nullptr;
+    mmi_data_create_array(&output_data);
+    if (nullptr == output_data) {
+        _E("[VOICE TOUCH] Fail to allocate memory for output");
+        return nullptr;
+    }
+
+    for (auto &candidate : candidates) {
+        mmi_data_h candidate_info = nullptr;
+        mmi_data_create_struct(&candidate_info);
+        if (nullptr == output_data) {
+            _E("[VOICE TOUCH] Fail to allocate memory for output");
+            mmi_data_destroy(output_data);
+            return nullptr;
+        }
+
+        set_integer_struct_member(candidate_info, "mode", candidate.mode);
+        set_integer_struct_member(candidate_info, "coord_x", candidate.candidate_area.x);
+        set_integer_struct_member(candidate_info, "coord_y", candidate.candidate_area.y);
+        set_integer_struct_member(candidate_info, "width", candidate.candidate_area.w);
+        set_integer_struct_member(candidate_info, "height", candidate.candidate_area.h);
+        set_text_struct_member(candidate_info, "label", candidate.label.c_str());
+
+        mmi_data_add_array_element(output_data, candidate_info);
+    }
+
+    return output_data;
+}
+
+mmi_data_h MmiDataHelper::make_internal_mmi_data(const voice_touch_candidate_s &candidate, const screen_element_s &element)
+{
+    mmi_data_h output_data = nullptr;
+    mmi_data_h candidate_info = nullptr;
+    mmi_data_h click_info = nullptr;
+    mmi_data_create_struct(&output_data);
+    mmi_data_create_struct(&candidate_info);
+    mmi_data_create_struct(&click_info);
+    if (nullptr == output_data || nullptr == candidate_info || nullptr == click_info) {
+        mmi_data_destroy(output_data);
+        mmi_data_destroy(candidate_info);
+        mmi_data_destroy(click_info);
+        _E("[VOICE TOUCH] Fail to allocate memory for data.");
+        return nullptr;
+    }
+
+    set_integer_struct_member(candidate_info, "mode", candidate.mode);
+    set_integer_struct_member(candidate_info, "coord_x", candidate.candidate_area.x);
+    set_integer_struct_member(candidate_info, "coord_y", candidate.candidate_area.y);
+    set_integer_struct_member(candidate_info, "width", candidate.candidate_area.w);
+    set_integer_struct_member(candidate_info, "height", candidate.candidate_area.h);
+    set_text_struct_member(candidate_info, "label", candidate.label.c_str());
+
+    auto &element_area = element.element_area;
+    set_integer_struct_member(click_info, "x", element_area.x + element_area.w / 2);
+    set_integer_struct_member(click_info, "y", element_area.y + element_area.h / 2);
+    set_text_struct_member(click_info, "object_id", element.object_id.c_str());
+
+    mmi_data_set_struct_element(output_data, NAME_OF_VARIABLE(candidate_info), candidate_info);
+    mmi_data_set_struct_element(output_data, NAME_OF_VARIABLE(click_info), click_info);
+
+    return output_data;
+}
+
+mmi_data_h MmiDataHelper::make_internal_mmi_data(const char *text)
+{
+    mmi_data_h output_data = nullptr;
+    mmi_data_create_text(text, &output_data);
+    if (nullptr == output_data) {
+        _E("[VOICE TOUCH] Fail to allocate memory for output");
+        return nullptr;
+    }
+
+    return output_data;
+}
+
+void MmiDataHelper::set_integer_struct_member(mmi_data_h struct_data, const char *member_name, int value)
+{
+    mmi_data_h member_data = nullptr;
+    mmi_data_create_int(value, &member_data);
+    if (nullptr == member_data) {
+        _E("[VOICE TOUCH] Fail to allocate memory for data.");
+        return;
+    }
+
+    if (MMI_ERROR_NONE != mmi_data_set_struct_element(struct_data, member_name, member_data)) {
+        _E("[VOICE TOUCH] Fail to set struct integer type member. name(%s). value(%d)", member_name, value);
+        mmi_data_destroy(member_data);
+    }
+}
+
+void MmiDataHelper::set_text_struct_member(mmi_data_h struct_data, const char *member_name, const char *value)
+{
+    mmi_data_h member_data = nullptr;
+    mmi_data_create_text(value, &member_data);
+    if (nullptr == member_data) {
+        _E("[VOICE TOUCH] Fail to allocate memory for data.");
+        return;
+    }
+
+    if (MMI_ERROR_NONE != mmi_data_set_struct_element(struct_data, member_name, member_data)) {
+        _E("[VOICE TOUCH] Fail to set struct text type member. name(%s). value(%s)", member_name, value);
+        mmi_data_destroy(member_data);
+    }
+}
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h
new file mode 100644 (file)
index 0000000..db0cc42
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__
+#define __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__
+
+
+#include <vector>
+
+#include <mmi-data.h>
+#include "mmi-module-voice-touch-common.h"
+
+class MmiDataHelper {
+public:
+    MmiDataHelper(mmi_data_h data, bool ownership = false) noexcept;
+    MmiDataHelper(const std::vector<voice_touch_candidate_s> &candidates) noexcept;
+    MmiDataHelper(const voice_touch_candidate_s &candidate, const screen_element_s &element) noexcept;
+    MmiDataHelper(const char *text) noexcept;
+    ~MmiDataHelper();
+
+    // For common
+    mmi_data_h get_handle() const;
+    mmi_data_type_e get_type() const;
+
+    // For struct type
+    int get_integer_struct_member(const char *name) const;
+    const char *get_text_struct_member(const char *name) const;
+
+    // For array type
+    size_t get_array_count() const;
+    MmiDataHelper get_array_element(size_t index) const;
+
+private:
+    mmi_data_h make_internal_mmi_data(const std::vector<voice_touch_candidate_s> &candidates);
+    mmi_data_h make_internal_mmi_data(const voice_touch_candidate_s &candidate, const screen_element_s &element);
+    mmi_data_h make_internal_mmi_data(const char *text);
+
+    void set_integer_struct_member(mmi_data_h struct_data, const char *member_name, int value);
+    void set_text_struct_member(mmi_data_h struct_data, const char *member_name, const char *value);
+
+private:
+    mmi_data_h m_handle;
+    bool m_ownership;
+};
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__ */
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp
new file mode 100644 (file)
index 0000000..6c39a1f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-error.h>
+#include <mmi-log.h>
+
+#include "mmi-module-voice-touch-primitive-utility.h"
+
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+
+MmiPrimitiveHelper::MmiPrimitiveHelper(mmi_primitive_value_h handle, bool ownership) noexcept
+{
+    _I("[VOICE TOUCH] Construct helper class for primitive value handle");
+    m_handle = handle;
+    m_ownership = ownership;
+}
+
+MmiPrimitiveHelper::~MmiPrimitiveHelper()
+{
+    _I("[VOICE TOUCH] Destruct helper class for primitive value handle");
+    if (m_ownership) {
+        mmi_primitive_value_destroy(m_handle);
+    }
+}
+
+mmi_primitive_value_h MmiPrimitiveHelper::get_handle() const
+{
+    _I("[VOICE TOUCH] Get raw primitive value handle. (%p)", m_handle);
+    return m_handle;
+}
+
+mmi_primitive_value_type_e MmiPrimitiveHelper::get_type() const
+{
+    _I("[VOICE TOUCH] Get type from handle.");
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    int ret = mmi_primitive_value_get_type(m_handle, &type);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return type;
+}
+
+int MmiPrimitiveHelper::get_integer_value() const
+{
+    _I("[VOICE TOUCH] Get integer value from handle.");
+    int value = -1;
+    int ret = mmi_primitive_value_get_int(m_handle, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return value;
+}
+
+size_t MmiPrimitiveHelper::get_array_count() const
+{
+    _I("[VOICE TOUCH] Get integer value from handle.");
+    size_t value = 0;
+    int ret = mmi_primitive_value_get_array_count(m_handle, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    return value;
+}
+
+MmiPrimitiveHelper MmiPrimitiveHelper::get_array_element(size_t index) const
+{
+    _I("[VOICE TOUCH] Get array element on index(%zu) from handle.", index);
+    mmi_primitive_value_h value = nullptr;
+    int ret = mmi_primitive_value_get_array_element(m_handle, index, &value);
+    if (MMI_ERROR_NONE != ret) {
+        throw ret;
+    }
+
+    if (nullptr == value) {
+        throw MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MmiPrimitiveHelper(value);
+}
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h
new file mode 100644 (file)
index 0000000..32c70a4
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__
+#define __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__
+
+
+#include <mmi-primitive-value.h>
+
+class MmiPrimitiveHelper {
+public:
+    MmiPrimitiveHelper(mmi_primitive_value_h handle, bool ownership = false) noexcept;
+    ~MmiPrimitiveHelper();
+
+    // For common
+    mmi_primitive_value_h get_handle() const;
+    mmi_primitive_value_type_e get_type() const;
+
+    // For integer value
+    int get_integer_value() const;
+
+    // For array value
+    size_t get_array_count() const;
+    MmiPrimitiveHelper get_array_element(size_t index) const;
+
+private:
+
+private:
+    mmi_primitive_value_h m_handle;
+    bool m_ownership;
+};
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__ */
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp
new file mode 100644 (file)
index 0000000..7aa456e
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-log.h>
+#include <mmi-error.h>
+
+#include <algorithm>
+#include <numeric>
+
+#include "mmi-module-voice-touch-screen-info.h"
+#include "mmi-module-voice-touch-data-utility.h"
+
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+
+static const double WORD_MATCHING_RATE = 0.3;
+static const double CHAR_MATCHING_RATE = 0.35;
+static const size_t MIN_WORD_SIZE = 3;
+
+
+ScreenElements::ScreenElements()
+{
+    _I("[VOICE TOUCH] Construct ScreenElements");
+    m_screen_width = 1920;
+    m_screen_height = 1080;
+    m_grid_depth = 0;
+    m_parent_grid_area = {
+            .x = 0,
+            .y = 0,
+            .w = m_screen_width,
+            .h = m_screen_height
+    };
+
+    m_grid_informations.clear();
+    m_grid_informations.push_back(grid_information_s{
+            .col_count = 7,
+            .row_count = 5
+    });
+    m_grid_informations.push_back(grid_information_s{
+            .col_count = 4,
+            .row_count = 3
+    });
+
+    m_screen_elements.clear();
+}
+
+ScreenElements::~ScreenElements()
+{
+    _I("[VOICE TOUCH] Destruct ScreenElements");
+    m_screen_width = 1920;
+    m_screen_height = 1080;
+    m_grid_depth = 0;
+    m_parent_grid_area = {
+            .x = 0,
+            .y = 0,
+            .w = m_screen_width,
+            .h = m_screen_height
+    };
+    m_grid_informations.clear();
+    m_screen_elements.clear();
+}
+
+void ScreenElements::set_screen_size(int width, int height)
+{
+    _I("[VOICE TOUCH] Set screen size information. w(%d), h(%d)", width, height);
+    m_screen_width = width;
+    m_screen_height = height;
+}
+
+void ScreenElements::set_grid_informations(const std::vector<grid_information_s> &grid_informations)
+{
+    _I("[VOICE TOUCH] Set grid information according to each depth. maximum depth(%zu)", grid_informations.size());
+    m_grid_informations = grid_informations;
+}
+
+void ScreenElements::set_screen_elements(const MmiDataHelper &screen_elements)
+{
+    _I("[VOICE TOUCH] Set screen elements from mmi data");
+    try {
+        size_t counts = screen_elements.get_array_count();
+
+        m_screen_elements.clear();
+        for (size_t i = 0; i < counts; i++) {
+            auto element = screen_elements.get_array_element(i);
+            m_screen_elements.push_back(make_screen_element(element));
+        }
+    } catch (mmi_error_e &error) {
+        _E("[VOICE TOUCH] Fail to make screen element. (%d)", error);
+        return;
+    }
+
+    _D("[VOICE TOUCH] The current number of elements (%zu)", m_screen_elements.size());
+}
+
+screen_element_s ScreenElements::make_screen_element(MmiDataHelper &data)
+{
+    area_coordinates_s element_area{
+            .x = data.get_integer_struct_member("coord_x"),
+            .y = data.get_integer_struct_member("coord_y"),
+            .w = data.get_integer_struct_member("width"),
+            .h = data.get_integer_struct_member("height")
+    };
+
+    screen_element_s screen_element{
+            .element_area = element_area,
+            .object_id = std::string(data.get_text_struct_member("object_id")),
+            .label = std::string(data.get_text_struct_member("label")),
+            .role = std::string(data.get_text_struct_member("role"))
+    };
+
+    return screen_element;
+}
+
+void ScreenElements::set_grid_area(const area_coordinates_s &grid_area)
+{
+    _I("[VOICE TOUCH] Set screen area information x(%d) / y(%d) / w(%d)/ h(%d)",
+       grid_area.x, grid_area.y, grid_area.w, grid_area.h);
+    m_parent_grid_area = grid_area;
+}
+
+void ScreenElements::increase_grid_depth()
+{
+    m_grid_depth++;
+    _I("[VOICE TOUCH] Increase current grid depth. (%zu)", m_grid_depth);
+}
+
+void ScreenElements::reset_grid_depth()
+{
+    _I("[VOICE TOUCH] Reset current grid depth.");
+    m_grid_depth = 0;
+    m_parent_grid_area = area_coordinates_s{
+            .x = 0,
+            .y = 0,
+            .w = m_screen_width,
+            .h = m_screen_height
+    };
+}
+
+bool ScreenElements::is_maximum_grid_depth()
+{
+    bool result = (m_grid_depth == m_grid_informations.size());
+    _I("[VOICE TOUCH] Check current grid depth reaches to maximum. current(%zu) / max(%zu). result(%s)",
+       m_grid_depth, m_grid_informations.size(), result ? "True" : "False");
+
+    return result;
+}
+
+std::vector<voice_touch_candidate_s> ScreenElements::make_voice_touch_candidates(voice_touch_mode_e mode)
+{
+    _I("[VOICE TOUCH] Make voice touch candidates according to the mode. (%d)", mode);
+    std::vector<voice_touch_candidate_s> candidates;
+
+    switch (mode)
+    {
+    case VOICE_TOUCH_MODE_TEXT:
+        fill_text_label_candidates(candidates);
+        break;
+
+    case VOICE_TOUCH_MODE_INDEX:
+        fill_index_label_candidates(candidates);
+        break;
+
+    case VOICE_TOUCH_MODE_GRID:
+        fill_grid_label_candidates(candidates);
+        break;
+
+    default:
+        _E("[VOICE TOUCH] Invalid voice touch mode (%d)", mode);
+        break;
+    }
+
+    return candidates;
+}
+
+void ScreenElements::fill_grid_label_candidates(std::vector<voice_touch_candidate_s> &candidates)
+{
+    if (m_grid_depth >= m_grid_informations.size()) {
+        _E("[VOICE TOUCH] Current grid depth is out of range (%zu) / (%zu)", m_grid_depth, m_grid_informations.size());
+        return;
+    }
+
+    size_t col_count = m_grid_informations.at(m_grid_depth).col_count;
+    size_t row_count = m_grid_informations.at(m_grid_depth).row_count;
+
+    size_t grid_width = m_parent_grid_area.w / col_count;
+    size_t grid_height = m_parent_grid_area.h / row_count;
+
+       size_t remain_width = m_parent_grid_area.w % col_count;
+       size_t remain_height = m_parent_grid_area.h % row_count;
+
+    // TODO: Enhance the calculation to uniformly seperate the grid
+    size_t index = 1;
+    for (size_t y = 0; y < row_count; y++) {
+        for (size_t x = 0; x < col_count; x++) {
+            area_coordinates_s candidate_area{
+                    .x = static_cast<int>(m_parent_grid_area.x + (x * grid_width)),
+                    .y = static_cast<int>(m_parent_grid_area.y + (y * grid_height)),
+                    .w = static_cast<int>((x == col_count - 1 ? grid_width + remain_width : grid_width)),
+                    .h = static_cast<int>((y == row_count - 1 ? grid_height + remain_height : grid_height))
+            };
+
+            voice_touch_candidate_s candidate{
+                    .mode = VOICE_TOUCH_MODE_GRID,
+                    .candidate_area = candidate_area,
+                    .index = index,
+                    .object_id = std::string(),
+                    .label = std::to_string(index)
+            };
+
+            candidates.push_back(candidate);
+            index++;
+        }
+    }
+}
+
+void ScreenElements::fill_index_label_candidates(std::vector<voice_touch_candidate_s> &candidates)
+{
+    size_t index = 1;
+    for (auto &element : m_screen_elements) {
+        voice_touch_candidate_s candidate{
+                .mode = VOICE_TOUCH_MODE_INDEX,
+                .candidate_area = element.element_area,
+                .index = index,
+                .object_id = element.object_id,
+                .label = std::to_string(index)
+        };
+
+        candidates.push_back(candidate);
+        index++;
+    }
+}
+
+void ScreenElements::fill_text_label_candidates(std::vector<voice_touch_candidate_s> &candidates)
+{
+    size_t index = 1;
+    for (auto &element : m_screen_elements) {
+        voice_touch_candidate_s candidate{
+                .mode = VOICE_TOUCH_MODE_TEXT,
+                .candidate_area = element.element_area,
+                .index = index,
+                .object_id = element.object_id,
+                .label = (element.label.empty() ? std::to_string(index) : element.label)
+        };
+
+        candidates.push_back(candidate);
+        index++;
+    }
+}
+
+std::optional<screen_element_s> ScreenElements::find_related_screen_element(const voice_touch_candidate_s &candidate)
+{
+    switch (candidate.mode)
+    {
+    case VOICE_TOUCH_MODE_TEXT:
+    case VOICE_TOUCH_MODE_INDEX:
+        return find_exact_element_by_object_id(candidate.object_id);
+
+    case VOICE_TOUCH_MODE_GRID:
+        return find_best_element_in_grid(candidate.candidate_area);
+
+    default:
+        _E("[VOICE TOUCH] Invalid voice touch mode (%d)", candidate.mode);
+        break;
+    }
+
+    return std::nullopt;
+}
+
+std::optional<screen_element_s> ScreenElements::find_exact_element_by_object_id(const std::string &object_id)
+{
+    auto is_same_element = [object_id](const screen_element_s &element) -> bool {
+        return element.object_id == object_id;
+    };
+
+    auto result = std::find_if(m_screen_elements.begin(), m_screen_elements.end(), is_same_element);
+    if (result == m_screen_elements.end()) {
+        _E("[VOICE TOUCH] No candidates which has object id(%s)", object_id.c_str());
+        return std::nullopt;
+    }
+
+    _I("[VOICE TOUCH] Result. object Id(%s)", object_id.c_str());
+    return *result;
+}
+
+std::optional<screen_element_s> ScreenElements::find_best_element_in_grid(const area_coordinates_s &grid_area)
+{
+    int grid_x1 = grid_area.x;
+    int grid_x2 = grid_area.x + grid_area.w;
+    int grid_y1 = grid_area.y;
+    int grid_y2 = grid_area.y + grid_area.h;
+
+    auto is_in_area = [grid_x1, grid_x2, grid_y1, grid_y2](const screen_element_s &element) -> bool {
+        auto &area = element.element_area;
+        if (area.x >= grid_x2 || area.x + area.w <= grid_x1 || area.y > grid_y2 || area.y + area.h <= grid_y1) {
+            return false;
+        }
+
+        return true;
+    };
+
+    std::vector<screen_element_s> filtered_elements;
+    std::copy_if(m_screen_elements.begin(), m_screen_elements.end(), std::back_inserter(filtered_elements), is_in_area);
+    if (filtered_elements.empty()) {
+        _E("[VOICE TOUCH] No candidates in grid (%d, %d / %d, %d)", grid_x1, grid_y1, grid_x2, grid_y2);
+        return std::nullopt;
+    }
+
+    std::optional<screen_element_s> result;
+    int maximum_area_size = -1;
+    for (auto &element : filtered_elements) {
+        auto &area = element.element_area;
+        int dx = std::min(grid_x2, area.x + area.w) - std::max(grid_x1, area.x);
+        int dy = std::min(grid_y2, area.y + area.h) - std::max(grid_y1, area.y);
+
+        int area_size = dx * dy;
+        if (maximum_area_size < area_size) {
+            maximum_area_size = area_size;
+            result = element;
+        }
+    }
+
+    _I("[VOICE TOUCH] Result. object Id(%s)", result->object_id.c_str());
+    return result;
+}
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h
new file mode 100644 (file)
index 0000000..057c901
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__
+#define __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__
+
+
+#include <vector>
+#include <string>
+#include <functional>
+#include <optional>
+
+#include "mmi-module-voice-touch-common.h"
+#include "mmi-module-voice-touch-data-utility.h"
+
+
+class ScreenElements {
+public:
+    ScreenElements();
+    ~ScreenElements();
+
+    void set_screen_size(int width, int height);
+    void set_grid_informations(const std::vector<grid_information_s> &grid_informations);
+    void set_screen_elements(const MmiDataHelper &screen_elements);
+    void set_grid_area(const area_coordinates_s &grid_area);
+
+    void increase_grid_depth();
+    void reset_grid_depth();
+    bool is_maximum_grid_depth();
+
+    std::vector<voice_touch_candidate_s> make_voice_touch_candidates(voice_touch_mode_e mode);
+    std::optional<screen_element_s> find_related_screen_element(const voice_touch_candidate_s &candidate);
+
+private:
+    screen_element_s make_screen_element(MmiDataHelper &data);
+
+    void fill_grid_label_candidates(std::vector<voice_touch_candidate_s> &candidates);
+    void fill_index_label_candidates(std::vector<voice_touch_candidate_s> &candidates);
+    void fill_text_label_candidates(std::vector<voice_touch_candidate_s> &candidates);
+
+    std::optional<screen_element_s> find_exact_element_by_object_id(const std::string &object_id);
+    std::optional<screen_element_s> find_best_element_in_grid(const area_coordinates_s &grid_area);
+
+private:
+    // Screen information
+    int m_screen_width;
+    int m_screen_height;
+
+    // Grid information
+    std::vector<grid_information_s> m_grid_informations;
+    area_coordinates_s m_parent_grid_area;
+    size_t m_grid_depth;
+
+    // Current screen elements
+    std::vector<screen_element_s> m_screen_elements;
+};
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__ */
similarity index 51%
rename from src/mmi-client.h
rename to plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp
index 6f5cc81..7a1134c 100644 (file)
  *
  */
 
-#ifndef __MMI_CLIENT_H__
-#define __MMI_CLIENT_H__
 
-#include <glib.h>
-#include "mmi.h"
+#include <algorithm>
+#include <sstream>
 
-typedef void* mmi_rpc_h;
+#include "mmi-module-voice-touch-string-utility.h"
 
-typedef struct {
-       mmi_rpc_h rpc_h;
-       const char *stub_appid;
-       int state;
-       GList* result_cb_list;
-} mmi_struct_s;
 
-typedef mmi_struct_s* mmi_handle;
-
-typedef struct {
-       int input_event_type;
-       mmi_result_cb result_callback;
-} mmi_result_cb_s;
+std::string StringUtility::make_lower_case(std::string text)
+{
+    transform(text.begin(), text.end(), text.begin(), ::tolower);
+    return text;
+}
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+std::vector<std::string> StringUtility::split_text(std::string text, char delimiter)
+{
+    std::vector<std::string> result;
+    std::istringstream ss{text};
+    std::string temp;
 
-int mmi_client_create(void);
-int mmi_client_destroy(void);
-int mmi_client_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data);
-mmi_handle mmi_client_get(void);
+    while (getline(ss, temp, delimiter)) {
+        result.push_back(temp);
+    }
 
-#ifdef __cplusplus
+    return result;
 }
-#endif
-
-#endif //__MMI_CLIENT_H__
\ No newline at end of file
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h
new file mode 100644 (file)
index 0000000..12c71b2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__
+#define __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__
+
+
+#include <vector>
+#include <string>
+
+class StringUtility {
+public:
+    static std::string make_lower_case(std::string text);
+    static std::vector<std::string> split_text(std::string text, char delimeter);
+};
+
+#endif /* __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__ */
diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch.cpp
new file mode 100644 (file)
index 0000000..76d72fc
--- /dev/null
@@ -0,0 +1,617 @@
+#include <mmi-attribute.h>
+#include <mmi-node.h>
+#include <mmi-node-processor.h>
+#include <mmi-log.h>
+
+#include <map>
+#include <memory>
+
+#include "mmi-module-voice-touch-candidates.h"
+#include "mmi-module-voice-touch-screen-info.h"
+#include "mmi-module-voice-touch-data-utility.h"
+#include "mmi-module-voice-touch-primitive-utility.h"
+
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+
+struct voice_touch_helper_s
+{
+    VoiceTouchCandidates voice_touch_candidates;
+    ScreenElements screen_elements;
+    voice_touch_mode_e mode{VOICE_TOUCH_MODE_TEXT};
+};
+
+
+static const char *INPUT_PORT_NAME_SCREEN_INFO = "SCREEN_INFO";
+static const char *INPUT_PORT_NAME_MODE_COMMANDS = "MODE_COMMANDS";
+static const char *INPUT_PORT_NAME_UTTERANCE = "UTTERANCE";
+
+static const char *OUTPUT_PORT_NAME_MATCHED_RESULT = "MATCHED_RESULT";
+static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED";
+static const char *OUTPUT_PORT_NAME_CANDIDATES = "CANDIDATES";
+
+static const char *ATTRIBUTE_NAME_GRID_CONFIGURATION = "GRID_CONFIGURATION";
+
+static const char *MODE_COMMAND_SHOW_INDEX = "숫자 보여줘";
+static const char *MODE_COMMAND_SHOW_TEXT = "텍스트 보여줘";
+static const char *MODE_COMMAND_SHOW_GRID = "그리드 보여줘";
+
+
+static std::map<mmi_node_instance_h, std::shared_ptr<voice_touch_helper_s>> g_node_to_helper_map;
+
+
+static int node_initialized_cb(mmi_node_instance_h instance)
+{
+    _D("[VOICE TOUCH] Node initialize callback is called for %p", instance);
+    if (nullptr == instance) {
+        _E("[VOICE TOUCH] Instance is null. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    try {
+        auto helper_instance = std::make_shared<voice_touch_helper_s>();
+        g_node_to_helper_map[instance] = helper_instance;
+    } catch (std::bad_alloc &e) {
+        _E("[VOICE TOUCH] Fail to allocate memory for node instance. %p", instance);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static bool is_node_instance_valid(mmi_node_instance_h instance)
+{
+    if (nullptr == instance) {
+        _E("[VOICE TOUCH] Node instance is null %p", instance);
+        return false;
+    }
+
+    auto iterator = g_node_to_helper_map.find(instance);
+    if (iterator == g_node_to_helper_map.end()) {
+        _E("[VOICE TOUCH] No instance %p", instance);
+        return false;
+    }
+
+    return true;
+}
+
+static int node_deinitialized_cb(mmi_node_instance_h instance)
+{
+    _D("[VOICE TOUCH] Node deinitialize callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    g_node_to_helper_map.erase(instance);
+    return MMI_ERROR_NONE;
+}
+
+static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute)
+{
+    _D("[VOICE TOUCH] Node attribute set callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    char *name = nullptr;
+    mmi_attribute_get_name(attribute, &name);
+    if (nullptr == name) {
+        _E("[VOICE TOUCH] Fail to get name from attribute. %p", attribute);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string attribute_name(name);
+    free(name);
+    name = nullptr;
+
+    if (0 != attribute_name.compare(ATTRIBUTE_NAME_GRID_CONFIGURATION)) {
+        _E("[VOICE TOUCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str());
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_primitive_value_h value = nullptr;
+    mmi_attribute_get_value(attribute, &value);
+    if (nullptr == value) {
+        _E("[VOICE TOUCH] Fail to get primitive value from attribute. %p", attribute);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    MmiPrimitiveHelper attribute_value(value, true);
+    value = nullptr;
+    if (MMI_PRIMITIVE_VALUE_TYPE_ARRAY != attribute_value.get_type()) {
+        _E("[VOICE TOUCH] Fail to get primitive value from attribute. %p", attribute);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto &screen_elements = g_node_to_helper_map[instance]->screen_elements;
+
+    // TODO: Check the efficient way to set attribute for voice touch.
+    try {
+        auto screen_w = attribute_value.get_array_element(0).get_integer_value();
+        auto screen_h = attribute_value.get_array_element(1).get_integer_value();
+
+        _D("[VOICE TOUCH] Screen size : %d, %d", screen_w, screen_h);
+        screen_elements.set_screen_size(screen_w, screen_h);
+    } catch (mmi_error_e &error) {
+        _E("[VOICE TOUCH] Fail to get screen size. %p", attribute);
+        return error;
+    }
+
+    size_t count = attribute_value.get_array_count();
+    std::vector<grid_information_s> grid_informations;
+    for (size_t i = 2; i < count; i += 2) {
+        try {
+            grid_information_s grid_information{
+                    .col_count = static_cast<size_t>(attribute_value.get_array_element(i).get_integer_value()),
+                    .row_count = static_cast<size_t>(attribute_value.get_array_element(i + 1).get_integer_value())
+            };
+
+            _D("[VOICE TOUCH] Grid information : col(%zu), row(%zu)", grid_information.col_count, grid_information.row_count);
+            grid_informations.push_back(grid_information);
+        } catch (mmi_error_e &error) {
+            _E("[VOICE TOUCH] Fail to get data. Please check error. index(%zu). error(%d)", i, error);
+            return error;
+        }
+    }
+
+    screen_elements.set_grid_informations(grid_informations);
+    return MMI_ERROR_NONE;
+}
+
+static int node_activated_cb(mmi_node_instance_h instance)
+{
+    _D("[VOICE TOUCH] Node activate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int node_deactivated_cb(mmi_node_instance_h instance)
+{
+    _D("[VOICE TOUCH] Node deactivate callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int utterance_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+    _D("[VOICE TOUCH] Port output format request callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static void generate_output_for_rejected_port(mmi_node_instance_h instance, const char *text)
+{
+    mmi_port_instance_h rejected_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_REJECTED, &rejected_port);
+    if (nullptr == rejected_port) {
+        _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_REJECTED, instance);
+        return;
+    }
+
+    MmiDataHelper output_data(text);
+    if (nullptr == output_data.get_handle()) {
+        _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance);
+        return;
+    }
+
+    mmi_port_instance_generate_output(rejected_port, output_data.get_handle());
+}
+
+static void generate_output_for_matched_result_port(mmi_node_instance_h instance, const voice_touch_candidate_s &candidate, const screen_element_s &element)
+{
+    mmi_port_instance_h matched_result_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_MATCHED_RESULT, &matched_result_port);
+    if (nullptr ==  matched_result_port) {
+        _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance);
+        return;
+    }
+
+    MmiDataHelper output_data(candidate, element);
+    if (nullptr == output_data.get_handle()) {
+        _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance);
+        return;
+    }
+
+    mmi_port_instance_generate_output(matched_result_port, output_data.get_handle());
+}
+
+static void generate_output_for_candidates_port(mmi_node_instance_h instance, const std::vector<voice_touch_candidate_s> &candidates)
+{
+    mmi_port_instance_h candidates_port = nullptr;
+    mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_CANDIDATES, &candidates_port);
+    if (nullptr == candidates_port) {
+        _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_CANDIDATES, instance);
+        return;
+    }
+
+    MmiDataHelper output_data(candidates);
+    if (nullptr == output_data.get_handle()) {
+        _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_CANDIDATES, instance);
+        return;
+    }
+
+    mmi_port_instance_generate_output(candidates_port, output_data.get_handle());
+}
+
+static mmi_error_e handle_utterance_input_as_text(mmi_node_instance_h instance, const char *utterance)
+{
+    auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates;
+    auto &screen_elements = g_node_to_helper_map[instance]->screen_elements;
+
+    auto matched_candidate = voice_touch_candidates.find_matched_candidate(utterance);
+    if (matched_candidate.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    auto matched_element = screen_elements.find_related_screen_element(*matched_candidate);
+    if (matched_element.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance);
+    generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element);
+
+    return MMI_ERROR_NONE;
+}
+
+static mmi_error_e handle_utterance_input_as_index(mmi_node_instance_h instance, const char *utterance)
+{
+    auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates;
+    auto &screen_elements = g_node_to_helper_map[instance]->screen_elements;
+
+    int index = -1;
+    try {
+        index = std::stoi(utterance);
+    } catch (std::exception &e) {
+        _I("[VOICE TOUCH] There is no number in utterance. utterance(%s). exception(%s)", utterance, e.what());
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    auto matched_candidate = voice_touch_candidates.find_matched_candidate(index);
+    if (matched_candidate.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    auto matched_element = screen_elements.find_related_screen_element(*matched_candidate);
+    if (matched_element.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance);
+    generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element);
+
+    return MMI_ERROR_NONE;
+}
+
+static mmi_error_e handle_utterance_input_as_grid(mmi_node_instance_h instance, const char *utterance)
+{
+    auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates;
+    auto &screen_elements = g_node_to_helper_map[instance]->screen_elements;
+
+    int index = -1;
+    try {
+        index = std::stoi(utterance);
+    } catch (std::exception &e) {
+        _I("[VOICE TOUCH] There is no number in utterance. utterance(%s). exception(%s)", utterance, e.what());
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    auto matched_candidate = voice_touch_candidates.find_matched_candidate(index);
+    if (matched_candidate.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    screen_elements.increase_grid_depth();
+    if (screen_elements.is_maximum_grid_depth() == false) {
+        _I("[VOICE TOUCH] Make candidate for next grid depth.");
+        screen_elements.set_grid_area(matched_candidate->candidate_area);
+
+        auto candidates = screen_elements.make_voice_touch_candidates(VOICE_TOUCH_MODE_GRID);
+        voice_touch_candidates.set_candidates(candidates);
+        generate_output_for_candidates_port(instance, candidates);
+        return MMI_ERROR_NONE;
+    }
+
+    auto matched_element = screen_elements.find_related_screen_element(*matched_candidate);
+    if (matched_element.has_value() == false) {
+        _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance);
+        _I("[VOICE TOUCH] Make output for center of grid");
+        matched_element = screen_element_s{
+                .element_area = matched_candidate->candidate_area,
+                .object_id = std::string(),
+                .label = matched_candidate->label,
+                .role = std::string()
+        };
+    } else {
+        _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance);
+    }
+
+    screen_elements.reset_grid_depth();
+    generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element);
+
+    return MMI_ERROR_NONE;
+}
+
+static std::optional<mmi_node_instance_h> find_node_instance(mmi_port_instance_h port_instance, const char *port_name)
+{
+    if (nullptr == port_instance || nullptr == port_name) {
+        return std::nullopt;
+    }
+
+    for (auto &element : g_node_to_helper_map) {
+        mmi_port_instance_h candidate_port = nullptr;
+        mmi_node_instance_find_port(element.first, MMI_PORT_TYPE_IN, port_name, &candidate_port);
+
+        if (candidate_port == port_instance) {
+            return element.first;
+        }
+    }
+
+    return std::nullopt;
+}
+
+static int utterance_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+    _D("[VOICE TOUCH] Text input data callback is called: %p", instance);
+    auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_UTTERANCE);
+    if (node_instance.has_value() == false) {
+        _E("[VOICE TOUCH] There is no proper node instance: %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const char *utterance = nullptr;
+    mmi_data_get_text(data, &utterance);
+    if (nullptr == utterance) {
+        _E("[VOICE TOUCH] Fail to get text from the data: %p", data);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    voice_touch_mode_e mode = g_node_to_helper_map[*node_instance]->mode;
+    mmi_error_e ret = MMI_ERROR_NONE;
+    switch (mode) {
+    case VOICE_TOUCH_MODE_TEXT:
+        ret = handle_utterance_input_as_text(*node_instance, utterance);
+        break;
+
+    case VOICE_TOUCH_MODE_INDEX:
+        ret = handle_utterance_input_as_index(*node_instance, utterance);
+        break;
+
+    case VOICE_TOUCH_MODE_GRID:
+        ret = handle_utterance_input_as_grid(*node_instance, utterance);
+        break;
+    }
+
+    if (MMI_ERROR_NONE != ret) {
+        _I("[VOICE TOUCH] Generate reject output. utterance(%s)", utterance);
+        generate_output_for_rejected_port(*node_instance, utterance);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int mode_commands_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+    _D("[VOICE TOUCH] Port output format request callback is called for %p", instance);
+
+    return MMI_ERROR_NONE;
+}
+
+static int mode_commands_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+    _D("[VOICE TOUCH] Text input data callback is called: %p", instance);
+    auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_MODE_COMMANDS);
+    if (node_instance.has_value() == false) {
+        _E("[VOICE TOUCH] There is no proper node instance: %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const char *text = nullptr;
+    mmi_data_get_text(data, &text);
+    if (nullptr == text) {
+        _E("[VOICE TOUCH] Fail to get text from the data: %p", data);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string mode_str = text;
+    text = nullptr;
+
+    voice_touch_mode_e mode = VOICE_TOUCH_MODE_TEXT;
+    if (mode_str.compare(MODE_COMMAND_SHOW_TEXT) == 0) {
+        mode = VOICE_TOUCH_MODE_TEXT;
+    } else if (mode_str.compare(MODE_COMMAND_SHOW_INDEX) == 0) {
+        mode = VOICE_TOUCH_MODE_INDEX;
+    } else if (mode_str.compare(MODE_COMMAND_SHOW_GRID) == 0) {
+        mode = VOICE_TOUCH_MODE_GRID;
+    } else {
+        _E("[VOICE TOUCH] Invalid mode command: %s", mode_str.c_str());
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto &node_helper = g_node_to_helper_map[*node_instance];
+    auto &voice_touch_candidates = node_helper->voice_touch_candidates;
+    auto &screen_elements = node_helper->screen_elements;
+
+    node_helper->mode = mode;
+    auto candidates = screen_elements.make_voice_touch_candidates(node_helper->mode);
+    voice_touch_candidates.set_candidates(candidates);
+
+    generate_output_for_candidates_port(*node_instance, candidates);
+
+    return MMI_ERROR_NONE;
+}
+
+static int screen_info_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format)
+{
+    _D("[VOICE TOUCH] Port output format request callback is called for %p", instance);
+    if (false == is_node_instance_valid(instance)) {
+        _E("[VOICE TOUCH] Instance is not valid. %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+static int screen_info_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data)
+{
+    _D("[VOICE TOUCH] Text input data callback is called: %p", instance);
+    auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_SCREEN_INFO);
+    if (node_instance.has_value() == false) {
+        _E("[VOICE TOUCH] There is no proper node instance: %p", instance);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const MmiDataHelper data_helper(data);
+    try {
+        mmi_data_type_e type = data_helper.get_type();
+        if (MMI_DATA_TYPE_ARRAY != type) {
+            _E("[VOICE TOUCH] Type is not valid. data(%p). type(%d)", data, type);
+            return MMI_ERROR_INVALID_PARAMETER;
+        }
+    } catch (mmi_error_e &error) {
+        return error;
+    }
+
+    auto &node_helper = g_node_to_helper_map[*node_instance];
+    auto &voice_touch_candidates = node_helper->voice_touch_candidates;
+    auto &screen_elements = node_helper->screen_elements;
+
+    screen_elements.set_screen_elements(data_helper);
+    auto candidates = screen_elements.make_voice_touch_candidates(node_helper->mode);
+    voice_touch_candidates.set_candidates(candidates);
+    generate_output_for_candidates_port(*node_instance, candidates);
+
+    return MMI_ERROR_NONE;
+}
+
+static mmi_port_h create_input_port(const char *name, mmi_data_type_e type, mmi_port_callbacks callbacks)
+{
+    mmi_port_h input_port = nullptr;
+    mmi_port_create(&input_port);
+    if (nullptr == input_port) {
+        return nullptr;
+    }
+
+    mmi_port_set_name(input_port, name);
+    mmi_port_set_type(input_port, MMI_PORT_TYPE_IN);
+    mmi_port_set_data_type(input_port, type);
+    mmi_port_set_callbacks(input_port, callbacks);
+
+    return input_port;
+}
+
+static mmi_port_h create_output_port(const char *name, mmi_data_type_e type)
+{
+    mmi_port_h output_port = nullptr;
+    mmi_port_create(&output_port);
+    if (nullptr == output_port) {
+        return nullptr;
+    }
+
+    mmi_port_set_name(output_port, name);
+    mmi_port_set_type(output_port, MMI_PORT_TYPE_OUT);
+    mmi_port_set_data_type(output_port, type);
+
+    return output_port;
+}
+
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_node_list()
+    {
+        mmi_node_callbacks node_callbacks {
+            node_initialized_cb,
+            node_deinitialized_cb,
+            node_attribute_set_cb,
+            node_activated_cb,
+            node_deactivated_cb,
+            nullptr
+        };
+
+        mmi_port_callbacks utterance_port_callbacks {
+            utterance_port_output_format_requested_cb,
+            utterance_port_input_data_received_cb
+        };
+
+        mmi_port_callbacks mode_commands_port_callbacks {
+            mode_commands_port_output_format_requested_cb,
+            mode_commands_port_input_data_received_cb
+        };
+
+        mmi_port_callbacks screen_info_port_callbacks {
+            screen_info_port_output_format_requested_cb,
+            screen_info_port_input_data_received_cb
+        };
+
+        mmi_port_h utterance_input_port = create_input_port(
+                INPUT_PORT_NAME_UTTERANCE,
+                MMI_DATA_TYPE_TEXT,
+                utterance_port_callbacks);
+
+        mmi_port_h mode_commands_input_port = create_input_port(
+                INPUT_PORT_NAME_MODE_COMMANDS,
+                MMI_DATA_TYPE_TEXT,
+                mode_commands_port_callbacks);
+
+        // TODO: check the type of input
+        mmi_port_h screen_info_input_port = create_input_port(
+                INPUT_PORT_NAME_SCREEN_INFO,
+                MMI_DATA_TYPE_ARRAY,
+                screen_info_port_callbacks);
+
+        mmi_port_h candidates_output_port = create_output_port(
+                OUTPUT_PORT_NAME_CANDIDATES,
+                MMI_DATA_TYPE_ARRAY);
+
+        mmi_port_h matched_result_output_port = create_output_port(
+                OUTPUT_PORT_NAME_MATCHED_RESULT,
+                MMI_DATA_TYPE_STRUCT);
+
+        mmi_port_h rejected_output_port = create_output_port(
+                OUTPUT_PORT_NAME_REJECTED,
+                MMI_DATA_TYPE_TEXT);
+
+        mmi_node_h voice_touch_node = nullptr;
+        mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH, &voice_touch_node);
+
+        // Add input ports
+        mmi_node_add_port(voice_touch_node, utterance_input_port);
+        mmi_node_add_port(voice_touch_node, mode_commands_input_port);
+        mmi_node_add_port(voice_touch_node, screen_info_input_port);
+
+        // Add output ports
+        mmi_node_add_port(voice_touch_node, candidates_output_port);
+        mmi_node_add_port(voice_touch_node, matched_result_output_port);
+        mmi_node_add_port(voice_touch_node, rejected_output_port);
+
+        mmi_node_set_callbacks(voice_touch_node, node_callbacks);
+        mmi_node_register(voice_touch_node);
+
+        mmi_node_destroy(voice_touch_node);
+    }
+
+} // extern "C"
diff --git a/plugins/workflows/meson.build b/plugins/workflows/meson.build
new file mode 100644 (file)
index 0000000..952a1b0
--- /dev/null
@@ -0,0 +1,4 @@
+subdir('script-parser')
+subdir('wakeupless-command')
+subdir('voice-touch')
+subdir('user-recognition')
diff --git a/plugins/workflows/script-parser/meson.build b/plugins/workflows/script-parser/meson.build
new file mode 100644 (file)
index 0000000..dd3564c
--- /dev/null
@@ -0,0 +1,25 @@
+mmi_module_script_parser_library_srcs = [
+       'mmi-module-script-parser.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_script_parser_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_script_parser_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_script_parser_library = library('mmi_module_script_parser',
+       mmi_module_script_parser_library_srcs,
+       include_directories : [ mmi_module_script_parser_include_dirs ],
+       dependencies : [mmi_module_script_parser_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/workflows/script-parser/mmi-module-script-parser.cpp b/plugins/workflows/script-parser/mmi-module-script-parser.cpp
new file mode 100644 (file)
index 0000000..b708256
--- /dev/null
@@ -0,0 +1,33 @@
+#include <mmi.h>
+#include <mmi-plugin-storage.h>
+#include <mmi-workflow.h>
+
+#include "mmi-log.h"
+
+#include <filesystem>
+#include <string>
+#include <vector>
+
+#include <dlog.h>
+#include <Ecore.h>
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_workflow_list()
+    {
+        const std::string plugin_module_path{"/usr/share/mmi/scripts"};
+        for (const auto & entry : std::filesystem::directory_iterator(plugin_module_path)) {
+            auto const pos = entry.path().string().find_last_of(".");
+            if (pos != std::string::npos) {
+                auto const ext = entry.path().string().substr(pos + 1);
+                if (ext == "mws") {
+                    mmi_workflow_h workflow = nullptr;
+                    mmi_workflow_create_from_script(entry.path().string().c_str(), &workflow);
+                    mmi_standard_workflow_register(workflow);
+                    mmi_workflow_destroy(workflow);
+                }
+            }
+        }
+    }
+
+} // extern "C"
diff --git a/plugins/workflows/user-recognition/meson.build b/plugins/workflows/user-recognition/meson.build
new file mode 100644 (file)
index 0000000..85328db
--- /dev/null
@@ -0,0 +1 @@
+install_data('user-recognition.mws', install_dir : mmi_prefix_scriptdir)
diff --git a/plugins/workflows/user-recognition/user-recognition.mws b/plugins/workflows/user-recognition/user-recognition.mws
new file mode 100644 (file)
index 0000000..49148b8
--- /dev/null
@@ -0,0 +1,21 @@
+@workflow
+name : USER_RECOGNITION
+
+@node-list
+[Source] MIC_AMBIENT as MIC
+[Source] CAMERA as CAMERA
+[Processor] FACE_RECOGNITION as FACE_RECOGNITION
+[Processor] SPEAKER_RECOGNITION as SPEAKER_RECOGNITION
+[Logic] USER_COMPARISON as USER_COMPARISON
+
+@link-list
+CAMERA.VIDEO -> FACE_RECOGNITION.VIDEO
+FACE_RECOGNITION.MATCHED_FACES -> USER_COMPARISON.PORT1
+MIC.AUDIO -> SPEAKER_RECOGNITION.AUDIO
+SPEAKER_RECOGNITION.MATCHED_SPEAKERS -> USER_COMPARISON.PORT2
+
+@attribute-list
+
+@output-list
+USER_COMPARISON.BYPASS_RESULT as SINGLE_MODAL_USER_ID
+USER_COMPARISON.COMPARISON_RESULT as MULTI_MODAL_USER_ID
diff --git a/plugins/workflows/voice-touch/meson.build b/plugins/workflows/voice-touch/meson.build
new file mode 100644 (file)
index 0000000..628d533
--- /dev/null
@@ -0,0 +1,25 @@
+mmi_module_voice_touch_library_srcs = [
+       'mmi-module-voice-touch.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_voice_touch_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_voice_touch_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_voice_touch_library = library('mmi_module_voice_touch',
+       mmi_module_voice_touch_library_srcs,
+       include_directories : [ mmi_module_voice_touch_include_dirs ],
+       dependencies : [mmi_module_voice_touch_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/workflows/voice-touch/mmi-module-voice-touch.cpp b/plugins/workflows/voice-touch/mmi-module-voice-touch.cpp
new file mode 100644 (file)
index 0000000..f8baebd
--- /dev/null
@@ -0,0 +1,150 @@
+#include <mmi.h>
+#include <mmi-plugin-storage.h>
+#include <mmi-workflow.h>
+
+#include "mmi-log.h"
+
+#include <string>
+#include <map>
+
+#include <dlog.h>
+#include <Ecore.h>
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-VOICE-TOUCH"
+
+#define ENABLE_TEMP_RESCAN
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_workflow_list()
+    {
+        const char *NODE_NAME_MIC = "MIC";
+        const char *NODE_NAME_ASR = "ASR";
+        const char *NODE_NAME_MATCH_MODE_COMMANDS = "MATCH_MODE_COMMANDS";
+        const char *NODE_NAME_MATCH_PREDEFINED_COMMANDS = "MATCH_PREDEFINED_COMMANDS";
+        const char *NODE_NAME_VOICE_TOUCH_PROCESSOR = "VOICE_TOUCH_PROCESSOR";
+        const char *NODE_NAME_SCREEN_ANALYZER = "SCREEN_ANALYZER";
+
+        mmi_workflow_h workflow = nullptr;
+        mmi_workflow_create(&workflow);
+
+        mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_VOICE_TOUCH);
+
+        mmi_node_h node_mic = nullptr;
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic);
+        mmi_workflow_node_add(workflow, NODE_NAME_MIC, node_mic);
+
+        mmi_node_h node_asr = nullptr;
+        mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr);
+        mmi_workflow_node_add(workflow, NODE_NAME_ASR, node_asr);
+
+        mmi_node_h node_match_mode_commands = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match_mode_commands);
+        mmi_workflow_node_add(workflow, NODE_NAME_MATCH_MODE_COMMANDS, node_match_mode_commands);
+
+        mmi_node_h node_match_predefined_commands = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match_predefined_commands);
+        mmi_workflow_node_add(workflow, NODE_NAME_MATCH_PREDEFINED_COMMANDS, node_match_predefined_commands);
+
+        mmi_node_h node_voice_touch = nullptr;
+        mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH, &node_voice_touch);
+        mmi_workflow_node_add(workflow, NODE_NAME_VOICE_TOUCH_PROCESSOR, node_voice_touch);
+
+        mmi_node_h node_screen_analyzer = nullptr;
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_SCREEN_ANALYZER, &node_screen_analyzer);
+        mmi_workflow_node_add(workflow, NODE_NAME_SCREEN_ANALYZER, node_screen_analyzer);
+
+#ifdef ENABLE_TEMP_RESCAN
+        /* This is a temporary node for rescanning the screen, it will be removed when the signal mechanism is ready */
+        mmi_node_h temp_node_match_rescan_command = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &temp_node_match_rescan_command);
+        mmi_workflow_node_add(workflow, "TEMP_RESCAN", temp_node_match_rescan_command);
+#endif
+
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MIC, "AUDIO", NODE_NAME_ASR, "AUDIO");
+
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_ASR, "FINAL_ASR", NODE_NAME_MATCH_MODE_COMMANDS, "TEXT");
+
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_MODE_COMMANDS, "MATCHED_CANDIDATE", NODE_NAME_VOICE_TOUCH_PROCESSOR, "MODE_COMMANDS");
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_MODE_COMMANDS, "REJECTED", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "TEXT");
+
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_PREDEFINED_COMMANDS, "REJECTED", NODE_NAME_VOICE_TOUCH_PROCESSOR, "UTTERANCE");
+
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_SCREEN_ANALYZER, "SCREEN_INFO", NODE_NAME_VOICE_TOUCH_PROCESSOR, "SCREEN_INFO");
+
+#ifdef ENABLE_TEMP_RESCAN
+        /* These are temporary links for rescanning the screen, it will be removed when the signal mechanism is ready */
+        mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_ASR, "FINAL_ASR", "TEMP_RESCAN", "TEXT");
+        mmi_workflow_link_nodes_by_names(workflow, "TEMP_RESCAN", "MATCHED_CANDIDATE", NODE_NAME_SCREEN_ANALYZER, "RESCAN");
+#endif
+
+        mmi_workflow_attribute_assign(workflow, "PREDEFINED_COMMANDS", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "CANDIDATES");
+
+        mmi_workflow_attribute_assign(workflow, "GRID_CONFIGURATION", NODE_NAME_VOICE_TOUCH_PROCESSOR, "GRID_CONFIGURATION");
+
+#ifdef ENABLE_TEMP_RESCAN
+        /* These are temporary attributes for rescanning the screen, it will be removed when the signal mechanism is ready */
+        mmi_workflow_attribute_assign(workflow, "REFRESH", NODE_NAME_SCREEN_ANALYZER, "RESCANNING");
+        mmi_workflow_attribute_assign(workflow, "TEMP_RESCAN_COMMAND", "TEMP_RESCAN", "CANDIDATES");
+#endif
+
+        mmi_workflow_output_assign(workflow, "PARTIAL_RESULT", NODE_NAME_ASR, "PARTIAL_ASR");
+        mmi_workflow_output_assign(workflow, "FINAL_RESULT", NODE_NAME_ASR, "FINAL_ASR");
+
+        mmi_workflow_output_assign(workflow, "MATCHED_CANDIDATE", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "MATCHED_COMMAND");
+
+        mmi_workflow_output_assign(workflow, "REJECTED", NODE_NAME_VOICE_TOUCH_PROCESSOR, "REJECTED");
+        mmi_workflow_output_assign(workflow, "CANDIDATES", NODE_NAME_VOICE_TOUCH_PROCESSOR, "CANDIDATES");
+        mmi_workflow_output_assign(workflow, "MATCHED_RESULT", NODE_NAME_VOICE_TOUCH_PROCESSOR, "MATCHED_RESULT");
+
+        mmi_primitive_value_h primitive_array = nullptr;
+        mmi_primitive_value_create_array(&primitive_array);
+
+        mmi_primitive_value_h predefined_command_grid = nullptr;
+        mmi_primitive_value_create_string("Grid", &predefined_command_grid);
+        mmi_primitive_value_add_array_element(primitive_array, predefined_command_grid);
+
+        mmi_primitive_value_h predefined_command_number = nullptr;
+        mmi_primitive_value_create_string("Number", &predefined_command_number);
+        mmi_primitive_value_add_array_element(primitive_array, predefined_command_number);
+
+        mmi_attribute_h attribute = nullptr;
+        mmi_attribute_create(primitive_array, "PREDEFINED_COMMANDS", &attribute);
+
+        mmi_workflow_attribute_set_default_value(workflow, attribute);
+
+        mmi_attribute_destroy(attribute);
+        mmi_primitive_value_destroy(primitive_array);
+
+#ifdef ENABLE_TEMP_RESCAN
+        /* This is a temporary attribute for rescanning the screen, it will be removed when the signal mechanism is ready */
+        mmi_primitive_value_h temp_rescan_command = nullptr;
+        mmi_primitive_value_create_string("Rescan", &temp_rescan_command);
+        mmi_attribute_h temp_attribute = nullptr;
+        mmi_attribute_create(temp_rescan_command, "TEMP_RESCAN_COMMAND", &temp_attribute);
+        mmi_workflow_attribute_set_default_value(workflow, temp_attribute);
+        mmi_attribute_destroy(temp_attribute);
+        mmi_primitive_value_destroy(temp_rescan_command);
+#endif
+
+        mmi_standard_workflow_register(workflow);
+
+#ifdef ENABLE_TEMP_RESCAN
+        /* This is a temporary node for rescanning the screen, it will be removed when the signal mechanism is ready */
+        mmi_node_destroy(temp_node_match_rescan_command);
+#endif
+
+        mmi_node_destroy(node_mic);
+        mmi_node_destroy(node_asr);
+        mmi_node_destroy(node_match_mode_commands);
+        mmi_node_destroy(node_match_predefined_commands);
+        mmi_node_destroy(node_voice_touch);
+        mmi_node_destroy(node_screen_analyzer);
+
+        mmi_workflow_destroy(workflow);
+    }
+
+} // extern "C"
diff --git a/plugins/workflows/voice-touch/voice-touch.mws b/plugins/workflows/voice-touch/voice-touch.mws
new file mode 100644 (file)
index 0000000..c4daa81
--- /dev/null
@@ -0,0 +1,19 @@
+@workflow
+name : VoiceTouch
+
+@node-list
+[Source] MIC_AMBIENT as MIC
+[Processor] ASR as ASR
+[Logic] FIXED_STRING_MATCH as MATCH
+[Logic] FIXED_STRING_MATCH as DUMMY
+
+@link-list
+MIC.AUDIO -> ASR.AUDIO
+ASR.FINAL_RESULT -> MATCH.TEXT
+ASR.FINAL_RESULT -> DUMMY.TEXT
+
+@attribute-list
+MATCH.CANDIDATES as COMMANDS
+
+@output-list
+MATCH.MATCHED_CANDIDATE as COMMAND
diff --git a/plugins/workflows/wakeupless-command/meson.build b/plugins/workflows/wakeupless-command/meson.build
new file mode 100644 (file)
index 0000000..55c517b
--- /dev/null
@@ -0,0 +1,25 @@
+mmi_module_wakeupless_command_library_srcs = [
+       'mmi-module-wakeupless-command.cpp',
+       ]
+
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_module_wakeupless_command_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       ]
+
+mmi_module_wakeupless_command_include_dirs = include_directories(
+       '.',
+       '../../../capi/',
+       )
+
+mmi_module_wakeupless_command_library = library('mmi_module_wakeupless_command',
+       mmi_module_wakeupless_command_library_srcs,
+       include_directories : [ mmi_module_wakeupless_command_include_dirs ],
+       dependencies : [mmi_module_wakeupless_command_deps],
+       install_dir : mmi_prefix_plugindir,
+       install : true
+       )
diff --git a/plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp b/plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp
new file mode 100644 (file)
index 0000000..62d0070
--- /dev/null
@@ -0,0 +1,69 @@
+#include <mmi.h>
+#include <mmi-plugin-storage.h>
+#include <mmi-workflow.h>
+
+#include <string>
+#include <map>
+
+#include <dlog.h>
+#include <Ecore.h>
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "MMI-MODULE-WAKEUPLESS-COMMAND"
+
+#define MMI_MODULE_LOG_(prio, tag, fmt, arg...) \
+               ({ do { \
+                               dlog_print(prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg); \
+               } while (0); })
+
+#define _D(fmt, args...) MMI_MODULE_LOG_(DLOG_DEBUG, LOG_TAG, fmt, ##args)
+#define _I(fmt, args...) MMI_MODULE_LOG_(DLOG_INFO, LOG_TAG, fmt, ##args)
+#define _W(fmt, args...) MMI_MODULE_LOG_(DLOG_WARN, LOG_TAG, fmt, ##args)
+#define _E(fmt, args...) MMI_MODULE_LOG_(DLOG_ERROR, LOG_TAG, fmt, ##args)
+
+extern "C" {
+
+    EXPORT_API void mmi_plugin_module_get_workflow_list()
+    {
+        mmi_workflow_h workflow = nullptr;
+        mmi_workflow_create(&workflow);
+
+        mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+        mmi_node_h node_mic = nullptr;
+        /* FIXME: node should be 'found' instead of 'created' */
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic);
+        mmi_workflow_node_add(workflow, "MIC", node_mic);
+
+        mmi_node_h node_asr = nullptr;
+        mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr);
+        mmi_workflow_node_add(workflow, "ASR", node_asr);
+
+        mmi_node_h node_match = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match);
+        mmi_workflow_node_add(workflow, "MATCH", node_match);
+
+        mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO");
+        mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_ASR", "MATCH", "TEXT");
+
+        mmi_workflow_attribute_assign(workflow, "COMMANDS", "MATCH", "CANDIDATES");
+        mmi_workflow_attribute_assign(workflow, "LANGUAGE", "ASR", "LANGUAGE");
+        mmi_workflow_attribute_assign(workflow, "STOP_METHOD", "ASR", "STOP_METHOD");
+        mmi_workflow_attribute_assign(workflow, "ASR_START", "ASR", "ASR_START");
+
+        mmi_workflow_output_assign(workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE");
+        mmi_workflow_output_assign(workflow, "UTTERANCE", "ASR", "PARTIAL_ASR");
+        mmi_workflow_output_assign(workflow, "UTTERANCE", "ASR", "FINAL_ASR");
+
+        mmi_standard_workflow_register(workflow);
+
+        mmi_node_destroy(node_mic);
+        mmi_node_destroy(node_asr);
+        mmi_node_destroy(node_match);
+
+        mmi_workflow_destroy(workflow);
+    }
+
+} // extern "C"
diff --git a/src/common/mmi-communication-channel.h b/src/common/mmi-communication-channel.h
new file mode 100644 (file)
index 0000000..c09948d
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "mmi-event-observer.h"
+#include "mmi-communication-message.h"
+#include "mmi-workflow-output-event.h"
+
+#include <memory>
+
+namespace mmi {
+
+namespace communication {
+
+enum class COMMUNICATION_CHANNEL_EVENT_TYPE {
+    CONNECTED,
+    DISCONNECTED,
+    MESSAGE_RECEIVED,
+};
+
+class CommunicationChannel : public SimpleEventObservable<COMMUNICATION_CHANNEL_EVENT_TYPE> {
+public:
+    CommunicationChannel() = default;
+    virtual ~CommunicationChannel() = default;
+
+    virtual int connect() = 0;
+    virtual int disconnect() = 0;
+    virtual int send(Message *message) = 0;
+};
+
+class CommunicationChannelManager : public CommunicationChannel,
+    public SimpleEventObserver<WORKFLOW_OUTPUT_EVENT_TYPE> {
+public:
+    CommunicationChannelManager() = default;
+    virtual ~CommunicationChannelManager() = default;
+};
+
+class CommunicationChannelClient : public CommunicationChannel {
+public:
+    CommunicationChannelClient() = default;
+    virtual ~CommunicationChannelClient() = default;
+};
+
+struct CommunicationChannelManagerFactory {
+    CommunicationChannelManagerFactory() = default;
+    virtual ~CommunicationChannelManagerFactory() = default;
+
+    virtual std::shared_ptr<CommunicationChannelManager> create_channel() = 0;
+};
+
+struct CommunicationChannelClientFactory {
+    CommunicationChannelClientFactory() = default;
+    virtual ~CommunicationChannelClientFactory() = default;
+
+    virtual std::shared_ptr<CommunicationChannelClient> create_channel() = 0;
+};
+
+}; // namespace communication
+
+}; // namespace mmi
diff --git a/src/common/mmi-communication-message.h b/src/common/mmi-communication-message.h
new file mode 100644 (file)
index 0000000..4fc7ca0
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+#include <vector>
+#include <functional>
+
+#include <bundle.h>
+
+#include "mmi.h"
+
+namespace mmi {
+
+namespace communication {
+
+enum class MESSAGE_GROUP {
+    CLIENT,
+    WORKFLOW,
+    NODE,
+};
+
+/* Client Messages */
+enum class CLIENT_MESSAGE_TYPE {
+    /* Client to Manager */
+    WORKFLOW_INSTANCE_CREATE,
+    WORKFLOW_INSTANCE_DESTROY,
+    WORKFLOW_INSTANCE_SET_ATTRIBUTE,
+    WORKFLOW_INSTANCE_ACTIVATE,
+    WORKFLOW_INSTANCE_DEACTIVATE,
+    /* Manager to Client */
+};
+
+struct Message {
+    Message(MESSAGE_GROUP group) : message_group{group} {}
+    virtual ~Message() {}
+    MESSAGE_GROUP message_group;
+    std::string sender;
+};
+
+struct ClientMessage : public Message {
+    ClientMessage(CLIENT_MESSAGE_TYPE type) : Message(MESSAGE_GROUP::CLIENT), message_type{type} {}
+    virtual ~ClientMessage() {}
+    CLIENT_MESSAGE_TYPE message_type;
+};
+
+struct ClientMessageWorkflowInstanceCreate : public ClientMessage {
+    ClientMessageWorkflowInstanceCreate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE} {}
+    mmi_standard_workflow_type_e workflow_type{MMI_STANDARD_WORKFLOW_NONE};
+    int local_workflow_instance_id{0};
+    void *user_data{nullptr};
+};
+
+struct ClientMessageWorkflowInstanceDestroy : public ClientMessage {
+    ClientMessageWorkflowInstanceDestroy() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY} {}
+    int local_workflow_instance_id{0};
+};
+
+struct ClientMessageWorkflowInstanceSetAttribute : public ClientMessage {
+    ClientMessageWorkflowInstanceSetAttribute() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE} {}
+    int local_workflow_instance_id{0};
+    mmi_attribute_h attribute{nullptr};
+};
+
+struct ClientMessageWorkflowInstanceActivate : public ClientMessage {
+    ClientMessageWorkflowInstanceActivate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE} {}
+    int local_workflow_instance_id{0};
+};
+
+struct ClientMessageWorkflowInstanceDeactivate : public ClientMessage {
+    ClientMessageWorkflowInstanceDeactivate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE} {}
+    int local_workflow_instance_id{0};
+};
+
+}; // namespace communication
+
+}; // namespace mmi
diff --git a/src/common/mmi-event-observer.h b/src/common/mmi-event-observer.h
new file mode 100644 (file)
index 0000000..84e5058
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <any>
+
+namespace mmi {
+
+/* SimpleEventObserver / SimpleEventObservable do not require EventSource to be specified. */
+
+template <typename EventType>
+class SimpleEventObserver {
+public:
+    SimpleEventObserver() = default;
+    virtual ~SimpleEventObserver() = default;
+
+    virtual void on_observer_event(const EventType &event, std::any data = std::any(nullptr)) = 0;
+};
+
+template <typename EventType>
+class SimpleEventObservable {
+public:
+    SimpleEventObservable() = default;
+    virtual ~SimpleEventObservable() = default;
+
+    void notify_observers(const EventType &event, std::any data = std::any(nullptr)) {
+        for (auto observer : m_observers) {
+            if (observer) {
+                observer->on_observer_event(event, data);
+            }
+        }
+    }
+    void add_observer(SimpleEventObserver<EventType> *observer) {
+        if (observer) {
+            m_observers.push_back(observer);
+        }
+    }
+    void remove_observer(SimpleEventObserver<EventType> *observer) {
+        m_observers.erase(
+            std::remove(m_observers.begin(), m_observers.end(), observer),
+            m_observers.end());
+    };
+
+private:
+    std::vector<SimpleEventObserver<EventType>*> m_observers;
+};
+
+/* EventObserver / EventObservable require EventSource to be specified.
+ * This is useful when you want to know which object triggered the event. */
+
+template <typename ObservableType, typename EventType>
+class EventObserver {
+public:
+    EventObserver() = default;
+    virtual ~EventObserver() = default;
+
+    virtual void on_observer_event(const ObservableType &source, const EventType &event, std::any data = std::any(nullptr)) = 0;
+};
+
+template <typename ObservableType, typename EventType>
+class EventObservable {
+public:
+    EventObservable() = default;
+    virtual ~EventObservable() = default;
+
+    void notify_observers(const ObservableType &source, const EventType &event, std::any data = std::any(nullptr)) {
+        for (auto observer : m_observers) {
+            if (observer) {
+                observer->on_observer_event(source, event, data);
+            }
+        }
+    }
+    void add_observer(EventObserver<ObservableType, EventType> *observer) {
+        if (observer) {
+            m_observers.push_back(observer);
+        }
+    }
+    void remove_observer(EventObserver<ObservableType, EventType> *observer) {
+        m_observers.erase(
+            std::remove(m_observers.begin(), m_observers.end(), observer),
+            m_observers.end());
+    };
+
+private:
+    std::vector<EventObserver<ObservableType, EventType>*> m_observers;
+};
+
+}
diff --git a/src/common/mmi-plugin-module-event.h b/src/common/mmi-plugin-module-event.h
new file mode 100644 (file)
index 0000000..5d784c5
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "mmi-event-observer.h"
+#include "mmi-node.h"
+#include "mmi-port.h"
+
+#include <memory>
+
+namespace mmi {
+
+enum class PLUGIN_MODULE_EVENT_TYPE {
+    OUTPUT_GENERATED,
+    SWITCH_EVENT_EMITTED,
+};
+
+struct PluginModuleEvent {
+    PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE type) : type(type) {}
+    virtual ~PluginModuleEvent() = default;
+
+    PLUGIN_MODULE_EVENT_TYPE type;
+};
+
+struct PluginModuleEventOutputGenerated : public PluginModuleEvent {
+    PluginModuleEventOutputGenerated() : PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED) {}
+    virtual ~PluginModuleEventOutputGenerated() {}
+
+    mmi_port_instance_h port_instance{nullptr};
+    mmi_data_h data{nullptr};
+};
+
+struct PluginModuleEventSwitchEventEmitted : public PluginModuleEvent {
+    PluginModuleEventSwitchEventEmitted() : PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED) {}
+    virtual ~PluginModuleEventSwitchEventEmitted() {}
+
+    mmi_node_instance_h node_instance{nullptr};
+    std::string controllee;
+    bool state{false};
+};
+
+}; // namespace mmi
diff --git a/src/common/mmi-workflow-event.h b/src/common/mmi-workflow-event.h
new file mode 100644 (file)
index 0000000..e9d6a55
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "mmi-event-observer.h"
+
+namespace mmi {
+
+enum class WORKFLOW_EVENT_TYPE {
+    WORKFLOW_CREATED,
+};
+
+struct WorkflowEvent {
+    WorkflowEvent(WORKFLOW_EVENT_TYPE type) : type(type) {}
+    virtual ~WorkflowEvent() = default;
+
+    WORKFLOW_EVENT_TYPE type;
+};
+
+struct WorkflowEventWorkflowCreated : public WorkflowEvent {
+    WorkflowEventWorkflowCreated() : WorkflowEvent(WORKFLOW_EVENT_TYPE::WORKFLOW_CREATED) {}
+    virtual ~WorkflowEventWorkflowCreated() {}
+
+    std::any workflow_instance{nullptr};
+};
+
+}; // namespace mmi
diff --git a/src/common/mmi-workflow-output-event.h b/src/common/mmi-workflow-output-event.h
new file mode 100644 (file)
index 0000000..818b3e5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "mmi-event-observer.h"
+
+#include <memory>
+#include <bundle.h>
+
+namespace mmi {
+
+enum class WORKFLOW_OUTPUT_EVENT_TYPE {
+    OUTPUT_GENERATED,
+};
+
+struct WorkflowOutputEvent {
+    WorkflowOutputEvent(WORKFLOW_OUTPUT_EVENT_TYPE type) : type(type) {}
+    virtual ~WorkflowOutputEvent() = default;
+
+    WORKFLOW_OUTPUT_EVENT_TYPE type;
+};
+
+struct WorkflowOutputEventOutputGenerated : public WorkflowOutputEvent {
+    WorkflowOutputEventOutputGenerated() : WorkflowOutputEvent(WORKFLOW_OUTPUT_EVENT_TYPE::OUTPUT_GENERATED) {}
+    virtual ~WorkflowOutputEventOutputGenerated() {}
+
+    bundle *data{nullptr};
+    std::string source_name;
+    std::string client_id;
+    int local_workflow_instance_id{0};
+};
+
+}; // namespace mmi
index 7803d2e..e9bae44 100644 (file)
@@ -1,59 +1,3 @@
-mmi_srcs = [
-       'mmi.h',
-       'mmi.c',
-       'mmi-ipc.h',
-       'mmi-ipc.c',
-       'mmi-dbg.h',
-       'mmi_proxy.h',
-       'mmi_proxy.c',
-       'mmi-client.h',
-       'mmi-client.c',
-       ]
-
-install_headers(
-       'mmi.h',
-       )
-
-glib_dep = dependency('glib-2.0', method : 'pkg-config')
-gio_dep = dependency('gio-2.0', method : 'pkg-config')
-bundle_dep = dependency('bundle', method : 'pkg-config')
-dlog_dep = dependency('dlog', method : 'pkg-config')
-rpc_port_dep = dependency('rpc-port', method : 'pkg-config')
-libtzplatform_config_dep = dependency('libtzplatform-config')
-ecore_dep = dependency('ecore', method : 'pkg-config')
-
-mmi_deps = [
-       ecore_dep,
-       glib_dep,
-       gio_dep,
-       bundle_dep,
-       dlog_dep,
-       rpc_port_dep,
-       libtzplatform_config_dep]
-
-mmi_include_dirs = include_directories(
-       '.'
-       )
-
-mmi_lib = shared_library(
-       'mmi',
-       mmi_srcs,
-       dependencies : [mmi_deps],
-       include_directories : [mmi_include_dirs],
-       version : meson.project_version(),
-       install : true
-       )
-
-pkgconfig.generate(
-       filebase : 'mmi',
-       name : 'mmi',
-       description : 'Multi-modal Interaction Framework Library',
-       version : meson.project_version(),
-       libraries : mmi_lib
-       )
-
-mmi_declared_dep = declare_dependency(
-       link_with : mmi_lib,
-       dependencies : [mmi_deps],
-       include_directories : [mmi_include_dirs]
-       )
+subdir('mmi')
+subdir('mmi-manager')
+subdir('mmi-cli')
diff --git a/src/mmi-cli/cliecoresession.h b/src/mmi-cli/cliecoresession.h
new file mode 100644 (file)
index 0000000..fb03f33
--- /dev/null
@@ -0,0 +1,106 @@
+#include "cli/cli.h"
+
+#include <string>
+#include <iostream>
+#include <stdexcept> // std::invalid_argument
+
+#include <Ecore.h>
+
+namespace cli
+{
+
+class CliEcoreSession : public CliSession
+{
+public:
+    /// @throw std::invalid_argument if @c _in or @c out are invalid streams
+    explicit CliEcoreSession(Cli& _cli, std::istream& _in=std::cin, std::ostream& _out=std::cout) :
+        CliSession(_cli, _out, 1),
+        exit(false),
+        loop(false),
+        in(_in),
+        handler(nullptr)
+    {
+        if (!_in.good()) throw std::invalid_argument("istream invalid");
+        if (!_out.good()) throw std::invalid_argument("ostream invalid");
+        ExitAction(
+            [this](std::ostream&) noexcept
+        {
+            exit = true;
+            ecore_main_loop_quit();
+        }
+        );
+    }
+    std::istream& GetInputStream() {
+        return in;
+    }
+    static Eina_Bool Handler(void *data, int type, void *event)
+    {
+        CliEcoreSession* session = static_cast<CliEcoreSession*>(data);
+        if (session->exit) return ECORE_CALLBACK_CANCEL;
+
+        if (type == ECORE_EVENT_SIGNAL_EXIT)
+        {
+            session->Prompt();
+
+            std::string line;
+            std::istream& in = session->GetInputStream();
+            if (!in.good())
+                session->Exit();
+            std::getline(in, line);
+            if (in.eof())
+                session->Exit();
+            else
+                session->Feed(line);
+        }
+
+        return ECORE_CALLBACK_RENEW;
+    }
+    void Start()
+    {
+        Enter();
+
+        ecore_init();
+
+        handler = ecore_event_handler_add(
+                      ECORE_EVENT_SIGNAL_EXIT, Handler, this);
+
+        while(!exit && !loop) {
+            Prompt();
+
+            std::string line;
+            if (!in.good())
+                Exit();
+            std::getline(in, line);
+            if (in.eof())
+                Exit();
+            else
+                Feed(line);
+        }
+    }
+    void Loop()
+    {
+        if (loop) {
+            std::cout << "Already looping" << std::endl;
+            return;
+        }
+
+        std::cout << "Running ecore main loop, please press <Ctrl+C> to enter commands" << std::endl;
+
+        loop = true;
+        ecore_main_loop_begin();
+    }
+    void Finish()
+    {
+        if (handler) ecore_event_handler_del(handler);
+
+        ecore_shutdown();
+    }
+
+private:
+    bool exit;
+    bool loop;
+    std::istream& in;
+    Ecore_Event_Handler *handler;
+};
+
+} // namespace cli
diff --git a/src/mmi-cli/meson.build b/src/mmi-cli/meson.build
new file mode 100644 (file)
index 0000000..0512065
--- /dev/null
@@ -0,0 +1,56 @@
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+rpc_port_dep = dependency('rpc-port')
+
+mmi_cli_deps = [
+       mmi_declared_dep,
+       dlog_dep,
+       ecore_dep,
+       rpc_port_dep,
+       ]
+
+mmi_cli_include_dirs = include_directories(
+       '.',
+       '../common/',
+       '../../capi/',
+       '../../external/',
+       )
+
+mmi_cli_declared_dep = declare_dependency(
+       dependencies : [mmi_cli_deps],
+       include_directories : [mmi_cli_include_dirs]
+       )
+
+mmi_cli_program_srcs = [
+       'mmi-cli.cpp'
+       ]
+
+mmi_cli_program_deps = [
+       mmi_cli_declared_dep
+       ]
+
+executable('mmi-cli',
+       mmi_cli_program_srcs,
+       include_directories : [ mmi_cli_include_dirs ],
+       dependencies : [mmi_cli_program_deps],
+       install_dir : mmi_prefix_bindir,
+       install : true,
+       pie : true
+       )
+
+mmi_cli_node_tester_program_srcs = [
+       'mmi-cli-node-tester.cpp'
+       ]
+
+mmi_cli_node_tester_program_deps = [
+       mmi_cli_declared_dep
+       ]
+
+executable('mmi-cli-node-tester',
+       mmi_cli_node_tester_program_srcs,
+       include_directories : [ mmi_cli_include_dirs ],
+       dependencies : [mmi_cli_program_deps],
+       install_dir : mmi_prefix_bindir,
+       install : true,
+       pie : true
+       )
diff --git a/src/mmi-cli/mmi-cli-node-tester.cpp b/src/mmi-cli/mmi-cli-node-tester.cpp
new file mode 100644 (file)
index 0000000..3a4a9c4
--- /dev/null
@@ -0,0 +1,822 @@
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <dlfcn.h>
+
+#include "cliecoresession.h"
+
+#include "cli/cli.h"
+
+#include "mmi.h"
+#include "mmi-plugin-module.h"
+#include "mmi-plugin-storage.h"
+
+#define MMI_CLI_LOG_(fmt, arg...) \
+               ({ do { \
+                               printf("%s(%d) > " fmt "\n", __func__, __LINE__, ##arg); \
+               } while (0); })
+
+#define _D(fmt, args...) MMI_CLI_LOG_(fmt, ##args)
+#define _I(fmt, args...) MMI_CLI_LOG_(fmt, ##args)
+#define _W(fmt, args...) MMI_CLI_LOG_(fmt, ##args)
+#define _E(fmt, args...) MMI_CLI_LOG_(fmt, ##args)
+
+class Program;
+
+struct PortInstance
+{
+    std::string m_name;
+    mmi_port_type_e m_port_type;
+    mmi_data_type_e m_data_type;
+    mmi_port_callbacks m_callbacks{nullptr,};
+};
+
+struct NodeInstance
+{
+    mmi_standard_node_type_e m_node_type;
+
+    mmi_node_callbacks m_callbacks{nullptr,};
+
+    std::vector<PortInstance*> m_ports;
+};
+
+enum class AttributeTestType {
+    Int,
+    Text,
+    Bool,
+    TextArray,
+
+    MaxCount,
+};
+
+typedef struct {
+    std::string name;
+    AttributeTestType type;
+    std::string description;
+    std::function<void(Program*, mmi_attribute_h*)> attribute_generator;
+} AttributeTestEntry;
+
+enum class DataTestType {
+    Int,
+    Text,
+    Bool,
+    TextArray,
+    ScreenInfo,
+    UserRecognition,
+    Audio,
+    Video,
+
+    MaxCount,
+};
+
+typedef struct {
+    std::string name;
+    DataTestType type;
+    std::string description;
+    std::function<void(Program*, PortInstance*)> data_generator;
+} DataTestEntry;
+
+class Program
+{
+public:
+    Program();
+    ~Program();
+
+    static void log_mmi_data(PortInstance *port_instance, mmi_data_h data);
+    static void port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data);
+    void load_module(std::string path);
+    void unload_module();
+    std::string read_string_value(std::string prompt);
+    int read_int_value(std::string prompt);
+    void create_node_instance();
+    NodeInstance* get_node_instance();
+    void destroy_node_instance();
+    void activate_node_instance();
+    void deactivate_node_instance();
+    void set_attribute(AttributeTestType type);
+    void push_data(DataTestType type);
+    void set_ecore_session(cli::CliEcoreSession *session);
+
+private:
+    cli::CliEcoreSession *m_session{nullptr};
+    void* m_handle{nullptr};
+    mmi_plugin_module_node_list_s m_node_list;
+
+    std::map<int, NodeInstance*> m_node_instance_map;
+    int m_node_instance_index{0};
+};
+
+static Program g_program;
+
+AttributeTestEntry g_attribute_test_entries[] = {
+    {"int", AttributeTestType::Int, "Set an integer attribute",
+        [](Program *program, mmi_attribute_h *attribute) {
+            if (!program || !attribute) {
+                return;
+            }
+            std::string name = program->read_string_value("Enter attribute name: ");
+            std::string input = program->read_string_value("Enter attribute value: ");
+            int value = 0;
+            try {
+                value = std::stoi(input);
+            } catch (std::invalid_argument& e) {
+                _E("Invalid argument: %s", e.what());
+                return;
+            } catch (std::out_of_range& e) {
+                _E("Out of range: %s", e.what());
+                return;
+            }
+            mmi_primitive_value_h primitive_value = nullptr;
+
+            mmi_primitive_value_create_int(value, &primitive_value);
+            mmi_attribute_create(primitive_value, name.c_str(), attribute);
+
+            mmi_primitive_value_destroy(primitive_value);
+        }
+    },
+    {"text", AttributeTestType::Text, "Set a text attribute",
+        [](Program *program, mmi_attribute_h *attribute) {
+            if (!program || !attribute) {
+                return;
+            }
+            std::string name = program->read_string_value("Enter attribute name: ");
+            std::string value = program->read_string_value("Enter attribute value: ");
+            mmi_primitive_value_h primitive_value = nullptr;
+
+            mmi_primitive_value_create_string(value.c_str(), &primitive_value);
+            mmi_attribute_create(primitive_value, name.c_str(), attribute);
+
+            mmi_primitive_value_destroy(primitive_value);
+        }
+    },
+    {"bool", AttributeTestType::Bool, "Set a boolean attribute",
+        [](Program *program, mmi_attribute_h *attribute) {
+            if (!program || !attribute) {
+                return;
+            }
+            std::string name = program->read_string_value("Enter attribute name: ");
+            std::string input = program->read_string_value("Enter attribute value: ");
+            int value = 0;
+            try {
+                value = std::stoi(input);
+            } catch (std::invalid_argument& e) {
+                _E("Invalid argument: %s", e.what());
+                return;
+            } catch (std::out_of_range& e) {
+                _E("Out of range: %s", e.what());
+                return;
+            }
+            mmi_primitive_value_h primitive_value = nullptr;
+
+            mmi_primitive_value_create_bool((value != 0), &primitive_value);
+            mmi_attribute_create(primitive_value, name.c_str(), attribute);
+
+            mmi_primitive_value_destroy(primitive_value);
+        }
+    },
+    {"text_array", AttributeTestType::TextArray, "Set a text array attribute",
+        [](Program *program, mmi_attribute_h *attribute) {
+            if (!program || !attribute) {
+                return;
+            }
+            std::string name = program->read_string_value("Enter attribute name: ");
+            /* Currently we only support predefined array with string type */
+            constexpr size_t COMMAND_NUM = 2;
+            const char *commands[] = {"Open", "Close"};
+            mmi_attribute_create_string_array(name.c_str(), commands, COMMAND_NUM, attribute);
+        }
+    },
+};
+
+DataTestEntry g_data_test_entries[] = {
+    {"int", DataTestType::Int, "Push an integer data",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            mmi_data_h data = nullptr;
+            int value = program->read_int_value("Enter data value: ");
+            mmi_data_create_int(value, &data);
+            if (data) {
+                port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                mmi_data_destroy(data);
+            }
+        }
+    },
+    {"text", DataTestType::Text, "Push a text data",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            mmi_data_h data = nullptr;
+            std::string value = program->read_string_value("Enter data value: ");
+            mmi_data_create_text(value.c_str(), &data);
+            if (data) {
+                port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                mmi_data_destroy(data);
+            }
+        }
+    },
+    {"screen_info", DataTestType::ScreenInfo, "Push a screen info data",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            mmi_data_h data = nullptr;
+            /* Currently we only support predefined screen info */
+            mmi_data_create_array(&data);
+            for (size_t loop = 0;loop < 5;loop++) {
+                mmi_data_h struct_data;
+                mmi_data_create_struct(&struct_data);
+
+                mmi_data_h coord_x;
+                mmi_data_create_int(10 * loop, &coord_x);
+                mmi_data_set_struct_element(struct_data, "coord_x", coord_x);
+
+                mmi_data_h coord_y;
+                mmi_data_create_int(10 * loop, &coord_y);
+                mmi_data_set_struct_element(struct_data, "coord_y", coord_y);
+
+                mmi_data_h width;
+                mmi_data_create_int(100, &width);
+                mmi_data_set_struct_element(struct_data, "width", width);
+
+                mmi_data_h height;
+                mmi_data_create_int(100, &height);
+                mmi_data_set_struct_element(struct_data, "height", height);
+
+                mmi_data_h object_id;
+                mmi_data_create_text(
+                        (std::string("id_") + std::to_string(loop)).c_str(), &object_id);
+                mmi_data_set_struct_element(struct_data, "object_id", object_id);
+
+                mmi_data_h label;
+                mmi_data_create_text(
+                        (std::string("label") + std::to_string(loop)).c_str(), &label);
+                mmi_data_set_struct_element(struct_data, "label", label);
+
+                mmi_data_h role;
+                mmi_data_create_text(
+                        (std::string("role") + std::to_string(loop)).c_str(), &role);
+                mmi_data_set_struct_element(struct_data, "role", role);
+
+                mmi_data_add_array_element(data, struct_data);
+            }
+            if (data) {
+                port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                mmi_data_destroy(data);
+            }
+        }
+    },
+    {"user_recognition", DataTestType::UserRecognition, "Push a user recognition info data",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            mmi_data_h data = nullptr;
+            mmi_data_create_struct(&data);
+
+            std::string value = program->read_string_value("Enter fromNode name: ");
+            mmi_data_h from_node = nullptr;
+            mmi_data_create_text(value.c_str(), &from_node);
+            mmi_data_set_struct_element(data, "fromNode", from_node);
+
+            /* Currently we only support predefined user recognition info */
+            mmi_data_h candidates = nullptr;
+            mmi_data_create_array(&candidates);
+            for (size_t loop = 0;loop < 2;loop++) {
+                mmi_data_h struct_data;
+                mmi_data_create_struct(&struct_data);
+
+                mmi_data_h user_id;
+                mmi_data_create_text(
+                        (std::string("user") + std::to_string(loop)).c_str(), &user_id);
+                mmi_data_set_struct_element(struct_data, "id", user_id);
+
+                mmi_data_h user_name;
+                mmi_data_create_text(
+                        (std::string("name") + std::to_string(loop)).c_str(), &user_name);
+                mmi_data_set_struct_element(struct_data, "name", user_name);
+
+                mmi_data_h score;
+                mmi_data_create_float(91.5f + loop, &score);
+                mmi_data_set_struct_element(struct_data, "confidence", score);
+
+                mmi_data_add_array_element(candidates, struct_data);
+            }
+            mmi_data_set_struct_element(data, "recognizedCandidates", candidates);
+
+            if (data) {
+                port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                mmi_data_destroy(data);
+            }
+        }
+    },
+    {"audio", DataTestType::Audio, "Push audio data from file",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            std::string value = program->read_string_value("Enter audio file path : ");
+            const size_t read_size = 640;
+            char frame_buf[read_size];
+            FILE *fp = fopen(value.c_str(), "rb");
+            if (fp) {
+                size_t size = 0;
+                while ((size = fread(frame_buf, sizeof(char), read_size, fp)) > 0) {
+                    mmi_data_h data = nullptr;
+                    mmi_data_create_audio(frame_buf, size, &data);
+                    if (data) {
+                        port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                        mmi_data_destroy(data);
+                    }
+                }
+                fclose(fp);
+            } else {
+                _E("Failed to open file %s", value.c_str());
+            }
+        }
+    },
+    {"video", DataTestType::Video, "Push video data from file",
+        [](Program *program, PortInstance *port_instance) {
+            if (!program || !port_instance) {
+                return;
+            }
+            std::string value = program->read_string_value("Enter video file path : ");
+            const size_t read_size = 921600;    // 640*480*3
+            char frame_buf[read_size];
+            FILE *fp = fopen(value.c_str(), "rb");
+            if (fp) {
+                size_t size;
+                while ((size = fread(frame_buf, sizeof(char), read_size, fp)) > 0) {
+                    mmi_data_h data = nullptr;
+                    mmi_data_create_video(frame_buf, size, &data);
+                    if (data) {
+                        port_instance->m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(port_instance), data);
+                        mmi_data_destroy(data);
+                    }
+                }
+                fclose(fp);
+            } else {
+                _E("Failed to open file %s", value.c_str());
+            }
+        }
+    },
+};
+
+Program::Program() {
+    memset(&m_node_list, 0, sizeof(mmi_plugin_module_node_list_s));
+}
+Program::~Program() {
+    unload_module();
+}
+
+void Program::log_mmi_data(PortInstance *port_instance, mmi_data_h data) {
+    mmi_data_type_e type;
+    mmi_data_get_type(data, &type);
+    switch(type) {
+        case MMI_DATA_TYPE_BOOLEAN:
+            {
+                bool value;
+                mmi_data_get_bool(data, &value);
+                _D("Port %s generated data type %d (BOOLEAN), value %d", port_instance->m_name.c_str(), type, value);
+            }
+            break;
+        case MMI_DATA_TYPE_INTEGER:
+            {
+                int value;
+                mmi_data_get_int(data, &value);
+                _D("Port %s generated data type %d (INTEGER), value %d", port_instance->m_name.c_str(), type, value);
+            }
+            break;
+        case MMI_DATA_TYPE_FLOAT:
+            {
+                float value;
+                mmi_data_get_float(data, &value);
+                _D("Port %s generated data type %d (FLOAT), value %f", port_instance->m_name.c_str(), type, value);
+            }
+            break;
+        case MMI_DATA_TYPE_TEXT:
+            {
+                const char *value = nullptr;
+                mmi_data_get_text(data, &value);
+                _D("Port %s generated data type %d (TEXT), value %s", port_instance->m_name.c_str(), type, value);
+            }
+            break;
+        case MMI_DATA_TYPE_USER_IDENTIFICATION:
+            {
+                const void *ptr = nullptr;
+                size_t len = 0;
+                mmi_data_get_user_identification(data, &ptr, &len);
+                std::string hex_string;
+                for (size_t i = 0; i < len; i++) {
+                    char buf[3] = {0, };
+                    snprintf(buf, sizeof(buf), "%02x", ((unsigned char*)ptr)[i]);
+                    hex_string += buf;
+                }
+                _D("Port %s generated data type %d (USER_IDENTIFICATION), size %zu, hex %s",
+                        port_instance->m_name.c_str(), type, len, hex_string.c_str());
+            }
+            break;
+        case MMI_DATA_TYPE_ARRAY:
+            {
+                size_t count = 0;
+                mmi_data_get_array_count(data, &count);
+
+                for (size_t i = 0; i < count; i++) {
+                    mmi_data_h element = nullptr;
+                    mmi_data_get_array_element(data, i, &element);
+                    _D("Array Element %zu", i);
+                    log_mmi_data(port_instance, element);
+                }
+            }
+            break;
+        case MMI_DATA_TYPE_STRUCT:
+            {
+                size_t count;
+                mmi_data_get_struct_count(data, &count);
+
+                for (size_t i = 0; i < count; i++) {
+                    const char *element_name = nullptr;
+                    mmi_data_get_struct_element_name(data, i, &element_name);
+                    _D("Struct Element %zu : %s", i, element_name);
+                    mmi_data_h element_value = nullptr;
+                    mmi_data_get_struct_element_value(data, i, &element_value);
+                    log_mmi_data(port_instance, element_value);
+                }
+            }
+            break;
+        default:
+            _D("Port %s generated data type %d", port_instance->m_name.c_str(), type);
+            break;
+    }
+}
+
+void Program::port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data) {
+    if (port == nullptr || data == nullptr) {
+        _E("Invalid parameter : port(%p), data(%p)", port, data);
+        return;
+    }
+
+    PortInstance *port_instance = static_cast<PortInstance*>(port);
+    log_mmi_data(port_instance, data);
+}
+
+void Program::load_module(std::string path) {
+    mmi_plugin_module_event_handler_info_s module_event_handler_info{nullptr, };
+    module_event_handler_info.port_instance_output_handler = port_instance_output_handler;
+
+    mmi_plugin_storage_set_plugin_module_event_handler(module_event_handler_info, nullptr);
+
+    m_handle = dlopen(path.c_str(), RTLD_NOW);
+    if (!m_handle) {
+        _E("Failed to load plugin module: %s", dlerror());
+        return;
+    }
+
+    mmi_plugin_storage_set_current_module_identifier(path.c_str());
+    auto prepare_node_list = reinterpret_cast<mmi_plugin_module_get_node_list_func>(
+            dlsym(m_handle, MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME));
+    if (!prepare_node_list) {
+        _E("Failed to find symbol: %s", dlerror());
+        return;
+    }
+
+    prepare_node_list();
+
+    m_node_list = mmi_plugin_storage_get_node_list(path.c_str());
+
+    /* Dump node_list */
+    _D("node_list.count: %zu", m_node_list.node_count);
+    for (size_t i = 0;i < m_node_list.node_count;i++) {
+        mmi_node_h node = m_node_list.nodes[i];
+        mmi_standard_node_type_e type;
+        mmi_node_get_type(node, &type);
+        mmi_node_callbacks callbacks;
+        mmi_node_get_callbacks(node, &callbacks);
+        size_t port_count;
+        _D("node_info[%zu].type: %d", i, type);
+        _D("node_info[%zu].callbacks : %p %p %p %p %p %p", i,
+                callbacks.initialized_cb,
+                callbacks.deinitialized_cb,
+                callbacks.attribute_set_cb,
+                callbacks.activated_cb,
+                callbacks.deactivated_cb,
+                callbacks.signal_received_cb);
+        mmi_node_get_port_count(node, &port_count);
+        _D("node_info[%zu].port_list.count: %zu", i, port_count);
+        for (size_t j = 0;j < port_count;j++) {
+            mmi_port_h port;
+            mmi_node_get_port(node, j, &port);
+            char *name = nullptr;
+            size_t length = 0;
+            mmi_port_get_name(port, &name, &length);
+            mmi_port_type_e port_type;
+            mmi_port_get_type(port, &port_type);
+            mmi_data_type_e data_type;
+            mmi_port_get_data_type(port, &data_type);
+            mmi_port_callbacks port_callbacks;
+            mmi_port_get_callbacks(port, &port_callbacks);
+            _D("node_info[%zu].port_info[%zu].name: %s", i, j, name);
+            _D("node_info[%zu].port_info[%zu].type: %d", i, j, port_type);
+            _D("node_info[%zu].port_info[%zu].data_type: %d", i, j, data_type);
+            _D("node_info[%zu].port_info[%zu].callbacks: %p %p", i, j,
+                    port_callbacks.output_format_requested_cb,
+                    port_callbacks.input_data_received_cb);
+        }
+    }
+}
+
+void Program::unload_module() {
+    if (m_handle) dlclose(m_handle);
+    m_handle = nullptr;
+}
+
+std::string Program::read_string_value(std::string prompt) {
+    if (!m_session) {
+        _E("Session is not created");
+        return "";
+    }
+    std::cout << prompt;
+    std::string value;
+    std::getline(m_session->GetInputStream(), value);
+    return value;
+}
+
+int Program::read_int_value(std::string prompt) {
+    int value = -1;
+    std::string string_value = read_string_value(prompt);
+    try {
+        value = std::stoi(string_value);
+    } catch (std::invalid_argument& e) {
+        _E("Invalid argument: %s", e.what());
+        return -1;
+    } catch (std::out_of_range& e) {
+        _E("Out of range: %s", e.what());
+        return -1;
+    }
+    return value;
+}
+
+void Program::create_node_instance() {
+    NodeInstance *node_instance = new NodeInstance();
+
+    /* Currently only one node is supported */
+    mmi_node_h node = m_node_list.nodes[0];
+    mmi_standard_node_type_e type;
+    mmi_node_get_type(node, &type);
+    mmi_node_callbacks callbacks;
+    mmi_node_get_callbacks(node, &callbacks);
+
+    node_instance->m_node_type = type;
+    node_instance->m_callbacks = callbacks;
+
+    mmi_plugin_module_node_instance_info_s node_instance_info;
+    node_instance_info.node = static_cast<mmi_node_instance_h>(node_instance);
+    node_instance_info.port_info_count = 0;
+
+    size_t port_count;
+    mmi_node_get_port_count(node, &port_count);
+    for (size_t j = 0;j < port_count;j++) {
+        mmi_port_h port;
+        mmi_node_get_port(node, j, &port);
+        char *name = nullptr;
+        size_t length = 0;
+        mmi_port_get_name(port, &name, &length);
+        mmi_port_type_e port_type;
+        mmi_port_get_type(port, &port_type);
+        mmi_data_type_e data_type;
+        mmi_port_get_data_type(port, &data_type);
+        mmi_port_callbacks port_callbacks;
+        mmi_port_get_callbacks(port, &port_callbacks);
+
+        PortInstance *port_instance = new PortInstance();
+        port_instance->m_name = name;
+        port_instance->m_port_type = port_type;
+        port_instance->m_data_type = data_type;
+        port_instance->m_callbacks = port_callbacks;
+
+        node_instance->m_ports.push_back(port_instance);
+
+        node_instance_info.port_infos[node_instance_info.port_info_count].port =
+            static_cast<mmi_port_instance_h>(port_instance);
+        snprintf(node_instance_info.port_infos[node_instance_info.port_info_count].name, MMI_NAME_MAX_LENGTH, "%s", name);
+
+        node_instance_info.port_info_count++;
+    }
+
+    mmi_plugin_storage_add_node_instance_info(&node_instance_info);
+
+    m_node_instance_map[m_node_instance_index] = node_instance;
+    node_instance->m_callbacks.initialized_cb(static_cast<mmi_node_instance_h>(node_instance));
+
+    _D("Created node instance: %zu", m_node_instance_index);
+
+    m_node_instance_index++;
+}
+
+NodeInstance* Program::get_node_instance() {
+    NodeInstance *node_instance = nullptr;
+    if (m_node_instance_map.size() == 1) {
+        node_instance = m_node_instance_map.begin()->second;
+    } else {
+        int index = read_int_value("Enter instance id: ");
+        auto it = m_node_instance_map.find(index);
+        if (it == m_node_instance_map.end()) {
+            _E("Failed to find node instance: %d", index);
+            return nullptr;
+        }
+        node_instance = it->second;
+    }
+    return node_instance;
+}
+
+void Program::destroy_node_instance() {
+    NodeInstance *node_instance = get_node_instance();
+    if (!node_instance) return;
+
+    node_instance->m_callbacks.deinitialized_cb(static_cast<mmi_node_instance_h>(node_instance));
+    mmi_plugin_storage_remove_node_instance_info(static_cast<mmi_node_instance_h>(node_instance));
+    for (auto port_instance : node_instance->m_ports) {
+        delete port_instance;
+    }
+    delete node_instance;
+
+    /* Erase node_instance from map */
+    for (auto it = m_node_instance_map.begin();it != m_node_instance_map.end();it++) {
+        if (it->second == node_instance) {
+            m_node_instance_map.erase(it);
+            break;
+        }
+    }
+}
+
+void Program::activate_node_instance() {
+    NodeInstance *node_instance = get_node_instance();
+    if (!node_instance) return;
+
+    node_instance->m_callbacks.activated_cb(static_cast<mmi_node_instance_h>(node_instance));
+}
+
+void Program::deactivate_node_instance() {
+    NodeInstance *node_instance = get_node_instance();
+    if (!node_instance) return;
+
+    node_instance->m_callbacks.deactivated_cb(static_cast<mmi_node_instance_h>(node_instance));
+}
+
+void Program::set_attribute(AttributeTestType type) {
+    NodeInstance *node_instance = get_node_instance();
+    if (!node_instance) return;
+
+    mmi_attribute_h attribute = nullptr;
+
+    if (type >= AttributeTestType::MaxCount) {
+        _E("Invalid attribute type");
+        return;
+    }
+
+    size_t entry_index = static_cast<size_t>(AttributeTestType::MaxCount);
+    for (size_t i = 0;i < sizeof(g_attribute_test_entries) / sizeof(g_attribute_test_entries[0]);i++) {
+        if (g_attribute_test_entries[i].type == type) {
+            entry_index = i;
+            break;
+        }
+    }
+    if (entry_index == static_cast<size_t>(AttributeTestType::MaxCount)) {
+        _E("Failed to find attribute type: %d", type);
+        return;
+    }
+    g_attribute_test_entries[entry_index].attribute_generator(this, &attribute);
+
+    node_instance->m_callbacks.attribute_set_cb(static_cast<mmi_node_instance_h>(node_instance), attribute);
+    mmi_attribute_destroy(attribute);
+}
+
+void Program::push_data(DataTestType type) {
+    NodeInstance *node_instance = get_node_instance();
+    if (!node_instance) return;
+
+    if (type >= DataTestType::MaxCount) {
+        _E("Invalid data type");
+        return;
+    }
+
+    std::string name = read_string_value("Enter port name: ");
+    for (auto port_instance : node_instance->m_ports) {
+        if (port_instance->m_name == name) {
+            mmi_data_h data = nullptr;
+
+            size_t entry_index = static_cast<size_t>(DataTestType::MaxCount);
+            for (size_t i = 0;i < sizeof(g_data_test_entries) / sizeof(g_data_test_entries[0]);i++) {
+                if (g_data_test_entries[i].type == type) {
+                    entry_index = i;
+                    break;
+                }
+            }
+            if (entry_index == static_cast<size_t>(DataTestType::MaxCount)) {
+                _E("Failed to find data type");
+                return;
+            }
+            g_data_test_entries[entry_index].data_generator(this, port_instance);
+
+            break;
+        }
+    }
+}
+
+void Program::set_ecore_session(cli::CliEcoreSession *session) {
+    m_session = session;
+}
+
+int main(int argc, char **argv)
+{
+    if (argc < 2) {
+        std::cout << "Usage: " << argv[0] << " <plugin module path>" << std::endl;
+        return 0;
+    }
+    std::string path = argv[1];
+    try {
+        cli::SetColor();
+
+        cli::CliEcoreSession *ref = nullptr;
+        auto rootMenu = std::make_unique<cli::Menu>( "tester" );
+
+        rootMenu->Insert("loop", [&ref](std::ostream& out) {
+            if (ref) {
+                ref->Loop();
+            }
+        },
+        "Start ecore main loop");
+
+        rootMenu->Insert("load", [path](std::ostream& out) {
+            g_program.load_module(path);
+        },
+        "Load the node module that needs to be tested");
+
+        rootMenu->Insert("unload", [](std::ostream& out) {
+            g_program.unload_module();
+        },
+        "Unload the currently loaded node module");
+
+        rootMenu->Insert("create", [](std::ostream& out) {
+            g_program.create_node_instance();
+        },
+        "Create a new node instance");
+
+        rootMenu->Insert("destroy", [](std::ostream& out) {
+            g_program.destroy_node_instance();
+        },
+        "Destroy the node instance with the given index");
+
+        rootMenu->Insert("activate", [](std::ostream& out) {
+            g_program.activate_node_instance();
+        },
+        "Activate the node instance with the given index");
+
+        rootMenu->Insert("deactivate", [](std::ostream& out) {
+            g_program.deactivate_node_instance();
+        },
+        "Dectivate the node instance with the given index");
+
+        auto attributeMenu = std::make_unique<cli::Menu>("set_attribute");
+        for (auto &entry : g_attribute_test_entries) {
+            attributeMenu->Insert(entry.name, [entry](std::ostream& out) {
+                g_program.set_attribute(entry.type);
+            },
+            entry.description);
+        }
+
+        rootMenu->Insert(std::move(attributeMenu));
+
+        auto dataMenu = std::make_unique<cli::Menu>("push_data");
+        for (auto &entry : g_data_test_entries) {
+            dataMenu->Insert(entry.name, [entry](std::ostream& out) {
+                g_program.push_data(entry.type);
+            },
+            entry.description);
+        }
+
+        rootMenu->Insert(std::move(dataMenu));
+
+        cli::Cli cli(std::move(rootMenu));
+
+        cli::CliEcoreSession session(cli);
+        ref = &session;
+
+        g_program.set_ecore_session(&session);
+
+        session.Start();
+        session.Finish();
+
+        g_program.set_ecore_session(nullptr);
+    } catch (const std::exception& e) {
+        std::cout << "Exception: " << e.what() << std::endl;
+    }
+
+    return 0;
+}
+
diff --git a/src/mmi-cli/mmi-cli.cpp b/src/mmi-cli/mmi-cli.cpp
new file mode 100644 (file)
index 0000000..d3e6963
--- /dev/null
@@ -0,0 +1,172 @@
+#include <iostream>
+#include <memory>
+
+#include "cliecoresession.h"
+
+#include "cli/cli.h"
+
+#include "mmi.h"
+
+class Program
+{
+public:
+    Program() {
+        mmi_initialize();
+        mmi_set_state_changed_cb(state_changed_cb, this);
+    }
+    ~Program() {
+        mmi_unset_state_changed_cb(state_changed_cb);
+        mmi_deinitialize();
+    }
+
+    void create_instance(int type) {
+        if (m_instance) {
+            std::cout << "Instance already created" << std::endl;
+            return;
+        }
+
+        std::cout << "Creating instance of type " << type << std::endl;
+        mmi_standard_workflow_instance_create(
+            static_cast<mmi_standard_workflow_type_e>(type), &m_instance);
+        mmi_workflow_instance_set_output_callback(m_instance, "COMMAND",
+            [](mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data) {
+                std::cout << "OUTPUT data received : " << std::string(name ? name : "") << std::endl;
+            },
+            nullptr);
+        mmi_workflow_instance_set_output_callback(m_instance, "UTTERANCE",
+            [](mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data) {
+                const char *value = nullptr;
+                mmi_data_get_text(data, &value);
+                std::cout << "OUTPUT data received : " << std::string(name ? name : "") << " : " << value << std::endl;
+            },
+            nullptr);
+    }
+    void destroy_instance() {
+        if (!m_instance) {
+            std::cout << "No instance to destroy" << std::endl;
+            return;
+        }
+
+        std::cout << "Destroying instance" << std::endl;
+        mmi_workflow_instance_destroy(m_instance);
+        m_instance = nullptr;
+    }
+    void activate_instance() {
+        if (!m_instance) {
+            std::cout << "No instance to activate" << std::endl;
+            return;
+        }
+
+        std::cout << "Activating instance" << std::endl;
+        mmi_workflow_instance_activate(m_instance);
+    }
+    void deactivate_instance() {
+        if (!m_instance) {
+            std::cout << "No instance to deactivate" << std::endl;
+            return;
+        }
+
+        std::cout << "Deactivating instance" << std::endl;
+        mmi_workflow_instance_deactivate(m_instance);
+    }
+    void set_attribute() {
+        if (!m_instance) {
+            std::cout << "No instance to set attribute" << std::endl;
+            return;
+        }
+
+        std::cout << "Setting attribute " << std::endl;
+
+        mmi_attribute_h attribute = nullptr;
+        constexpr size_t COMMAND_NUM = 2;
+        const char *commands[] = {"Open", "Close"};
+        mmi_attribute_create_string_array("COMMANDS", commands, COMMAND_NUM, &attribute);
+
+        mmi_workflow_instance_set_attribute(m_instance, attribute);
+
+        mmi_attribute_destroy(attribute);
+
+        attribute = nullptr;
+        mmi_primitive_value_h lang_value = nullptr;
+        mmi_primitive_value_create_string("ko-KR", &lang_value);
+        mmi_attribute_create(lang_value, "LANGUAGE", &attribute);
+        mmi_workflow_instance_set_attribute(m_instance, attribute);
+        mmi_attribute_destroy(attribute);
+        mmi_primitive_value_destroy(lang_value);
+
+        attribute = nullptr;
+        mmi_primitive_value_h stop_method_value = nullptr;
+        mmi_primitive_value_create_int(1, &stop_method_value);  // 1 : silence detection
+        mmi_attribute_create(stop_method_value, "STOP_METHOD", &attribute);
+        mmi_workflow_instance_set_attribute(m_instance, attribute);
+        mmi_attribute_destroy(attribute);
+        mmi_primitive_value_destroy(stop_method_value);
+    }
+
+    static int state_changed_cb(mmi_state_e state, void *user_data) {
+        Program *program = static_cast<Program*>(user_data);
+        program->m_state = state;
+        std::cout << "State changed to " << state << std::endl;
+        return 0;
+    }
+private:
+    mmi_state_e m_state{MMI_STATE_NONE};
+    mmi_workflow_instance_h m_instance{nullptr};
+};
+
+static Program g_program;
+
+int main(void)
+{
+    try {
+        cli::SetColor();
+
+        cli::CliEcoreSession *ref = nullptr;
+        auto rootMenu = std::make_unique<cli::Menu>( "cli" );
+
+        rootMenu->Insert("loop", [&ref](std::ostream& out) {
+            if (ref) {
+                ref->Loop();
+            }
+        },
+        "Start ecore main loop");
+
+        rootMenu->Insert("create", [](std::ostream& out, int type) {
+            g_program.create_instance(type);
+        },
+        "Creates a new workflow instance");
+
+        rootMenu->Insert("destroy", [](std::ostream& out) {
+            g_program.destroy_instance();
+        },
+        "Destroys the current workflow instance");
+
+        rootMenu->Insert("set_attribute", [](std::ostream& out) {
+            g_program.set_attribute();
+        },
+        "Set attributes for the current workflow instance");
+
+        rootMenu->Insert("activate", [](std::ostream& out) {
+            g_program.activate_instance();
+        },
+        "Activates the current workflow instance");
+
+        rootMenu->Insert("deactivate", [](std::ostream& out) {
+            g_program.deactivate_instance();
+        },
+        "Deactivates the current workflow instance");
+
+        cli::Cli cli(std::move(rootMenu));
+
+        cli::CliEcoreSession session(cli);
+        ref = &session;
+
+        session.Start();
+        session.Finish();
+    } catch (const std::exception& e) {
+        std::cout << "Exception: " << e.what() << std::endl;
+    }
+
+    return 0;
+}
+
diff --git a/src/mmi-client.c b/src/mmi-client.c
deleted file mode 100644 (file)
index 2d047a9..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "mmi-ipc.h"
-#include "mmi-dbg.h"
-#include "mmi-client.h"
-
-static mmi_handle mmi_h = NULL;
-
-int mmi_client_create(void)
-{
-       mmi_h = (mmi_handle)calloc(1, sizeof(mmi_struct_s));
-
-       if (mmi_h == NULL) {
-               LOGE("Failed to allocate memory for mmi_h !\n");
-               return MMI_ERROR_OUT_OF_MEMORY;
-       }
-
-       if (mmi_ipc_initialize()) {
-               LOGE("Failed to init mmi ipc !\n");
-               if(mmi_h != NULL) {
-                       free(mmi_h);
-                       mmi_h = NULL;
-               }
-               return MMI_ERROR_NOT_SUPPORTED;
-       }
-
-       mmi_h->rpc_h = mmi_ipc_get_rpc_h();
-       mmi_h->stub_appid = mmi_ipc_get_stub_appid();
-       mmi_h->result_cb_list = NULL;
-
-       return MMI_ERROR_NONE;
-}
-
-int mmi_client_destroy(void)
-{
-       if (mmi_h == NULL) {
-               LOGE("A mmi_h is already NULL");
-               return MMI_ERROR_NONE;
-       }
-
-       mmi_ipc_deinitialize();
-       mmi_h->rpc_h = NULL;
-
-       GList *iter = NULL;
-       mmi_result_cb_s *data = NULL;
-
-       if (g_list_length(mmi_h->result_cb_list) > 0) {
-               iter = g_list_first(mmi_h->result_cb_list);
-               while (iter != NULL) {
-                       data = iter->data;
-                       if (data != NULL) {
-                               data->input_event_type = 0;
-                               data->result_callback = NULL;
-                               free(data);
-                               data = NULL;
-
-                               GList *temp = iter;
-                               iter = g_list_next(iter);
-                               mmi_h->result_cb_list = g_list_remove_link(mmi_h->result_cb_list, temp);
-                       }
-               }
-       }
-
-       free(mmi_h);
-       mmi_h = NULL;
-
-       return MMI_ERROR_NONE;
-}
-
-static const char* __mmi_client_convert_error_code(int err)
-{
-       switch (err) {
-       case MMI_ERROR_NONE:                    return "MMI_ERROR_NONE";
-       case MMI_ERROR_OUT_OF_MEMORY:           return "MMI_ERROR_OUT_OF_MEMORY";
-       case MMI_ERROR_IO_ERROR:                return "MMI_ERROR_IO_ERROR";
-       case MMI_ERROR_INVALID_PARAMETER:       return "MMI_ERROR_INVALID_PARAMETER";
-       case MMI_ERROR_OUT_OF_NETWORK:          return "MMI_ERROR_OUT_OF_NETWORK";
-       case MMI_ERROR_TIMED_OUT:               return "MMI_ERROR_TIMED_OUT";
-       case MMI_ERROR_PERMISSION_DENIED:       return "MMI_ERROR_PERMISSION_DENIED";
-       case MMI_ERROR_NOT_SUPPORTED:           return "MMI_ERROR_NOT_SUPPORTED";
-       case MMI_ERROR_OPERATION_FAILED:                return "MMI_ERROR_OPERATION_FAILED";
-       default:
-                       return "Invalid error code";
-       }
-       return NULL;
-}
-
-int mmi_client_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data)
-{
-       LOGI("Set result cb about input event type(%d) to client", input_event_type);
-
-       if (callback == NULL) {
-               LOGE("Callback is invalid");
-               return MMI_ERROR_INVALID_PARAMETER;
-       }
-
-       if (mmi_h == NULL) {
-               LOGE("Fail to get client");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       mmi_result_cb_s* input_result_callback = (mmi_result_cb_s*)calloc(1, sizeof(mmi_result_cb_s));
-       if (input_result_callback == NULL) {
-               LOGE("[ERROR] Fail to allocate memory");
-               return MMI_ERROR_OUT_OF_MEMORY;
-       }
-
-       input_result_callback->input_event_type = input_event_type;
-       input_result_callback->result_callback = callback;
-       mmi_h->result_cb_list  = g_list_append(mmi_h->result_cb_list, input_result_callback);
-
-       int ret = mmi_ipc_register_input_event_result_cb(input_event_type, callback);
-       if (ret != MMI_ERROR_NONE) {
-               LOGE("Fail to register input event's result callback(%d), reason(%s)", ret, __mmi_client_convert_error_code(ret));
-               callback(input_event_type, __mmi_client_convert_error_code(ret), NULL);
-               return ret;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-mmi_handle mmi_client_get(void)
-{
-       return mmi_h;
-}
diff --git a/src/mmi-ipc.c b/src/mmi-ipc.c
deleted file mode 100644 (file)
index 8f66c5e..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "mmi-ipc.h"
-#include "mmi-dbg.h"
-#include "mmi-client.h"
-
-#include <tzplatform_config.h>
-#include <unistd.h>
-#include <rpc-port.h>
-#include <rpc-port-internal.h>
-
-static rpc_port_proxy_mmi_h _rpc_h;
-static const char* _stub_appid = "mmi-manager";
-static int _connected = 0;
-
-rpc_port_proxy_mmi_h mmi_ipc_get_rpc_h(void)
-{
-       return _rpc_h;
-}
-
-bool mmi_ipc_is_connected(void)
-{
-       return _connected ? true : false;
-}
-
-const char * mmi_ipc_get_stub_appid(void)
-{
-       return _stub_appid;
-}
-
-static void _on_connected(rpc_port_proxy_mmi_h h, void *user_data)
-{
-       int r;
-       _connected = 1;
-
-       LOGI("...");
-       return;
-}
-
-static void _on_disconnected(rpc_port_proxy_mmi_h h, void *user_data)
-{
-       _connected = 0;
-
-       LOGI("...");
-}
-static void _on_rejected(rpc_port_proxy_mmi_h h, void *user_data)
-{
-       LOGI("...");
-       _connected = 0;
-}
-
-int mmi_ipc_retry_connection(rpc_port_proxy_mmi_h h)
-{
-       LOGI("Retry connection");
-
-       int ret = rpc_port_proxy_mmi_connect_sync(h);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Fail to retry connection(%d)", ret);
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-int mmi_ipc_initialize(void)
-{
-       /* initialize handles */
-       _rpc_h = NULL;
-       int ret;
-
-       rpc_port_proxy_mmi_callback_s callback = {
-               .connected = _on_connected,
-               .disconnected = _on_disconnected,
-               .rejected = _on_rejected
-       };
-
-       ret = rpc_port_proxy_mmi_create(_stub_appid, &callback, NULL, &_rpc_h);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Failed to create mmi proxy handle ! (error:%d)\n", ret);
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       ret = rpc_port_proxy_mmi_connect_sync(_rpc_h);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Try to connect again");
-               //Retry
-               ret = mmi_ipc_retry_connection(_rpc_h);
-               if (ret != RPC_PORT_ERROR_NONE) {
-                       LOGE("Fail to retry connection(%d)", ret);
-                       return MMI_ERROR_OPERATION_FAILED;
-               }
-       }
-
-       LOGI("connect mmi ipc");
-
-       return MMI_ERROR_NONE;
-}
-
-void mmi_ipc_deinitialize(void)
-{
-       if (!_rpc_h)
-               return;
-
-       LOGI("disconnect mmi ipc");
-
-       rpc_port_proxy_mmi_destroy(_rpc_h);
-       rpc_port_deregister_proc_info();
-       _rpc_h = NULL;
-
-       _connected = 0;
-}
-
-int mmi_ipc_register_input_event_result_cb(int input_event_type, rpc_port_proxy_mmi_result_cb_cb callback)
-{
-       LOGI("Register input event(%d) result cb to MMI Service", input_event_type);
-       int ret;
-
-       if (callback == NULL) {
-               LOGE("Parameter callback is NULL");
-               return MMI_ERROR_INVALID_PARAMETER;
-       }
-
-       if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) {
-               LOGE("Parameter input event type is NULL");
-               return MMI_ERROR_INVALID_PARAMETER;
-       }
-
-       mmi_handle client = mmi_client_get();
-       if (client == NULL) {
-               LOGE("Fail to get client");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       rpc_port_proxy_mmi_h rpc_h = client->rpc_h;
-       if (rpc_h == NULL) {
-               LOGE("Fail to get tidl rpc info");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       if (mmi_ipc_is_connected() == false) {
-               LOGE("Try to connect again");
-               ret = mmi_ipc_retry_connection(rpc_h);
-               if (ret != RPC_PORT_ERROR_NONE) {
-                       LOGE("Fail to retry connection(%d)", ret);
-                       return MMI_ERROR_OPERATION_FAILED;
-               }
-       }
-
-       rpc_port_proxy_mmi_result_cb_h result_cb_h;
-       ret = rpc_port_proxy_mmi_result_cb_create(&result_cb_h);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Failed to create result callback handle (error:%d)\n", ret);
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-       rpc_port_proxy_mmi_result_cb_set_callback(result_cb_h, callback, NULL);
-       rpc_port_proxy_mmi_result_cb_set_once(result_cb_h, false);
-       if (result_cb_h == NULL) {
-               LOGE("Failed to create event callbacks");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       ret = rpc_port_proxy_mmi_invoke_register_input_event(rpc_h, input_event_type, result_cb_h);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Failed to register event callbacks(%d)\n", ret);
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-int mmi_ipc_activate_input_event(int input_event_type)
-{
-       LOGI("Activate about input event type(%d)", input_event_type);
-
-       int ret;
-
-       if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) {
-               LOGE("Parameter input event type is NULL");
-               return MMI_ERROR_INVALID_PARAMETER;
-       }
-
-       mmi_handle client = mmi_client_get();
-       if (client == NULL) {
-               LOGE("Fail to get client");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       rpc_port_proxy_mmi_h rpc_h = client->rpc_h;
-       if (rpc_h == NULL) {
-               LOGE("Fail to get tidl rpc info");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       if (mmi_ipc_is_connected() == false) {
-               LOGE("Try to connect again");
-               ret = mmi_ipc_retry_connection(_rpc_h);
-               if (ret != RPC_PORT_ERROR_NONE) {
-                       LOGE("Fail to retry connection(%d)", ret);
-                       return MMI_ERROR_OPERATION_FAILED;
-               }
-       }
-
-       ret = rpc_port_proxy_mmi_invoke_activate_input_event(rpc_h, input_event_type);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Fail to invoke activate input event");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-int mmi_ipc_deactivate_input_event(int input_event_type)
-{
-       LOGE("Dectivate about input event type(%d)", input_event_type);
-
-       int ret;
-       rpc_port_proxy_mmi_h rpc_h = NULL;
-
-       if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) {
-               LOGE("Parameter input event type is NULL");
-               return MMI_ERROR_INVALID_PARAMETER;
-       }
-
-       rpc_h = mmi_ipc_get_rpc_h();
-       if (rpc_h == NULL) {
-               LOGE("Fail to get tidl rpc info");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       ret = rpc_port_proxy_mmi_invoke_deactivate_input_event(rpc_h, input_event_type);
-       if (ret != RPC_PORT_ERROR_NONE) {
-               LOGE("Fail to invoke activate input event");
-               return MMI_ERROR_OPERATION_FAILED;
-       }
-
-       return MMI_ERROR_NONE;
-}
diff --git a/src/mmi-manager/main.cpp b/src/mmi-manager/main.cpp
new file mode 100644 (file)
index 0000000..97c95c5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include <Ecore.h>
+
+#include "mmi-manager.h"
+#include "mmi-ipc-tidl.h"
+#include "mmi-self-container.h"
+
+#include "adishavit/argh.h"
+
+struct CommunicationChannelFactoryTIDL :
+    public mmi::communication::CommunicationChannelManagerFactory {
+    std::shared_ptr<mmi::communication::CommunicationChannelManager> create_channel() override {
+        return std::make_shared<mmi::communication::CommunicationChannelTIDL>();
+    }
+};
+
+static Ecore_Event_Handler *exit_signal_event_handler = nullptr;
+
+static Eina_Bool
+exit_signal_cb(void *data EINA_UNUSED, int type, void *event) {
+    ecore_main_loop_quit();
+
+    return ECORE_CALLBACK_PASS_ON;
+}
+
+int main(int argc, char *argv[]) {
+    ecore_init();
+
+    std::shared_ptr<mmi::communication::CommunicationChannelManagerFactory> communication_channel_factory;
+    std::shared_ptr<mmi::IPluginModuleProxyFactory> plugin_module_proxy_factory;
+
+    argh::parser cmdl(argv);
+
+    if (cmdl["--self-container-test"]) {
+        plugin_module_proxy_factory = std::make_shared<mmi::PluginModuleProxyFactorySelfContainerTest>();
+    } else {
+        plugin_module_proxy_factory = std::make_shared<mmi::PluginModuleProxyFactoryDefault>();
+    }
+
+    communication_channel_factory = std::make_shared<CommunicationChannelFactoryTIDL>();
+
+    mmi::Manager manager(communication_channel_factory, plugin_module_proxy_factory);
+
+    manager.initialize();
+
+    exit_signal_event_handler = ecore_event_handler_add(
+                                    ECORE_EVENT_SIGNAL_EXIT, exit_signal_cb, nullptr);
+
+    ecore_main_loop_begin();
+
+    ecore_main_loop_thread_safe_call_wait(1.0f);
+
+    ecore_event_handler_del(exit_signal_event_handler);
+    exit_signal_event_handler = nullptr;
+
+    manager.deinitialize();
+
+    ecore_shutdown();
+    return 0;
+}
+
diff --git a/src/mmi-manager/meson.build b/src/mmi-manager/meson.build
new file mode 100644 (file)
index 0000000..ecec178
--- /dev/null
@@ -0,0 +1,78 @@
+mmi_manager_library_srcs = [
+       'mmi-manager.cpp',
+       'mmi-ipc-tidl.cpp',
+       'mmi-client.cpp',
+       'mmi-common.cpp',
+       'mmi-data-gateway.cpp',
+       'mmi-port-instance.cpp',
+       'mmi-port-instance-manager.cpp',
+       'mmi-node-prototype.cpp',
+       'mmi-node-prototype-manager.cpp',
+       'mmi-node-instance.cpp',
+       'mmi-node-instance-manager.cpp',
+       'mmi-workflow-prototype.cpp',
+       'mmi-workflow-prototype-manager.cpp',
+       'mmi-workflow-instance.cpp',
+       'mmi-workflow-instance-manager.cpp',
+       'mmi-plugin-module-proxy.cpp',
+       'mmi-plugin-module-registry.cpp',
+       'mmi-self-container.cpp',
+       'mmi_stub.c',
+       ]
+
+bundle_dep = dependency('bundle', method : 'pkg-config')
+dlog_dep = dependency('dlog', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+glib_dep = dependency('glib-2.0', method : 'pkg-config')
+rpc_port_dep = dependency('rpc-port')
+libtzplatform_config_dep = dependency('libtzplatform-config')
+
+mmi_manager_deps = [
+       mmi_declared_dep,
+       bundle_dep,
+       dlog_dep,
+       ecore_dep,
+       glib_dep,
+       rpc_port_dep,
+       libtzplatform_config_dep,
+       ]
+
+mmi_manager_include_dirs = include_directories(
+       '.',
+       '../common/',
+       '../../capi/',
+       '../../external/',
+       )
+
+mmi_manager_library = library('mmi_manager',
+       mmi_manager_library_srcs,
+       include_directories : [ mmi_manager_include_dirs ],
+       dependencies : [mmi_manager_deps],
+       install_dir : mmi_prefix_libdir,
+       install : true
+       )
+
+mmi_manager_declared_dep = declare_dependency(
+       link_with : mmi_manager_library,
+       dependencies : [mmi_manager_deps],
+       include_directories : [mmi_manager_include_dirs]
+       )
+
+mmi_manager_program_srcs = [
+       'main.cpp'
+       ]
+
+mmi_manager_program_deps = [
+       mmi_manager_declared_dep
+       ]
+
+executable('mmi-manager',
+       mmi_manager_program_srcs,
+       include_directories : [ mmi_manager_include_dirs ],
+       dependencies : [mmi_manager_program_deps],
+       install_dir : mmi_prefix_bindir,
+       install : true,
+       pie : true
+       )
+
+install_data('mmi-manager.xml', install_dir:'/usr/share/packages/')
diff --git a/src/mmi-manager/mmi-client.cpp b/src/mmi-manager/mmi-client.cpp
new file mode 100644 (file)
index 0000000..3135075
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-client.h"
+#include "mmi-common.h"
+#include "mmi-manager-log.h"
+#include "mmi_stub.h"
+
+namespace mmi {
+
+ClientManager::ClientManager() {
+}
+
+ClientManager::~ClientManager() {
+}
+
+void ClientManager::on_observer_event(
+    const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) {
+    try {
+        if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) {
+            std::string sender = std::any_cast<std::string>(data);
+            add_client(sender);
+        } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) {
+            std::string sender = std::any_cast<std::string>(data);
+            remove_client(sender);
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+int ClientManager::initialize() {
+    return MMI_ERROR_NONE;
+}
+
+int ClientManager::deinitialize() {
+    for (auto it = m_clients.begin(); it != m_clients.end(); it++) {
+        delete it->second;
+    }
+    m_clients.clear();
+
+    return MMI_ERROR_NONE;
+}
+
+Client* ClientManager::add_client(std::string sender) {
+    LOGI("Add client(%s)\n", sender.c_str());
+
+    Client *client = new Client(sender);
+    if (!client) {
+        LOGE("Failed to allocate memory for client !\n");
+        return nullptr;
+    }
+
+    m_clients.insert(std::pair<std::string, Client*>(sender, client));
+
+    return client;
+}
+
+Client* ClientManager::get_client(std::string sender) {
+    auto it = m_clients.find(sender);
+
+    if (it == m_clients.end()) {
+        LOGE("Failed to find client info from sender(%s)\n", sender.c_str());
+        return nullptr;
+    }
+
+    return it->second;
+}
+
+int ClientManager::remove_client(std::string sender) {
+    LOGI("Remove client(%s)\n", sender.c_str());
+
+    auto it = m_clients.find(sender);
+
+    if (it == m_clients.end()) {
+        LOGE("Failed to find client info from sender(%s)\n", sender.c_str());
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    delete it->second;
+    m_clients.erase(it);
+
+    return MMI_ERROR_NONE;
+}
+
+
+int ClientManager::set_client_uid(Client *client, uid_t uid) {
+    if (!client) {
+        LOGE("client is NULL\n");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    client->set_uid(uid);
+
+    return MMI_ERROR_NONE;
+}
+
+uid_t ClientManager::get_client_uid(Client *client) {
+    uid_t uid = 0;
+
+    if (!client) {
+        LOGE("client is NULL\n");
+        return uid;
+    }
+
+    uid = client->get_uid();
+
+    return uid;
+}
+
+int ClientManager::set_client_pid(Client *client, pid_t pid) {
+    if (!client) {
+        LOGE("client is NULL\n");
+        return MMI_ERROR_INVALID_PARAMETER;;
+    }
+
+    client->set_pid(pid);
+
+    return MMI_ERROR_NONE;
+}
+
+pid_t ClientManager::get_client_pid(Client *client) {
+    pid_t pid = 0;
+
+    if (!client) {
+        LOGE("client is NULL\n");
+        return pid;
+    }
+
+    pid = client->get_pid();
+
+    return pid;
+}
+
+std::string ClientManager::get_client_sender(Client *client) {
+    std::string sender;
+
+    if (!client) {
+        LOGE("client is NULL\n");
+        return sender;
+    }
+
+    sender = client->get_sender();
+
+    return sender;
+}
+
+} // namespace mmi
diff --git a/src/mmi-manager/mmi-client.h b/src/mmi-manager/mmi-client.h
new file mode 100644 (file)
index 0000000..50e1c1e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_CLIENT_H__
+#define __MMI_CLIENT_H__
+
+#include <stdlib.h>
+
+#include <string>
+#include <map>
+
+#include <mmi.h>
+
+#include "mmi-common.h"
+#include "mmi-communication-channel.h"
+#include "mmi_stub.h"
+
+namespace mmi {
+
+using namespace communication;
+
+class Client {
+public:
+    Client(std::string sender) : m_sender(sender) {
+    }
+    virtual ~Client() {
+    }
+
+    std::string get_sender() {
+        return m_sender;
+    }
+
+    uid_t get_uid() {
+        return m_uid;
+    }
+    pid_t get_pid() {
+        return m_pid;
+    }
+
+    void set_uid(uid_t uid) {
+        m_uid = uid;
+    }
+    void set_pid(pid_t pid) {
+        m_pid = pid;
+    }
+
+private:
+    std::string m_sender;
+    uid_t m_uid{0};
+    pid_t m_pid{0};
+};
+
+class ClientManager :
+    public SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE> {
+public:
+    ClientManager();
+    virtual ~ClientManager();
+
+    void on_observer_event(
+        const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override;
+
+    int initialize();
+    int deinitialize();
+
+    Client* add_client(std::string sender);
+    Client* get_client(std::string sender);
+    int remove_client(std::string sender);
+
+    int set_client_uid(Client *client, uid_t uid);
+    uid_t get_client_uid(Client *client);
+
+    int set_client_pid(Client *client, pid_t pid);
+    pid_t get_client_pid(Client *client);
+
+    std::string get_client_sender(Client *client);
+
+private:
+    std::map<std::string, Client*> m_clients;
+};
+
+} // namespace mmi
+
+#endif //__MMI_CLIENT_H__
diff --git a/src/mmi-manager/mmi-common.cpp b/src/mmi-manager/mmi-common.cpp
new file mode 100644 (file)
index 0000000..139d4b9
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-common.h"
+
diff --git a/src/mmi-manager/mmi-common.h b/src/mmi-manager/mmi-common.h
new file mode 100644 (file)
index 0000000..3fc6ad1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_COMMON_H__
+#define __MMI_COMMON_H__
+
+#include <stdbool.h>
+#include <tizen.h>
+
+#ifndef MMI_API
+#define MMI_API __attribute__ ((visibility("default")))
+#endif
+
+#endif //__MMI_COMMON_H__
diff --git a/src/mmi-manager/mmi-data-gateway.cpp b/src/mmi-manager/mmi-data-gateway.cpp
new file mode 100644 (file)
index 0000000..dc3eccf
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-data-gateway.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+DataGateway::DataGateway(std::string name)
+    : m_name(name) {
+    _D("Data Gateway is created: %s", m_name.c_str());
+}
+
+DataGateway::~DataGateway() {
+    _D("Data Gateway is destroyed: %s", m_name.c_str());
+}
+
+bool DataGateway::initialize() {
+    _D("Data Gateway is initialized: %s", m_name.c_str());
+    return true;
+}
+
+bool DataGateway::deinitialize() {
+    _D("Data Gateway is deinitialized: %s", m_name.c_str());
+    return true;
+}
+
+bool DataGateway::set_name(std::string name) {
+    m_name = name;
+    return true;
+}
+
+bool DataGateway::set_callback(mmi_data_transfer_callback callback) {
+    _D("[DataGateway] set data transfer callback");
+    m_data_transfer_callback = callback;
+    return true;
+}
+
+void DataGateway::send_data(mmi_data_h data) {
+    _D("[DataGateway] Send Data: %s", m_name.c_str());
+
+    if (m_data_transfer_callback) {
+        m_data_transfer_callback(data, m_name);
+    }
+    else {
+        _E("Data Gateway is not set callback: %s", m_name.c_str());
+    }
+}
+
+} // namespace mmi
\ No newline at end of file
diff --git a/src/mmi-manager/mmi-data-gateway.h b/src/mmi-manager/mmi-data-gateway.h
new file mode 100644 (file)
index 0000000..ffc0f5d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_DATA_GATEWAY_H__
+#define __MMI_DATA_GATEWAY_H__
+
+#include "mmi-data.h"
+
+#include <string>
+#include <functional>
+
+namespace mmi {
+
+using mmi_data_transfer_callback = std::function<void(mmi_data_h, std::string)>;
+
+class DataGateway {
+public:
+    DataGateway(std::string name);
+    virtual ~DataGateway();
+
+    bool initialize();
+    bool deinitialize();
+
+    bool set_name(std::string name);
+    bool set_callback(mmi_data_transfer_callback callback);
+
+    void send_data(mmi_data_h data);
+
+private:
+    std::string m_name;
+    mmi_data_transfer_callback m_data_transfer_callback;
+};
+
+} // namesapce mmi
+
+#endif // __MMI_DATA_GATEWAY_H__
\ No newline at end of file
diff --git a/src/mmi-manager/mmi-ipc-tidl.cpp b/src/mmi-manager/mmi-ipc-tidl.cpp
new file mode 100644 (file)
index 0000000..a3f2325
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-ipc-tidl.h"
+
+#include "mmi-manager-log.h"
+#include "mmi-client.h"
+#include "mmi-common.h"
+
+#include <tuple>
+
+#include <bundle.h>
+#include <rpc-port.h>
+#include <rpc-port-internal.h>
+
+namespace mmi {
+
+namespace communication {
+
+CommunicationChannelTIDL::CommunicationChannelTIDL() {
+}
+
+CommunicationChannelTIDL::~CommunicationChannelTIDL() {
+}
+
+int CommunicationChannelTIDL::connect() {
+    int r;
+    LOGD("mmi_api_handler_initialize");
+
+    if (m_initialized) {
+        LOGE("Already initialized");
+        return MMI_ERROR_NONE;
+    }
+
+    r = rpc_port_register_proc_info(m_stub_appid.c_str(), nullptr);
+    if (r != RPC_PORT_ERROR_NONE) {
+        LOGE("Failed to register proc info ! (error:%d)", r);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    rpc_port_stub_mmi_callback_s callback = {
+        on_create,
+        on_terminate,
+        workflow_instance_create,
+        workflow_instance_destroy,
+        workflow_instance_set_attribute,
+        workflow_instance_activate,
+        workflow_instance_deactivate,
+        workflow_instance_register_result_callback,
+    };
+
+    r = rpc_port_stub_mmi_register(&callback, this);
+    if (r != RPC_PORT_ERROR_NONE) {
+        LOGE("Failed to register callbacks (error:%d)", r);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_initialized = true;
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::disconnect() {
+    if (!m_initialized)
+        return MMI_ERROR_OPERATION_FAILED;
+
+    clear_invoke_callbacks();
+
+    rpc_port_stub_mmi_unregister();
+    rpc_port_deregister_proc_info();
+
+    m_initialized = false;
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::send(communication::Message *message) {
+    return 0;
+}
+
+static bool get_sender_string(rpc_port_stub_mmi_context_h context,
+                              CommunicationChannelTIDL *channel, std::string &sender) {
+    char *buf = nullptr;
+
+    //int res = rpc_port_stub_mmi_context_get_sender(context, &buf);
+    int res = rpc_port_stub_mmi_context_get_instance(context, &buf);
+    if (res != MMI_ERROR_NONE || !buf) {
+        LOGE("Failed to get sender from context ! (error:%d)", res);
+        free(buf);
+        return false;
+    }
+
+    sender = std::string{buf};
+    free(buf);
+    buf = nullptr;
+
+    return true;
+}
+
+static int notify_connection_changes(CommunicationChannelTIDL *channel, bool connected, std::string sender) {
+    if (nullptr == channel) {
+        LOGE("Failed to get channel information !");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    channel->notify_observers(
+        connected ?
+        COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED :
+        COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED,
+        sender);
+
+    return MMI_ERROR_NONE;
+}
+
+static int notify_message_received(CommunicationChannelTIDL *channel, Message *message) {
+    if (nullptr == channel) {
+        LOGE("Failed to get channel information !");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    channel->notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, message);
+
+    return MMI_ERROR_NONE;
+}
+
+void CommunicationChannelTIDL::on_create(rpc_port_stub_mmi_context_h context, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return;
+
+    LOGI("Client(%s) has been connected !", sender.c_str());
+
+    //TODO: set additional information of client
+    notify_connection_changes(channel, true, sender);
+}
+
+void CommunicationChannelTIDL::on_terminate(rpc_port_stub_mmi_context_h context, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return;
+
+    LOGI("Client(%s) has been disconnected !", sender.c_str());
+
+    channel->clear_invoke_callbacks();
+
+    notify_connection_changes(channel, false, sender);
+}
+
+int CommunicationChannelTIDL::workflow_instance_create(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, int workflow_type, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = sender;
+    msg.local_workflow_instance_id = local_workflow_instance_id;
+    msg.workflow_type = static_cast<mmi_standard_workflow_type_e>(workflow_type);
+
+    return notify_message_received(channel, static_cast<Message*>(&msg));
+}
+
+int CommunicationChannelTIDL::workflow_instance_destroy(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    ClientMessageWorkflowInstanceDestroy msg;
+    msg.sender = sender;
+    msg.local_workflow_instance_id = local_workflow_instance_id;
+
+    channel->remove_invoke_callbacks(sender, local_workflow_instance_id);
+
+    return notify_message_received(channel, static_cast<Message*>(&msg));
+}
+
+int CommunicationChannelTIDL::workflow_instance_set_attribute(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, bundle *attribute, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    mmi_attribute_h restored_attribute;
+
+    unsigned char *bytes = nullptr;
+    size_t returned_size = 0;
+
+    if (BUNDLE_ERROR_NONE != bundle_get_byte(attribute, "data", (void**)&bytes, &returned_size)) {
+        _E("Bundle does not contain 'data' field");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+    if (nullptr == bytes || 0 == returned_size) {
+        _E("Bundle does not contain 'data' field");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (MMI_ERROR_NONE != mmi_attribute_from_bytes(bytes, returned_size, &restored_attribute)) {
+        _E("Failed to restore attribute from bytes");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    ClientMessageWorkflowInstanceSetAttribute msg;
+    msg.sender = sender;
+    msg.local_workflow_instance_id = local_workflow_instance_id;
+    msg.attribute = restored_attribute;
+
+    notify_message_received(channel, static_cast<Message*>(&msg));
+
+    mmi_attribute_destroy(restored_attribute);
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_activate(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    ClientMessageWorkflowInstanceActivate msg;
+    msg.sender = sender;
+    msg.local_workflow_instance_id = local_workflow_instance_id;
+
+    return notify_message_received(channel, static_cast<Message*>(&msg));
+}
+
+int CommunicationChannelTIDL::workflow_instance_deactivate(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    ClientMessageWorkflowInstanceDeactivate msg;
+    msg.sender = sender;
+    msg.local_workflow_instance_id = local_workflow_instance_id;
+
+    return notify_message_received(channel, static_cast<Message*>(&msg));
+}
+
+int CommunicationChannelTIDL::workflow_instance_register_result_callback(rpc_port_stub_mmi_context_h context,
+        int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback, void *user_data) {
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+
+    std::string sender;
+    if (!get_sender_string(context, channel, sender))
+        return MMI_ERROR_INVALID_PARAMETER;
+
+    rpc_port_stub_mmi_result_cb_h result_cb_h = nullptr;
+    int ret = rpc_port_stub_mmi_result_cb_clone(callback, &result_cb_h);
+
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Fail to clone invoke callback(%d)", ret);
+        rpc_port_stub_mmi_result_cb_destroy(result_cb_h);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+    channel->set_invoke_callbacks(sender, local_workflow_instance_id, result_cb_h);
+
+    return MMI_ERROR_NONE;
+}
+
+void CommunicationChannelTIDL::on_observer_event(
+        const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) {
+    try {
+        WorkflowOutputEventOutputGenerated *output_generated = std::any_cast<WorkflowOutputEventOutputGenerated*>(data);
+    
+        rpc_port_stub_mmi_result_cb_h callback = get_invoke_callbacks(output_generated->client_id, output_generated->local_workflow_instance_id);
+
+        std::string source_name = output_generated->source_name;
+        bundle *data_b = reinterpret_cast<bundle*>(output_generated->data);
+
+        int ret = rpc_port_stub_mmi_result_cb_invoke(callback, source_name.c_str(), data_b);
+        if (ret != RPC_PORT_ERROR_NONE) {
+            LOGE("Failed to send result to client !");
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+void CommunicationChannelTIDL::set_invoke_callbacks(std::string client_id, int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback) {
+    std::string key = client_id + "_" + std::to_string(local_workflow_instance_id);
+    m_invoke_callbacks[key] = callback;
+}
+
+void CommunicationChannelTIDL::remove_invoke_callbacks(std::string client_id, int local_workflow_instance_id) {
+    std::string key = client_id + "_" + std::to_string(local_workflow_instance_id);
+    rpc_port_stub_mmi_result_cb_destroy(m_invoke_callbacks[key]);
+    m_invoke_callbacks.erase(key);
+}
+
+rpc_port_stub_mmi_result_cb_h CommunicationChannelTIDL::get_invoke_callbacks(std::string client_id, int local_workflow_instance_id) {
+    std::string key = client_id + "_" + std::to_string(local_workflow_instance_id);
+    return m_invoke_callbacks[key];
+}
+
+void CommunicationChannelTIDL::clear_invoke_callbacks() {
+    for (auto &it : m_invoke_callbacks) {
+        rpc_port_stub_mmi_result_cb_destroy(it.second);
+    }
+    m_invoke_callbacks.clear();
+}
+
+} // namespace communication
+//
+} // namespace mmi
diff --git a/src/mmi-manager/mmi-ipc-tidl.h b/src/mmi-manager/mmi-ipc-tidl.h
new file mode 100644 (file)
index 0000000..9117ece
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_IPC_TIDL_H__
+#define __MMI_IPC_TIDL_H__
+
+#include "mmi_stub.h"
+#include "mmi-communication-channel.h"
+#include "mmi-workflow-output-event.h"
+
+#include <map>
+namespace mmi {
+
+namespace communication {
+
+class CommunicationChannelTIDL : public CommunicationChannelManager {
+public:
+    CommunicationChannelTIDL();
+    ~CommunicationChannelTIDL();
+
+    int connect() override;
+    int disconnect() override;
+    int send(communication::Message *message) override;
+
+    void on_observer_event(
+        const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) override;
+
+private:
+    static void on_create(rpc_port_stub_mmi_context_h context, void *user_data);
+    static void on_terminate(rpc_port_stub_mmi_context_h context, void *user_data);
+
+    static int workflow_instance_create(rpc_port_stub_mmi_context_h context,
+                                        int local_workflow_instance_id, int workflow_type, void *user_data);
+    static int workflow_instance_destroy(rpc_port_stub_mmi_context_h context,
+                                         int local_workflow_instance_id, void *user_data);
+    static int workflow_instance_set_attribute(rpc_port_stub_mmi_context_h context,
+            int workflow_instance, bundle *attribute, void *user_data);
+    static int workflow_instance_activate(rpc_port_stub_mmi_context_h context,
+                                          int local_workflow_instance_id, void *user_data);
+    static int workflow_instance_deactivate(rpc_port_stub_mmi_context_h context,
+                                            int local_workflow_instance_id, void *user_data);
+    static int workflow_instance_register_result_callback(rpc_port_stub_mmi_context_h context,
+            int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback, void *user_data);
+
+    void set_invoke_callbacks(std::string appid, int local_workflow_instance_id,
+                              rpc_port_stub_mmi_result_cb_h callback);
+    rpc_port_stub_mmi_result_cb_h get_invoke_callbacks(std::string appid, int local_workflow_instance_id);
+    void remove_invoke_callbacks(std::string appid, int local_workflow_instance_id);
+    void clear_invoke_callbacks();
+
+private:
+    bool m_initialized{false};
+    const std::string m_stub_appid{"mmi-manager"};
+    std::map<std::string, rpc_port_stub_mmi_result_cb_h> m_invoke_callbacks;
+};
+
+} // namespace communication
+
+} // namespace mmi
+
+#endif //__MMI_IPC_TIDL_H__
diff --git a/src/mmi-manager/mmi-manager-log.h b/src/mmi-manager/mmi-manager-log.h
new file mode 100644 (file)
index 0000000..51afe9c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_MANAGER_LOG_H__
+#define __MMI_MANAGER_LOG_H__
+
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "MMIMGR"
+
+#ifndef _E
+#define _E LOGE
+#endif
+
+#ifndef _D
+#define _D LOGD
+#endif
+
+#ifndef _I
+#define _I LOGI
+#endif
+
+#ifndef _W
+#define _W LOGW
+#endif
+
+#endif // __MMI_MANAGER_LOG_H__ 
diff --git a/src/mmi-manager/mmi-manager.cpp b/src/mmi-manager/mmi-manager.cpp
new file mode 100644 (file)
index 0000000..f23c043
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-manager.h"
+
+namespace mmi {
+
+Manager::Manager(
+    std::shared_ptr<CommunicationChannelManagerFactory> communication_channel_factory,
+    std::shared_ptr<IPluginModuleProxyFactory> plugin_module_proxy_factory) :
+    m_communication_channel_factory(communication_channel_factory),
+    m_plugin_module_proxy_factory(plugin_module_proxy_factory) {
+}
+
+Manager::~Manager() {
+}
+
+int Manager::initialize() {
+    if (nullptr == m_communication_channel_factory) {
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_plugin_module_registry = std::make_shared<PluginModuleRegistry>();
+    if (nullptr == m_plugin_module_registry) {
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_client_manager = std::make_shared<ClientManager>();
+
+    m_port_instance_manager = std::make_shared<PortInstanceManager>();
+
+    m_node_prototype_manager = std::make_shared<NodePrototypeManager>();
+    m_node_instance_manager = std::make_shared<NodeInstanceManager>();
+    if (nullptr == m_node_instance_manager) {
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_node_instance_manager->set_plugin_module_proxy_provider(m_plugin_module_registry);
+    m_node_instance_manager->set_node_prototype_store(m_node_prototype_manager);
+    m_node_instance_manager->set_port_instance_manager(m_port_instance_manager);
+
+    m_workflow_prototype_manager = std::make_shared<WorkflowPrototypeManager>();
+    m_workflow_instance_manager = std::make_shared<WorkflowInstanceManager>();
+    if (nullptr == m_workflow_instance_manager) {
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_workflow_instance_manager->set_workflow_prototype_store(m_workflow_prototype_manager);
+    m_workflow_instance_manager->set_plugin_module_proxy_provider(m_plugin_module_registry);
+    m_workflow_instance_manager->set_node_instance_manager(m_node_instance_manager);
+    m_workflow_instance_manager->add_observer(this);
+
+    m_communication_channel = m_communication_channel_factory->create_channel();
+    if (m_communication_channel) {
+        m_communication_channel->add_observer(m_client_manager.get());
+        m_communication_channel->add_observer(m_workflow_instance_manager.get());
+
+        m_communication_channel->connect();
+    } else {
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    m_plugin_module_proxy_factory->add_observer(m_node_instance_manager.get());
+
+    m_plugin_module_registry->set_plugin_module_proxy_factory(m_plugin_module_proxy_factory);
+    m_plugin_module_registry->set_node_prototype_store(m_node_prototype_manager);
+    m_plugin_module_registry->set_workflow_prototype_store(m_workflow_prototype_manager);
+
+    m_plugin_module_registry->initialize();
+
+    return MMI_ERROR_NONE;
+}
+
+int Manager::deinitialize() {
+    if (m_communication_channel) {
+        m_communication_channel->disconnect();
+
+        m_communication_channel->remove_observer(m_client_manager.get());
+        m_communication_channel->remove_observer(m_workflow_instance_manager.get());
+    }
+
+    m_communication_channel.reset();
+    m_communication_channel_factory.reset();
+
+    m_plugin_module_proxy_factory.reset();
+
+    m_workflow_instance_manager.reset();
+    m_workflow_prototype_manager.reset();
+
+    m_node_instance_manager.reset();
+
+    m_port_instance_manager.reset();
+
+    m_client_manager.reset();
+
+    if (m_plugin_module_registry) {
+        m_plugin_module_registry->deinitialize();
+    }
+    m_plugin_module_registry.reset();
+
+    return MMI_ERROR_NONE;
+}
+
+void Manager::on_observer_event(
+    const WORKFLOW_EVENT_TYPE &event, std::any data) {
+    try {
+        WorkflowEventWorkflowCreated *workflow_created = std::any_cast<WorkflowEventWorkflowCreated*>(data);
+        std::shared_ptr<WorkflowInstance> workflow_instance = std::any_cast<std::shared_ptr<WorkflowInstance>>(workflow_created->workflow_instance);
+        workflow_instance->add_observer(m_communication_channel.get());
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+
+} // namespace mmi
diff --git a/src/mmi-manager/mmi-manager.h b/src/mmi-manager/mmi-manager.h
new file mode 100644 (file)
index 0000000..610159a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_MANAGER_H__
+#define __MMI_MANAGER_H__
+
+#include <memory>
+
+#include "mmi-client.h"
+#include "mmi-communication-channel.h"
+#include "mmi-plugin-module-registry.h"
+#include "mmi-node-instance-manager.h"
+#include "mmi-node-prototype-manager.h"
+#include "mmi-port-instance-manager.h"
+#include "mmi-workflow-instance-manager.h"
+#include "mmi-workflow-prototype-manager.h"
+#include "mmi-workflow-event.h"
+
+namespace mmi {
+
+using namespace mmi::communication;
+
+class Manager : SimpleEventObserver<WORKFLOW_EVENT_TYPE> {
+public:
+    Manager(std::shared_ptr<CommunicationChannelManagerFactory> communication_channel_factory,
+            std::shared_ptr<IPluginModuleProxyFactory> plugin_module_proxy_factory);
+    virtual ~Manager();
+
+    int initialize();
+    int deinitialize();
+
+    void on_observer_event(
+        const WORKFLOW_EVENT_TYPE &event, std::any data) override;
+
+private:
+    std::shared_ptr<CommunicationChannelManagerFactory> m_communication_channel_factory;
+    std::shared_ptr<CommunicationChannelManager> m_communication_channel;
+
+    std::shared_ptr<IPluginModuleProxyFactory> m_plugin_module_proxy_factory;
+    std::shared_ptr<PluginModuleRegistry> m_plugin_module_registry;
+
+    std::shared_ptr<ClientManager> m_client_manager;
+
+    std::shared_ptr<PortInstanceManager> m_port_instance_manager;
+
+    std::shared_ptr<NodePrototypeManager> m_node_prototype_manager;
+    std::shared_ptr<NodeInstanceManager> m_node_instance_manager;
+
+    std::shared_ptr<WorkflowPrototypeManager> m_workflow_prototype_manager;
+    std::shared_ptr<WorkflowInstanceManager> m_workflow_instance_manager;
+};
+
+} // namespace mmi
+
+#endif //__MMI_MANAGER_H__
diff --git a/src/mmi-manager/mmi-manager.xml b/src/mmi-manager/mmi-manager.xml
new file mode 100644 (file)
index 0000000..9ff40f9
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="7" package="mmi-manager" version="1.0.3">
+       <label>mmi-manager</label>
+       <author email="dyamy.lee@samsung.com" href="www.samsung.com">DaYe Lee</author>
+       <description>autofilld</description>
+       <ui-application appid="mmi-manager" exec="/usr/bin/mmi-manager" multiple="false" nodisplay="false" taskmanage="false" type="capp">
+               <label>mmi-manager</label>
+       </ui-application>
+       <privileges>
+               <privilege>http://tizen.org/privilege/inputgenerator</privilege>
+               <privilege>http://tizen.org/privilege/datasharing</privilege>
+               <privilege>http://tizen.org/privilege/externalstroage</privilege>
+               <privilege>http://tizen.org/privilege/mediastorage</privilege>
+       </privileges>
+</manifest>
diff --git a/src/mmi-manager/mmi-node-instance-manager.cpp b/src/mmi-manager/mmi-node-instance-manager.cpp
new file mode 100644 (file)
index 0000000..ab07751
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-node-instance-manager.h"
+
+#include "mmi-manager-log.h"
+
+#include <algorithm>
+
+namespace mmi {
+
+NodeInstanceManager::NodeInstanceManager() {
+}
+
+NodeInstanceManager::~NodeInstanceManager() {
+}
+
+std::shared_ptr<NodeInstance> NodeInstanceManager::create_node_instance(
+    mmi_standard_node_type_e node_type,
+    mmi_standard_node_sub_type_e node_sub_type) {
+    std::shared_ptr<NodeInstance> ret;
+
+    ret = std::make_shared<NodeInstance>(node_type, node_sub_type);
+    if (ret == nullptr) {
+        _E("Failed to create node instance");
+        return ret;
+    }
+
+    ret->set_node_prototype_store(m_prototype_store);
+    ret->set_plugin_module_proxy_provider(m_plugin_module_proxy_provider);
+    ret->set_port_instance_manager(m_port_instance_manager);
+
+    ret->initialize();
+
+    m_node_instances.push_back(ret);
+
+    return ret;
+}
+
+bool NodeInstanceManager::destroy_node_instance(
+    std::shared_ptr<NodeInstance> node_instance) {
+    if (node_instance) {
+        node_instance->deinitialize();
+    }
+
+    auto &instances = m_node_instances;
+
+    auto it = std::find(instances.begin(), instances.end(), node_instance);
+    if (it == instances.end()) {
+        _E("Node instance is not found");
+        return false;
+    }
+
+    instances.erase(it);
+
+    return true;
+}
+
+bool NodeInstanceManager::link_node_instances(
+    std::shared_ptr<NodeInstance> from_node_instance,
+    std::shared_ptr<NodeInstance> to_node_instance,
+    std::optional<std::string> from_port_name,
+    std::optional<std::string> to_port_name) {
+    if (nullptr == from_node_instance || nullptr == to_node_instance) {
+        LOGE("Failed to find node instances : from(%p) to(%p)",
+             from_node_instance.get(), to_node_instance.get());
+        return false;
+    }
+
+    if (false == from_port_name.has_value() || false == to_port_name.has_value() ) {
+        LOGE("Failed to find port names : from(%s) to(%s)",
+             from_port_name.has_value() ? from_port_name.value().c_str() : "null",
+             to_port_name.has_value() ? to_port_name.value().c_str() : "null");
+        return false;
+    }
+
+    std::shared_ptr<PortInstance> from_port_instance =
+        from_node_instance->get_port_instance(from_port_name.value());
+    std::shared_ptr<PortInstance> to_port_instance =
+        to_node_instance->get_port_instance(to_port_name.value());
+
+    if (nullptr == from_port_instance || nullptr == to_port_instance) {
+        LOGE("Failed to find port instance : %s(%p) %s(%p)",
+             from_port_name.value().c_str(), from_port_instance.get(),
+             to_port_name.value().c_str(), to_port_instance.get());
+        return false;
+    }
+
+    LOGD("Linking port instances : [%p:%s] - [%p:%s]",
+         from_node_instance.get(), from_port_name.value().c_str(),
+         to_node_instance.get(), to_port_name.value().c_str());
+
+    m_port_instance_manager->link_port_instances(from_port_instance, to_port_instance);
+
+    return true;
+}
+
+void NodeInstanceManager::initialize() {
+}
+
+void NodeInstanceManager::deinitialize() {
+}
+
+void NodeInstanceManager::set_node_prototype_store(
+    std::shared_ptr<INodePrototypeStore> prototype_store) {
+    m_prototype_store = prototype_store;
+}
+
+void NodeInstanceManager::set_plugin_module_proxy_provider(
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider) {
+    m_plugin_module_proxy_provider = plugin_module_proxy_provider;
+}
+
+void NodeInstanceManager::set_port_instance_manager(
+    std::shared_ptr<IPortInstanceManager> port_instance_manager) {
+    m_port_instance_manager = port_instance_manager;
+}
+
+void NodeInstanceManager::on_observer_event(
+    const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) {
+    try {
+        if (event == PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED) {
+            PluginModuleEventOutputGenerated *output_generated = std::any_cast<PluginModuleEventOutputGenerated*>(data);
+            if (output_generated) {
+                PortInstance *port_instance = reinterpret_cast<PortInstance*>(output_generated->port_instance);
+                if (port_instance) {
+                    _D("Output generated for %p : %p", port_instance, output_generated->data);
+                    port_instance->on_output_data_generated(output_generated->data);
+                }
+            }
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+void NodeInstanceManager::on_observer_event(
+    const PROXY_FACTORY_EVENT_TYPE &event, std::any data) {
+    try {
+        if (event == PROXY_FACTORY_EVENT_TYPE::PROXY_CREATED) {
+            IPluginModuleProxy *proxy = std::any_cast<IPluginModuleProxy*>(data);
+            if (proxy) {
+                /* Add 'this' observer as a SimpleEventObserver<PLUGIN_MODULE_EVENT_TYPE> */
+                proxy->add_observer(this);
+            }
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-node-instance-manager.h b/src/mmi-manager/mmi-node-instance-manager.h
new file mode 100644 (file)
index 0000000..3a6f613
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_NODE_INSTANCE_MANAGER_H__
+#define __MMI_NODE_INSTANCE_MANAGER_H__
+
+#include "mmi-plugin-module-event.h"
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-node-instance.h"
+#include "mmi-node-prototype-manager.h"
+#include "mmi-port-instance-manager.h"
+
+#include <memory>
+#include <vector>
+
+namespace mmi {
+
+class INodeInstanceManager {
+public:
+    INodeInstanceManager() = default;
+    virtual ~INodeInstanceManager() = default;
+
+    virtual std::shared_ptr<NodeInstance> create_node_instance(
+        mmi_standard_node_type_e node_type,
+        mmi_standard_node_sub_type_e node_sub_type) = 0;
+    virtual bool destroy_node_instance(
+        std::shared_ptr<NodeInstance> node_instance) = 0;
+    virtual bool link_node_instances(
+        std::shared_ptr<NodeInstance> from_node_instance,
+        std::shared_ptr<NodeInstance> to_node_instance,
+        std::optional<std::string> from_port_name,
+        std::optional<std::string> to_port_name) = 0;
+};
+
+class NodeInstanceManager :
+    public INodeInstanceManager,
+    public SimpleEventObserver<PLUGIN_MODULE_EVENT_TYPE>,
+    public SimpleEventObserver<PROXY_FACTORY_EVENT_TYPE> {
+public:
+    NodeInstanceManager();
+    virtual ~NodeInstanceManager();
+
+    virtual std::shared_ptr<NodeInstance> create_node_instance(
+        mmi_standard_node_type_e node_type,
+        mmi_standard_node_sub_type_e node_sub_type) override;
+    virtual bool destroy_node_instance(
+        std::shared_ptr<NodeInstance> node_instance) override;
+    virtual bool link_node_instances(
+        std::shared_ptr<NodeInstance> from_node_instance,
+        std::shared_ptr<NodeInstance> to_node_instance,
+        std::optional<std::string> from_port_name,
+        std::optional<std::string> to_port_name) override;
+
+    void set_node_prototype_store(
+        std::shared_ptr<INodePrototypeStore> prototype_store);
+    void set_plugin_module_proxy_provider(
+        std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider);
+    void set_port_instance_manager(
+        std::shared_ptr<IPortInstanceManager> port_instance_manager);
+
+    void initialize();
+    void deinitialize();
+
+    void on_observer_event(
+        const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) override;
+    void on_observer_event(
+        const PROXY_FACTORY_EVENT_TYPE &event, std::any data) override;
+
+protected:
+    std::shared_ptr<INodePrototypeStore> m_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> m_plugin_module_proxy_provider;
+    std::shared_ptr<IPortInstanceManager> m_port_instance_manager;
+
+    std::vector<std::shared_ptr<NodeInstance>> m_node_instances;
+};
+
+} // namespace mmi
+
+#endif //__MMI_NODE_INSTANCE_MANAGER_H__
+
+
diff --git a/src/mmi-manager/mmi-node-instance.cpp b/src/mmi-manager/mmi-node-instance.cpp
new file mode 100644 (file)
index 0000000..8c4c6db
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-node-instance.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+NodeInstance::NodeInstance(
+    mmi_standard_node_type_e node_type,
+    mmi_standard_node_sub_type_e node_sub_type)
+    : m_node_type(node_type)
+    , m_node_sub_type(node_sub_type) {
+    _D("Node instance is created : %d %d", m_node_type, to_int(m_node_sub_type));
+}
+
+NodeInstance::~NodeInstance() {
+    _D("Node instance is destroyed : %d %d", m_node_type, to_int(m_node_sub_type));
+}
+
+static bool node_type_and_subtype_matches(
+    mmi_node_h node,
+    mmi_standard_node_type_e node_type,
+    mmi_standard_node_sub_type_e node_sub_type) {
+    mmi_standard_node_type_e type;
+    mmi_node_get_type(node, &type);
+
+    if (type != node_type) {
+        return false;
+    }
+
+    try {
+        if (node_type == MMI_STANDARD_NODE_TYPE_SOURCE) {
+            mmi_standard_node_source_type_e source_type;
+            mmi_standard_node_get_source_type(node, &source_type);
+            return source_type == std::get<mmi_standard_node_source_type_e>(node_sub_type);
+        } else if (node_type == MMI_STANDARD_NODE_TYPE_PROCESSOR) {
+            mmi_standard_node_processor_type_e processor_type;
+            mmi_standard_node_get_processor_type(node, &processor_type);
+            return processor_type == std::get<mmi_standard_node_processor_type_e>(node_sub_type);
+        } else if (node_type == MMI_STANDARD_NODE_TYPE_LOGIC) {
+            mmi_standard_node_logic_type_e logic_type;
+            mmi_standard_node_get_logic_type(node, &logic_type);
+            return logic_type == std::get<mmi_standard_node_logic_type_e>(node_sub_type);
+        } else if (node_type == MMI_STANDARD_NODE_TYPE_CONTROLLER) {
+            mmi_standard_node_controller_type_e controller_type;
+            mmi_standard_node_get_controller_type(node, &controller_type);
+            return controller_type == std::get<mmi_standard_node_controller_type_e>(node_sub_type);
+        }
+    } catch (const std::bad_variant_access &e) {
+        _E("Failed to get node sub type : %s", e.what());
+    }
+
+    return false;
+}
+
+
+
+bool NodeInstance::initialize() {
+    if (m_prototype_store == nullptr) {
+        _E("Node prototype store is not set");
+        return false;
+    }
+
+    m_prototype = m_prototype_store->get_node_prototype(m_node_type, m_node_sub_type);
+    if (m_prototype == nullptr) {
+        _E("Failed to get node prototype");
+        return false;
+    }
+
+    if (m_plugin_module_proxy_provider == nullptr) {
+        _E("Plugin module proxy provider is not set");
+        return false;
+    }
+
+    /* Owning this shared pointer guarantees that the callback functions are
+     * valid until the node instance is destroyed. */
+    m_plugin_module_proxy = m_plugin_module_proxy_provider->get_plugin_module_proxy(
+            m_prototype->get_plugin_module_info());
+
+    if (m_plugin_module_proxy == nullptr) {
+        _E("Failed to get plugin module proxy");
+        return false;
+    }
+
+    std::vector<mmi_port_h> ports;
+    size_t port_count = 0;
+
+    mmi_plugin_module_node_list_s node_list_s = m_plugin_module_proxy->load_node_prototypes();
+    for (int i = 0; i < node_list_s.node_count; i++) {
+        mmi_node_h node = node_list_s.nodes[i];
+        if (node_type_and_subtype_matches(node, m_node_type, m_node_sub_type)) {
+            mmi_node_get_callbacks(node, &m_callbacks);
+            mmi_node_get_port_count(node, &port_count);
+            for (size_t i = 0; i < port_count; i++) {
+                mmi_port_h port;
+                mmi_node_get_port(node, i, &port);
+                ports.push_back(port);
+            }
+            break;
+        }
+    }
+
+    _D("Node instance is initialized for %d %d",
+            m_node_type, to_int(m_node_sub_type));
+
+    if (m_port_instance_manager == nullptr) {
+        _E("Port instance manager is not set");
+        return false;
+    }
+
+    mmi_plugin_module_node_instance_info_s plugin_module_node_instance_info;
+    plugin_module_node_instance_info.node = static_cast<mmi_node_instance_h>(this);
+    plugin_module_node_instance_info.port_info_count = 0;
+
+    for (auto &port_info : m_prototype->get_port_infos()) {
+        std::shared_ptr<PortInstance> port_instance = m_port_instance_manager->create_port_instance();
+        if (port_instance == nullptr) {
+            _E("Failed to create port instance");
+            return false;
+        }
+        for (auto &port : ports) {
+            char *name = nullptr;
+            size_t name_length = 0;
+            mmi_port_get_name(port, &name, &name_length);
+
+            mmi_port_callbacks callbacks;
+            mmi_port_get_callbacks(port, &callbacks);
+
+            if (strncmp(name, port_info.name.c_str(), name_length) == 0) {
+                _D("Port instance is initialized for %s, %p %p", port_info.name.c_str(),
+                        callbacks.output_format_requested_cb,
+                        callbacks.input_data_received_cb);
+                port_instance->set_port_callbacks(callbacks);
+                break;
+            }
+        }
+
+        m_port_instances[port_info.name] = port_instance;
+
+        size_t index = plugin_module_node_instance_info.port_info_count;
+        plugin_module_node_instance_info.port_infos[index].port =
+            static_cast<mmi_port_instance_h>(port_instance.get());
+        strncpy(plugin_module_node_instance_info.port_infos[index].name,
+                port_info.name.c_str(), MMI_NAME_MAX_LENGTH - 1);
+        plugin_module_node_instance_info.port_infos[index].name[MMI_NAME_MAX_LENGTH - 1] = '\0';
+
+        plugin_module_node_instance_info.port_info_count++;
+    }
+
+    m_plugin_module_proxy->add_node_instance_info(&plugin_module_node_instance_info);
+
+    if (m_callbacks.initialized_cb != nullptr) {
+        m_callbacks.initialized_cb(static_cast<mmi_node_instance_h>(this));
+    }
+
+    return true;
+}
+
+bool NodeInstance::deinitialize() {
+    if (m_callbacks.deinitialized_cb != nullptr) {
+        m_callbacks.deinitialized_cb(static_cast<mmi_node_instance_h>(this));
+    }
+
+    m_plugin_module_proxy->remove_node_instance_info(static_cast<mmi_node_instance_h>(this));
+    return true;
+}
+
+void NodeInstance::set_node_prototype_store(
+    std::shared_ptr<INodePrototypeStore> prototype_store) {
+    m_prototype_store = prototype_store;
+}
+
+void NodeInstance::set_plugin_module_proxy_provider(
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider) {
+    m_plugin_module_proxy_provider = plugin_module_proxy_provider;
+}
+
+void NodeInstance::set_port_instance_manager(
+    std::shared_ptr<IPortInstanceManager> port_instance_manager) {
+    m_port_instance_manager = port_instance_manager;
+}
+
+void NodeInstance::add_data_gateway(std::string port_name,
+                                    std::shared_ptr<DataGateway> gateway) {
+    try {
+        auto port = m_port_instances.at(port_name);
+        port->add_data_gateway(gateway);
+    } catch (const std::out_of_range &e) {
+        _E("Port instance is not found for %s, %s", port_name.c_str(), e.what());
+    }
+}
+
+mmi_standard_node_type_e NodeInstance::get_node_type() {
+    return m_node_type;
+}
+
+mmi_standard_node_sub_type_e NodeInstance::get_node_sub_type() {
+    return m_node_sub_type;
+}
+
+bool NodeInstance::activate() {
+    m_activated = true;
+    if (m_suspended) {
+        _D("Node instance is suspended, so the activated_cb is not called");
+    }
+    if (m_callbacks.activated_cb == nullptr) {
+        _E("Activate callback is not set");
+        return false;
+    }
+    m_callbacks.activated_cb(static_cast<mmi_node_instance_h>(this));
+    return true;
+}
+
+bool NodeInstance::deactivate() {
+    m_activated = false;
+    if (m_suspended) {
+        _D("Node instance is suspended, so the deactivated_cb is not called");
+    }
+    if (m_callbacks.deactivated_cb == nullptr) {
+        _E("Deactivate callback is not set");
+        return false;
+    }
+    m_callbacks.deactivated_cb(static_cast<mmi_node_instance_h>(this));
+    return true;
+}
+
+bool NodeInstance::suspend() {
+    m_suspended = true;
+    if (m_activated) {
+        if (m_callbacks.deactivated_cb != nullptr) {
+            m_callbacks.deactivated_cb(static_cast<mmi_node_instance_h>(this));
+        }
+    }
+    return true;
+}
+
+bool NodeInstance::resume() {
+    m_suspended = false;
+    if (m_activated) {
+        if (m_callbacks.activated_cb != nullptr) {
+            m_callbacks.activated_cb(static_cast<mmi_node_instance_h>(this));
+        }
+    }
+    return true;
+}
+
+std::shared_ptr<PortInstance> NodeInstance::get_port_instance(std::string name) {
+    auto it = m_port_instances.find(name);
+    if (it == m_port_instances.end()) {
+        _E("Port instance is not found : %s", name.c_str());
+        return nullptr;
+    }
+
+    return it->second;
+}
+
+std::shared_ptr<IPluginModuleProxy> NodeInstance::get_plugin_module_proxy() {
+    return m_plugin_module_proxy;
+}
+
+bool NodeInstance::set_attribute(mmi_attribute_h attribute)
+{
+    char *name = nullptr;
+    mmi_attribute_get_name(attribute, &name);
+    _D("Set attribute : %s [%p]", name, attribute);
+    free(name);
+
+    if (m_callbacks.attribute_set_cb == nullptr) {
+        _E("Set attribute callback is not set");
+        return false;
+    }
+    m_callbacks.attribute_set_cb(static_cast<mmi_node_instance_h>(this), attribute);
+    return true;
+}
+
+} // namespace mmi
+
+
diff --git a/src/mmi-manager/mmi-node-instance.h b/src/mmi-manager/mmi-node-instance.h
new file mode 100644 (file)
index 0000000..5e7e9bf
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_NODE_INSTANCE_H__
+#define __MMI_NODE_INSTANCE_H__
+
+#include "mmi-node.h"
+#include "mmi-node-prototype-manager.h"
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-port-instance-manager.h"
+
+#include <map>
+#include <memory>
+
+namespace mmi {
+
+class NodeInstance {
+public:
+    NodeInstance(mmi_standard_node_type_e node_type,
+                 mmi_standard_node_sub_type_e node_sub_type);
+    virtual ~NodeInstance();
+
+    bool initialize();
+    bool deinitialize();
+
+    void set_node_prototype_store(
+        std::shared_ptr<INodePrototypeStore> prototype_store);
+    void set_plugin_module_proxy_provider(
+        std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider);
+    void set_port_instance_manager(
+        std::shared_ptr<IPortInstanceManager> port_instance_manager);
+
+    void add_data_gateway(std::string port_name,
+                          std::shared_ptr<DataGateway> gateway);
+
+    mmi_standard_node_type_e get_node_type();
+    mmi_standard_node_sub_type_e get_node_sub_type();
+
+    bool activate();
+    bool deactivate();
+
+    bool suspend();
+    bool resume();
+
+    std::shared_ptr<PortInstance> get_port_instance(std::string name);
+    std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy();
+
+    bool set_attribute(mmi_attribute_h attribute);
+protected:
+    std::shared_ptr<INodePrototypeStore> m_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> m_plugin_module_proxy_provider;
+    std::shared_ptr<IPortInstanceManager> m_port_instance_manager;
+
+    std::shared_ptr<NodePrototype> m_prototype;
+    std::shared_ptr<IPluginModuleProxy> m_plugin_module_proxy;
+    std::map<std::string, std::shared_ptr<PortInstance>> m_port_instances;
+
+    mmi_standard_node_type_e m_node_type;
+    mmi_standard_node_sub_type_e m_node_sub_type;
+
+    mmi_node_callbacks m_callbacks{nullptr,};
+
+    bool m_suspended{false};
+    bool m_activated{false};
+};
+
+} // namespace mmi
+
+#endif //__MMI_NODE_INSTANCE_H__
+
+
diff --git a/src/mmi-manager/mmi-node-prototype-manager.cpp b/src/mmi-manager/mmi-node-prototype-manager.cpp
new file mode 100644 (file)
index 0000000..f86eec9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-node-prototype-manager.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+NodePrototypeManager::NodePrototypeManager() {
+}
+
+NodePrototypeManager::~NodePrototypeManager() {
+}
+
+bool NodePrototypeManager::add_node_prototype(std::shared_ptr<NodePrototype> prototype) {
+    if (!prototype->is_valid()) {
+        _E("ERROR: invalid node prototype");
+        return false;
+    }
+
+    /* check if we already have a prototype with the same type and sub type */
+    for (auto& elem : m_prototypes) {
+        if (elem->get_type() == prototype->get_type() &&
+                elem->get_sub_type() == prototype->get_sub_type()) {
+            _E("ERROR: prototype with the same type / sub type already exists");
+            return false;
+        }
+    }
+
+    m_prototypes.push_back(prototype);
+
+    return true;
+}
+
+std::shared_ptr<NodePrototype> NodePrototypeManager::get_node_prototype(
+    mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) {
+    std::shared_ptr<NodePrototype> ret;
+
+    for (auto& elem : m_prototypes) {
+        if (elem->get_type() == type && elem->get_sub_type() == sub_type) {
+            ret = elem;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+void NodePrototypeManager::initialize() {
+}
+
+void NodePrototypeManager::deinitialize() {
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-node-prototype-manager.h b/src/mmi-manager/mmi-node-prototype-manager.h
new file mode 100644 (file)
index 0000000..4794c90
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_NODE_PROTOTYPE_MANAGER_H__
+#define __MMI_NODE_PROTOTYPE_MANAGER_H__
+
+#include "mmi-node-prototype.h"
+
+#include <memory>
+#include <vector>
+
+namespace mmi {
+
+struct INodePrototypeStore {
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) = 0;
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) = 0;
+};
+
+class NodePrototypeManager : public INodePrototypeStore {
+public:
+    NodePrototypeManager();
+    virtual ~NodePrototypeManager();
+
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) override;
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override;
+
+    void initialize();
+    void deinitialize();
+
+protected:
+    std::vector<std::shared_ptr<NodePrototype>> m_prototypes;
+};
+
+} // namespace mmi
+
+#endif //__MMI_NODE_PROTOTYPE_MANAGER_H__
+
+
diff --git a/src/mmi-manager/mmi-node-prototype.cpp b/src/mmi-manager/mmi-node-prototype.cpp
new file mode 100644 (file)
index 0000000..2cb9a77
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-node-prototype.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+NodePrototype::NodePrototype() {
+}
+
+NodePrototype::~NodePrototype() {
+}
+
+bool NodePrototype::is_valid() {
+    if (!m_type.has_value() || !m_sub_type.has_value()) {
+        _E("Failed to validate node prototype: type or sub type is empty");
+        return false;
+    }
+
+    bool valid = false;
+    /* Make sure that the m_sub_type is a valid match for m_type */
+    try {
+        switch (m_type.value()) {
+        case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_SOURCE: {
+            auto subclass =
+                std::get<mmi_standard_node_source_type_e>(m_sub_type.value());
+            valid = true;
+        }
+        break;
+        case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_PROCESSOR: {
+            auto subclass =
+                std::get<mmi_standard_node_processor_type_e>(m_sub_type.value());
+            valid = true;
+        }
+        break;
+        case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_LOGIC: {
+            auto subclass =
+                std::get<mmi_standard_node_logic_type_e>(m_sub_type.value());
+            valid = true;
+        }
+        break;
+        case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_CONTROLLER: {
+            auto subclass =
+                std::get<mmi_standard_node_controller_type_e>(m_sub_type.value());
+            valid = true;
+        }
+        }
+    } catch (const std::bad_variant_access& e) {
+        _E("[std::bad_variant_access] Failed to create node prototype: %s", e.what());
+    } catch (const std::bad_optional_access& e) {
+        _E("[std::bad_optional_access] Failed to create node prototype: %s", e.what());
+    }
+
+    if (get_plugin_module_info().plugin_module_identifier.empty()) {
+        _E("Failed to validate node prototype: plugin module info is empty");
+        valid = false;
+    }
+
+    return valid;
+}
+
+void NodePrototype::set_type(mmi_standard_node_type_e type) {
+    m_type = type;
+}
+
+void NodePrototype::set_sub_type(mmi_standard_node_sub_type_e sub_type) {
+    m_sub_type = sub_type;
+}
+
+bool NodePrototype::add_port_info(PortInfo &port_info) {
+    for (auto& port : m_port_infos) {
+        if (port.name == port_info.name) {
+            _E("Failed to add port info: port with the same name already exists");
+            return false;
+        }
+    }
+
+    m_port_infos.push_back(port_info);
+    return true;
+}
+
+bool NodePrototype::add_attribute_info(AttributeInfo &attribute_info) {
+    /* check if we already have an attribute with the same name */
+    for (auto& attribute : m_attribute_infos) {
+        if (attribute.name == attribute_info.name) {
+            _E("Failed to add attribute info: attribute with the same name already exists");
+            return false;
+        }
+    }
+
+    m_attribute_infos.push_back(attribute_info);
+    return true;
+}
+
+mmi_standard_node_type_e NodePrototype::get_type() {
+    mmi_standard_node_type_e ret = MMI_STANDARD_NODE_TYPE_NONE;
+    if (m_type.has_value()) {
+        ret = m_type.value();
+    }
+    return ret;
+}
+
+mmi_standard_node_sub_type_e NodePrototype::get_sub_type() {
+    mmi_standard_node_sub_type_e ret = std::monostate{};
+    if (m_sub_type.has_value()) {
+        ret = m_sub_type.value();
+    }
+    return ret;
+}
+
+std::vector<PortInfo> NodePrototype::get_port_infos() {
+    return m_port_infos;
+}
+
+std::vector<AttributeInfo> NodePrototype::get_attribute_infos() {
+    return m_attribute_infos;
+}
+
+bool NodePrototype::has_port_with_name(std::string name) {
+    for (auto& port : m_port_infos) {
+        if (port.name == name) {
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-node-prototype.h b/src/mmi-manager/mmi-node-prototype.h
new file mode 100644 (file)
index 0000000..9273134
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_NODE_PROTOTYPE_H__
+#define __MMI_NODE_PROTOTYPE_H__
+
+#include "mmi-common.h"
+#include "mmi-node.h"
+#include "mmi-node-source.h"
+#include "mmi-node-processor.h"
+#include "mmi-node-logic.h"
+#include "mmi-node-controller.h"
+#include "mmi-plugin-module-proxy.h"
+
+#include <memory>
+#include <optional>
+#include <variant>
+#include <vector>
+
+namespace mmi {
+
+typedef std::variant<
+    std::monostate,
+    mmi_standard_node_source_type_e,
+    mmi_standard_node_processor_type_e,
+    mmi_standard_node_logic_type_e,
+    mmi_standard_node_controller_type_e
+    > mmi_standard_node_sub_type_e;
+
+static int to_int(mmi_standard_node_sub_type_e sub_type) {
+    try {
+        return std::visit([](auto&& arg) -> int {
+            using T = std::decay_t<decltype(arg)>;
+            if constexpr (std::is_same_v<T, std::monostate>)
+                return -1;
+            else
+                return static_cast<int>(arg);
+        }, sub_type);
+    } catch (std::bad_variant_access&) {
+        return -1;
+    }
+}
+
+typedef struct {
+    std::string name;
+    mmi_port_type_e port_type;
+    mmi_data_type_e data_type;
+    std::string data_description;
+} PortInfo;
+
+typedef struct {
+    std::string name;
+    mmi_primitive_value_type_e value_type;
+} AttributeInfo;
+
+class NodePrototype {
+public:
+    NodePrototype();
+    virtual ~NodePrototype();
+
+    bool is_valid();
+
+    void set_plugin_module_info(PluginModuleInfo plugin_module_info) {
+        m_plugin_module_info = plugin_module_info;
+    }
+    PluginModuleInfo get_plugin_module_info() {
+        return m_plugin_module_info;
+    }
+
+    void set_type(mmi_standard_node_type_e type);
+    void set_sub_type(mmi_standard_node_sub_type_e sub_type);
+
+    bool add_port_info(PortInfo &port_info);
+    bool add_attribute_info(AttributeInfo &attribute_info);
+
+    mmi_standard_node_type_e get_type();
+    mmi_standard_node_sub_type_e get_sub_type();
+
+    std::vector<PortInfo> get_port_infos();
+    std::vector<AttributeInfo> get_attribute_infos();
+
+    bool has_port_with_name(std::string name);
+private:
+    PluginModuleInfo m_plugin_module_info;
+
+    std::optional<mmi_standard_node_type_e> m_type;
+    std::optional<mmi_standard_node_sub_type_e> m_sub_type;
+
+    std::vector<PortInfo> m_port_infos;
+    std::vector<AttributeInfo> m_attribute_infos;
+};
+
+} // namespace mmi
+
+#endif //__MMI_NODE_PROTOTYPE_H__
+
+
diff --git a/src/mmi-manager/mmi-plugin-module-proxy.cpp b/src/mmi-manager/mmi-plugin-module-proxy.cpp
new file mode 100644 (file)
index 0000000..b9682dc
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-manager-log.h"
+
+#include <dlfcn.h>
+
+namespace mmi {
+
+static void port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data) {
+    PluginModuleProxySharedLibrary* proxy = static_cast<PluginModuleProxySharedLibrary*>(user_data);
+    if (proxy == nullptr) {
+        _E("Invalid proxy");
+        return;
+    }
+
+    PluginModuleEventOutputGenerated event;
+    event.port_instance = port;
+    event.data = data;
+    proxy->notify_observers(PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED, &event);
+}
+
+static void switch_node_event_handler(mmi_node_instance_h node, const char *controllee, bool state, void *user_data) {
+    PluginModuleProxySharedLibrary* proxy = static_cast<PluginModuleProxySharedLibrary*>(user_data);
+    if (proxy == nullptr) {
+        _E("Invalid proxy");
+        return;
+    }
+    if (controllee == nullptr) {
+        _E("Controllee is null");
+        return;
+    }
+
+    PluginModuleEventSwitchEventEmitted event;
+    event.node_instance = node;
+    event.controllee = controllee;
+    event.state = state;
+    proxy->notify_observers(PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED, &event);
+}
+
+PluginModuleProxySharedLibrary::PluginModuleProxySharedLibrary(
+    const mmi_plugin_module_identifier plugin_module_identifier) :
+    m_plugin_module_identifier(plugin_module_identifier) {
+    mmi_plugin_module_event_handler_info_s module_event_handler_info{nullptr, };
+    module_event_handler_info.port_instance_output_handler = port_instance_output_handler;
+    module_event_handler_info.switch_node_event_handler = switch_node_event_handler;
+
+    /* FIXME : When there are more than one plugin module, the logic should be changed
+     * since the global event handler will be overwritten by the last plugin module.
+     * But the overwrite would not cause any problem for now, since the event handler
+     * will always have the pointer to the static function port_instance_output_handler(). */
+    mmi_plugin_storage_set_plugin_module_event_handler(module_event_handler_info, this);
+
+    m_shared_object_handle = dlopen(m_plugin_module_identifier.c_str(), RTLD_NOW);
+    if (!m_shared_object_handle) {
+        _E("Failed to load plugin module: %s", dlerror());
+    }
+}
+
+PluginModuleProxySharedLibrary::~PluginModuleProxySharedLibrary() {
+    if (m_shared_object_handle) dlclose(m_shared_object_handle);
+    m_shared_object_handle = nullptr;
+}
+
+function_pointer PluginModuleProxySharedLibrary::get_function(const std::string& function_name) {
+    return dlsym(m_shared_object_handle, function_name.c_str());
+}
+
+mmi_plugin_module_node_list_s PluginModuleProxySharedLibrary::load_node_prototypes() {
+    mmi_plugin_module_node_list_s node_list;
+    memset(&node_list, 0, sizeof(mmi_plugin_module_node_list_s));
+
+    mmi_plugin_storage_set_current_module_identifier(m_plugin_module_identifier.c_str());
+
+    function_pointer load_node_prototypes = get_function(MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME);
+    if (load_node_prototypes) {
+        auto func = (mmi_plugin_module_get_node_list_func)load_node_prototypes;
+        func();
+    } else {
+        _E("No prototype provider function exists for node prototypes : %s", m_plugin_module_identifier.c_str());
+        return node_list;
+    }
+
+    node_list = mmi_plugin_storage_get_node_list(m_plugin_module_identifier.c_str());
+
+    /* Dump node_list */
+    _D("node_list.count: %zu", node_list.node_count);
+    for (size_t i = 0;i < node_list.node_count;i++) {
+        mmi_node_h node = node_list.nodes[i];
+        mmi_standard_node_type_e type;
+        mmi_node_get_type(node, &type);
+        mmi_node_callbacks callbacks;
+        mmi_node_get_callbacks(node, &callbacks);
+        size_t port_count;
+        _D("node_info[%zu].type: %d", i, type);
+        _D("node_info[%zu].callbacks : %p %p %p %p %p %p", i,
+            callbacks.initialized_cb,
+            callbacks.deinitialized_cb,
+            callbacks.attribute_set_cb,
+            callbacks.activated_cb,
+            callbacks.deactivated_cb,
+            callbacks.signal_received_cb);
+        mmi_node_get_port_count(node, &port_count);
+        _D("node_info[%zu].port_list.count: %zu", i, port_count);
+        for (size_t j = 0;j < port_count;j++) {
+            mmi_port_h port;
+            mmi_node_get_port(node, j, &port);
+            char *name = nullptr;
+            size_t length = 0;
+            mmi_port_get_name(port, &name, &length);
+            mmi_port_type_e port_type;
+            mmi_port_get_type(port, &port_type);
+            mmi_data_type_e data_type;
+            mmi_port_get_data_type(port, &data_type);
+            mmi_port_callbacks port_callbacks;
+            mmi_port_get_callbacks(port, &port_callbacks);
+            _D("node_info[%zu].port_info[%zu].name: %s", i, j, name);
+            _D("node_info[%zu].port_info[%zu].type: %d", i, j, port_type);
+            _D("node_info[%zu].port_info[%zu].data_type: %d", i, j, data_type);
+            _D("node_info[%zu].port_info[%zu].callbacks: %p %p", i, j,
+                port_callbacks.output_format_requested_cb,
+                port_callbacks.input_data_received_cb);
+        }
+    }
+    return node_list;
+}
+
+mmi_plugin_module_workflow_list_s PluginModuleProxySharedLibrary::load_workflow_prototypes() {
+    mmi_plugin_module_workflow_list_s workflow_list;
+    memset(&workflow_list, 0, sizeof(mmi_plugin_module_workflow_list_s));
+
+    mmi_plugin_storage_set_current_module_identifier(m_plugin_module_identifier.c_str());
+
+    function_pointer load_workflow_prototypes = get_function(MMI_PLUGIN_MODULE_GET_WORKFLOW_LIST_FUNC_NAME);
+    if (load_workflow_prototypes) {
+        auto func = (mmi_plugin_module_get_workflow_list_func)load_workflow_prototypes;
+        func();
+    } else {
+        _E("No prototype provider function exists for workflow prototypes : %s", m_plugin_module_identifier.c_str());
+        return workflow_list;
+    }
+
+    workflow_list = mmi_plugin_storage_get_workflow_list(m_plugin_module_identifier.c_str());
+
+    /* Dump workflow_list */
+    _D("workflow_list.count: %zu", workflow_list.workflow_count);
+    for (size_t i = 0;i < workflow_list.workflow_count;i++) {
+        mmi_workflow_h workflow = workflow_list.workflows[i];
+        mmi_workflow_s *workflow_info = static_cast<mmi_workflow_s*>(workflow);
+        mmi_standard_workflow_type_e type;
+        mmi_workflow_get_type(workflow, &type);
+        _D("workflow_info[%zu].type: %d", i, type);
+
+        _D("workflow_info[%zu].node_info_count: %zu", i, workflow_info->node_info_count);
+        for (size_t j = 0;j < workflow_info->node_info_count;j++) {
+            mmi_workflow_node_info_s *node_info = &(workflow_info->node_infos[j]);
+            mmi_standard_node_type_e type;
+            mmi_node_get_type(workflow_info->node_infos[j].node, &type);
+            _D("workflow_info[%zu].node_info[%zu].name: %s", i, j, node_info->name);
+            _D("workflow_info[%zu].node_info[%zu].type: %d", i, j, type);
+        }
+
+        _D("workflow_info[%zu].link_info_count: %zu", i, workflow_info->link_info_count);
+        for (size_t j = 0;j < workflow_info->link_info_count;j++) {
+            mmi_workflow_link_info_s *link_info = &(workflow_info->link_infos[j]);
+            _D("workflow_info[%zu].link_info[%zu].from_node_name: %s", i, j, link_info->from_node_name);
+            _D("workflow_info[%zu].link_info[%zu].from_port_name: %s", i, j, link_info->from_port_name);
+            _D("workflow_info[%zu].link_info[%zu].to_node_name: %s", i, j, link_info->to_node_name);
+            _D("workflow_info[%zu].link_info[%zu].to_port_name: %s", i, j, link_info->to_port_name);
+        }
+
+        _D("workflow_info[%zu].output_assignment_info_count: %zu", i, workflow_info->output_assignment_info_count);
+        for (size_t j = 0;j < workflow_info->output_assignment_info_count;j++) {
+            mmi_workflow_output_assignment_info_s *output_assignment_info = &(workflow_info->output_assignment_infos[j]);
+            _D("workflow_info[%zu].output_assignment_info[%zu].output_name: %s", i, j, output_assignment_info->output_name);
+            _D("workflow_info[%zu].output_assignment_info[%zu].from_node_name: %s", i, j, output_assignment_info->from_node_name);
+            _D("workflow_info[%zu].output_assignment_info[%zu].from_port_name: %s", i, j, output_assignment_info->from_port_name);
+        }
+    }
+    return workflow_list;
+}
+
+void PluginModuleProxySharedLibrary::add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) {
+    mmi_plugin_storage_add_node_instance_info(node_instance_info);
+}
+
+void PluginModuleProxySharedLibrary::remove_node_instance_info(mmi_node_instance_h node_instance) {
+    mmi_plugin_storage_remove_node_instance_info(node_instance);
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-plugin-module-proxy.h b/src/mmi-manager/mmi-plugin-module-proxy.h
new file mode 100644 (file)
index 0000000..f02002e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_PLUGIN_MODULE_PROXY_H__
+#define __MMI_PLUGIN_MODULE_PROXY_H__
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "mmi-common.h"
+#include "mmi-plugin-module.h"
+#include "mmi-plugin-module-event.h"
+#include "mmi-plugin-storage.h"
+
+#include "mmi.h"
+
+namespace mmi {
+
+typedef void* function_pointer;
+
+typedef std::string mmi_plugin_module_identifier;
+
+enum class mmi_plugin_module_type_e {
+    SELF_CONTAINED = 0,
+    SHARED_LIBRARY,
+};
+
+typedef struct {
+    mmi_plugin_module_type_e plugin_module_type;
+    mmi_plugin_module_identifier plugin_module_identifier;
+} PluginModuleInfo;
+
+struct IPluginModuleProxy : public SimpleEventObservable<PLUGIN_MODULE_EVENT_TYPE> {
+    IPluginModuleProxy() = default;
+    virtual ~IPluginModuleProxy() = default;
+    virtual function_pointer get_function(const std::string& function_name) = 0;
+
+    virtual mmi_plugin_module_node_list_s load_node_prototypes() = 0;
+    virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() = 0;
+
+    virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) = 0;
+    virtual void remove_node_instance_info(mmi_node_instance_h node_instance) = 0;
+};
+
+struct IPluginModuleProxyProvider {
+    virtual std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy(
+        const PluginModuleInfo plugin_module_info) = 0;
+};
+
+class PluginModuleProxySharedLibrary : public IPluginModuleProxy {
+public:
+    PluginModuleProxySharedLibrary(const mmi_plugin_module_identifier plugin_module_identifier);
+    virtual ~PluginModuleProxySharedLibrary();
+
+    virtual function_pointer get_function(const std::string& function_name) override;
+
+    virtual mmi_plugin_module_node_list_s load_node_prototypes() override;
+    virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() override;
+
+    virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) override;
+    virtual void remove_node_instance_info(mmi_node_instance_h node_instance) override;
+private:
+    const mmi_plugin_module_identifier m_plugin_module_identifier;
+
+    void* m_shared_object_handle{nullptr};
+};
+
+enum class PROXY_FACTORY_EVENT_TYPE {
+    PROXY_CREATED,
+};
+
+struct IPluginModuleProxyFactory : public SimpleEventObservable<PROXY_FACTORY_EVENT_TYPE> {
+    virtual std::vector<PluginModuleInfo> get_plugin_module_list() = 0;
+    virtual std::shared_ptr<IPluginModuleProxy> create(
+        PluginModuleInfo plugin_module_info) = 0;
+};
+
+class PluginModuleProxyFactoryDefault : public IPluginModuleProxyFactory {
+public:
+    virtual std::vector<PluginModuleInfo> get_plugin_module_list() override {
+        const std::string plugin_module_path{"/usr/share/mmi/plugins"};
+        std::vector<PluginModuleInfo> plugin_module_list;
+        for (const auto & entry : std::filesystem::directory_iterator(plugin_module_path)) {
+            PluginModuleInfo info{mmi_plugin_module_type_e::SHARED_LIBRARY, entry.path().string()};
+            plugin_module_list.push_back(info);
+        }
+        return plugin_module_list;
+    }
+    virtual std::shared_ptr<IPluginModuleProxy> create(
+        PluginModuleInfo plugin_module_info) override {
+        if (plugin_module_info.plugin_module_type == mmi_plugin_module_type_e::SHARED_LIBRARY) {
+            auto proxy = std::make_shared<PluginModuleProxySharedLibrary>(
+                             plugin_module_info.plugin_module_identifier);
+            notify_observers(PROXY_FACTORY_EVENT_TYPE::PROXY_CREATED, static_cast<IPluginModuleProxy*>(proxy.get()));
+            return proxy;
+        }
+        return std::shared_ptr<IPluginModuleProxy>();
+    }
+};
+
+} // namespace mmi
+
+#endif //__MMI_PLUGIN_MODULE_PROXY_H__
+
+
diff --git a/src/mmi-manager/mmi-plugin-module-registry.cpp b/src/mmi-manager/mmi-plugin-module-registry.cpp
new file mode 100644 (file)
index 0000000..0f19279
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-plugin-module-registry.h"
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+PluginModuleRegistry::PluginModuleRegistry() {
+    _D("PluginModuleRegistry is created");
+}
+
+PluginModuleRegistry::~PluginModuleRegistry() {
+    _D("PluginModuleRegistry is destroyed");
+}
+
+void PluginModuleRegistry::initialize() {
+    if (!m_factory) {
+        _E("Plugin module proxy factory is not set");
+        return;
+    }
+
+    load_node_prototypes();
+    load_workflow_prototypes();
+}
+
+void PluginModuleRegistry::deinitialize() {
+}
+
+void PluginModuleRegistry::set_plugin_module_proxy_factory(std::shared_ptr<IPluginModuleProxyFactory> factory) {
+    m_factory = factory;
+}
+
+void PluginModuleRegistry::set_node_prototype_store(std::shared_ptr<INodePrototypeStore> node_prototype_store) {
+    m_node_prototype_store = node_prototype_store;
+}
+
+void PluginModuleRegistry::set_workflow_prototype_store(std::shared_ptr<IWorkflowPrototypeStore> workflow_prototype_store) {
+    m_workflow_prototype_store = workflow_prototype_store;
+}
+
+/* FIXME : Check if this function needs to be moved to INodePrototypeStore */
+static std::shared_ptr<NodePrototype> create_node_prototype(mmi_node_h node) {
+    if (!node) {
+        _E("Invalid argument");
+        return nullptr;
+    }
+
+    auto prototype = std::make_shared<NodePrototype>();
+
+    if (prototype) {
+        mmi_standard_node_type_e type;
+        mmi_node_get_type(node, &type);
+
+        prototype->set_type(type);
+        switch (type) {
+        case MMI_STANDARD_NODE_TYPE_SOURCE: {
+            mmi_standard_node_source_type_e source_type;
+            mmi_standard_node_get_source_type(node, &source_type);
+            prototype->set_sub_type(source_type);
+            break;
+        }
+        case MMI_STANDARD_NODE_TYPE_PROCESSOR: {
+            mmi_standard_node_processor_type_e processor_type;
+            mmi_standard_node_get_processor_type(node, &processor_type);
+            prototype->set_sub_type(processor_type);
+            break;
+        }
+        case MMI_STANDARD_NODE_TYPE_LOGIC: {
+            mmi_standard_node_logic_type_e logic_type;
+            mmi_standard_node_get_logic_type(node, &logic_type);
+            prototype->set_sub_type(logic_type);
+            break;
+        }
+        case MMI_STANDARD_NODE_TYPE_CONTROLLER: {
+            mmi_standard_node_controller_type_e controller_type;
+            mmi_standard_node_get_controller_type(node, &controller_type);
+            prototype->set_sub_type(controller_type);
+            break;
+        }
+        }
+
+        size_t port_count;
+        mmi_node_get_port_count(node, &port_count);
+        for (size_t i = 0; i < port_count; i++) {
+            mmi_port_h port;
+            mmi_node_get_port(node, i, &port);
+
+            char *name = nullptr;
+            size_t name_length = 0;
+            mmi_port_get_name(port, &name, &name_length);
+
+            mmi_port_type_e type;
+            mmi_port_get_type(port, &type);
+
+            mmi_data_type_e data_type;
+            mmi_port_get_data_type(port, &data_type);
+
+            PortInfo port_info;
+            port_info.name = name;
+            port_info.port_type = type;
+            port_info.data_type = data_type;
+            prototype->add_port_info(port_info);
+        }
+    } else {
+        _E("Failed to create node prototype");
+    }
+
+    return prototype;
+}
+
+bool PluginModuleRegistry::load_node_prototypes() {
+    if (!m_node_prototype_store) {
+        _E("Node prototype store is not set");
+        return false;
+    }
+
+    std::vector<PluginModuleInfo> plugin_module_list = m_factory->get_plugin_module_list();
+    for (auto& plugin_module_info : plugin_module_list) {
+        std::shared_ptr<IPluginModuleProxy> ptr =
+            get_plugin_module_proxy(plugin_module_info);
+        if (ptr) {
+            mmi_plugin_module_node_list_s node_list = ptr->load_node_prototypes();
+            for (size_t i = 0; i < node_list.node_count; i++) {
+                auto prototype = create_node_prototype(node_list.nodes[i]);
+                if (prototype) {
+                    prototype->set_plugin_module_info(plugin_module_info);
+                    bool ret = m_node_prototype_store->add_node_prototype(prototype);
+                    _D("Add Node Prototype ret(%d): %d %d %s", ret, prototype->get_type(), to_int(prototype->get_sub_type()), plugin_module_info.plugin_module_identifier.c_str());
+                }
+            }
+        } else {
+            _E("Failed to get plugin module: %s", plugin_module_info.plugin_module_identifier.c_str());
+        }
+    }
+
+    return true;
+}
+
+/* FIXME : Check if this function needs to be moved to IWorkflowPrototypeStore */
+static std::shared_ptr<WorkflowPrototype> create_workflow_prototype(mmi_workflow_h workflow) {
+    if (!workflow) {
+        _E("Invalid argument");
+        return nullptr;
+    }
+
+    auto prototype = std::make_shared<WorkflowPrototype>();
+
+    if (prototype) {
+        mmi_standard_workflow_type_e type;
+        mmi_workflow_get_type(workflow, &type);
+
+        prototype->set_type(type);
+
+        mmi_workflow_s *workflow_info = static_cast<mmi_workflow_s*>(workflow);
+        for (size_t i = 0; i < workflow_info->node_info_count; i++) {
+            NodeInfo node_info;
+            node_info.name = workflow_info->node_infos[i].name;
+            mmi_node_get_type(workflow_info->node_infos[i].node, &node_info.node_type);
+            switch (node_info.node_type) {
+            case MMI_STANDARD_NODE_TYPE_SOURCE: {
+                mmi_standard_node_source_type_e source_type;
+                mmi_standard_node_get_source_type(workflow_info->node_infos[i].node, &source_type);
+                node_info.node_sub_type = source_type;
+            }
+            break;
+            case MMI_STANDARD_NODE_TYPE_PROCESSOR: {
+                mmi_standard_node_processor_type_e processor_type;
+                mmi_standard_node_get_processor_type(workflow_info->node_infos[i].node, &processor_type);
+                node_info.node_sub_type = processor_type;
+            }
+            break;
+            case MMI_STANDARD_NODE_TYPE_LOGIC: {
+                mmi_standard_node_logic_type_e logic_type;
+                mmi_standard_node_get_logic_type(workflow_info->node_infos[i].node, &logic_type);
+                node_info.node_sub_type = logic_type;
+            }
+            break;
+            case MMI_STANDARD_NODE_TYPE_CONTROLLER: {
+                mmi_standard_node_controller_type_e controller_type;
+                mmi_standard_node_get_controller_type(workflow_info->node_infos[i].node, &controller_type);
+                node_info.node_sub_type = controller_type;
+            }
+            break;
+            default: {
+                _E("Invalid node type");
+                return nullptr;
+            }
+            }
+
+            prototype->add_node_info(node_info);
+        }
+
+        for (size_t i = 0; i < workflow_info->link_info_count; i++) {
+            LinkInfo link_info;
+            link_info.from_node_name = workflow_info->link_infos[i].from_node_name;
+            link_info.from_port_name = workflow_info->link_infos[i].from_port_name;
+            link_info.to_node_name = workflow_info->link_infos[i].to_node_name;
+            link_info.to_port_name = workflow_info->link_infos[i].to_port_name;
+
+            prototype->add_link_info(link_info);
+        }
+
+        for (size_t i = 0; i < workflow_info->attribute_assignment_info_count; i++) {
+            AttributeAssignmentInfo attribute_assignment_info;
+            attribute_assignment_info.attribute_name = workflow_info->attribute_assignment_infos[i].attribute_name;
+            attribute_assignment_info.target_node_name = workflow_info->attribute_assignment_infos[i].target_node_name;
+            attribute_assignment_info.target_attribute_name = workflow_info->attribute_assignment_infos[i].target_attribute_name;
+
+            prototype->add_attribute_assignment_info(attribute_assignment_info);
+        }
+
+        for (size_t i = 0; i < workflow_info->attribute_default_value_info_count; i++) {
+            AttributeDefaultValueInfo attribute_default_value_info;
+            mmi_attribute_h default_value = nullptr;
+            int ret = mmi_attribute_from_bytes(
+                workflow_info->attribute_default_value_infos[i].serialized_default_value,
+                workflow_info->attribute_default_value_infos[i].serialized_default_value_size,
+                &default_value);
+            if (MMI_ERROR_NONE != ret) {
+                _E("Failed to create attribute from bytes");
+            }
+            attribute_default_value_info.default_value = default_value;
+
+            prototype->add_attribute_default_value_info(attribute_default_value_info);
+        }
+
+        for (size_t i = 0; i < workflow_info->output_assignment_info_count; i++) {
+            OutputAssignmentInfo output_assignment_info;
+            output_assignment_info.output_name = workflow_info->output_assignment_infos[i].output_name;
+            output_assignment_info.from_node_name = workflow_info->output_assignment_infos[i].from_node_name;
+            output_assignment_info.from_port_name = workflow_info->output_assignment_infos[i].from_port_name;
+
+            prototype->add_output_assignment_info(output_assignment_info);
+        }
+    } else {
+        _E("Failed to create workflow prototype");
+    }
+
+    return prototype;
+}
+
+bool PluginModuleRegistry::load_workflow_prototypes() {
+    if (!m_workflow_prototype_store) {
+        _E("Workflow prototype store is not set");
+        return false;
+    }
+
+    std::vector<PluginModuleInfo> plugin_module_list = m_factory->get_plugin_module_list();
+    for (auto& plugin_module_info : plugin_module_list) {
+        std::shared_ptr<IPluginModuleProxy> ptr =
+            get_plugin_module_proxy(plugin_module_info);
+        if (ptr) {
+            mmi_plugin_module_workflow_list_s workflow_list = ptr->load_workflow_prototypes();
+            for (size_t i = 0; i < workflow_list.workflow_count; i++) {
+                auto prototype = create_workflow_prototype(workflow_list.workflows[i]);
+                if (prototype) {
+                    prototype->set_plugin_module_info(plugin_module_info);
+                    m_workflow_prototype_store->add_workflow_prototype(prototype);
+                }
+            }
+        } else {
+            _E("Failed to get plugin module: %s", plugin_module_info.plugin_module_identifier.c_str());
+        }
+    }
+
+    return true;
+}
+
+std::shared_ptr<IPluginModuleProxy> PluginModuleRegistry::get_plugin_module_proxy(
+    const PluginModuleInfo plugin_module_info) {
+    plugin_module_map_key key(
+        plugin_module_info.plugin_module_type, plugin_module_info.plugin_module_identifier);
+    auto it = m_plugin_modules.find(key);
+    if (it != m_plugin_modules.end()) {
+        auto plugin_module = it->second.lock();
+        if (plugin_module) {
+            return plugin_module;
+        } else {
+            _D("Plugin module proxy is expired: %d %s",
+               static_cast<int>(plugin_module_info.plugin_module_type),
+               plugin_module_info.plugin_module_identifier.c_str());
+        }
+    }
+
+    if (!m_factory) {
+        _E("Plugin module proxy factory is not set");
+        return nullptr;
+    }
+
+    _D("Create plugin module proxy: %d %s",
+       static_cast<int>(plugin_module_info.plugin_module_type),
+       plugin_module_info.plugin_module_identifier.c_str());
+
+    auto plugin_module = m_factory->create(plugin_module_info);
+    if (plugin_module) {
+        m_plugin_modules[key] = plugin_module;
+    }
+
+    return plugin_module;
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-plugin-module-registry.h b/src/mmi-manager/mmi-plugin-module-registry.h
new file mode 100644 (file)
index 0000000..f5b6fae
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_PLUGIN_MODULE_REGISTRY_H__
+#define __MMI_PLUGIN_MODULE_REGISTRY_H__
+
+#include "mmi.h"
+#include "mmi-common.h"
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-manager-log.h"
+#include "mmi-plugin-module.h"
+#include "mmi-node-prototype-manager.h"
+#include "mmi-workflow-prototype-manager.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace mmi {
+
+class PluginModuleRegistry : public IPluginModuleProxyProvider {
+public:
+    PluginModuleRegistry();
+    virtual ~PluginModuleRegistry();
+
+    void initialize();
+    void deinitialize();
+
+    void set_plugin_module_proxy_factory(std::shared_ptr<IPluginModuleProxyFactory> factory);
+    void set_node_prototype_store(std::shared_ptr<INodePrototypeStore> node_prototype_store);
+    void set_workflow_prototype_store(std::shared_ptr<IWorkflowPrototypeStore> workflow_prototype_store);
+
+    bool load_node_prototypes();
+    bool load_workflow_prototypes();
+
+    std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy(
+        const PluginModuleInfo plugin_module_info) override;
+protected:
+    std::shared_ptr<IPluginModuleProxyFactory> m_factory;
+
+    std::shared_ptr<INodePrototypeStore> m_node_prototype_store;
+    std::shared_ptr<IWorkflowPrototypeStore> m_workflow_prototype_store;
+
+    typedef std::pair<mmi_plugin_module_type_e, std::string> plugin_module_map_key;
+    std::map<plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>> m_plugin_modules;
+};
+
+} // namespace mmi
+
+#endif //__MMI_PLUGIN_MODULE_REGISTRY_H__
+
+
diff --git a/src/mmi-manager/mmi-port-instance-manager.cpp b/src/mmi-manager/mmi-port-instance-manager.cpp
new file mode 100644 (file)
index 0000000..d5bd9d1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-port-instance-manager.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+PortInstanceManager::PortInstanceManager() {
+}
+
+PortInstanceManager::~PortInstanceManager() {
+}
+
+std::shared_ptr<PortInstance> PortInstanceManager::create_port_instance() {
+    std::shared_ptr<PortInstance> port_instance = std::make_shared<PortInstance>();
+    m_port_instances.push_back(port_instance);
+    return port_instance;
+}
+
+bool PortInstanceManager::destroy_port_instance(
+    std::shared_ptr<PortInstance> port_instance) {
+    for (auto it = m_port_instances.begin(); it != m_port_instances.end(); ++it) {
+        if (*it == port_instance) {
+            m_port_instances.erase(it);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool PortInstanceManager::link_port_instances(
+    std::shared_ptr<PortInstance> out_port,
+    std::shared_ptr<PortInstance> in_port) {
+    out_port->add_linked_port_instance(in_port);
+    in_port->add_linked_port_instance(out_port);
+    return true;
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-port-instance-manager.h b/src/mmi-manager/mmi-port-instance-manager.h
new file mode 100644 (file)
index 0000000..cce6b52
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_PORT_INSTANCE_MANAGER_H__
+#define __MMI_PORT_INSTANCE_MANAGER_H__
+
+#include "mmi-port-instance.h"
+
+#include <memory>
+#include <vector>
+
+namespace mmi {
+
+class IPortInstanceManager {
+public:
+    IPortInstanceManager() = default;
+    virtual ~IPortInstanceManager() = default;
+
+    virtual std::shared_ptr<PortInstance> create_port_instance() = 0;
+    virtual bool destroy_port_instance(
+        std::shared_ptr<PortInstance> port_instance) = 0;
+    virtual bool link_port_instances(
+        std::shared_ptr<PortInstance> out_port,
+        std::shared_ptr<PortInstance> in_port) = 0;
+};
+
+class PortInstanceManager : public IPortInstanceManager {
+public:
+    PortInstanceManager();
+    virtual ~PortInstanceManager();
+
+    virtual std::shared_ptr<PortInstance> create_port_instance() override;
+    virtual bool destroy_port_instance(
+        std::shared_ptr<PortInstance> port_instance) override;
+    virtual bool link_port_instances(
+        std::shared_ptr<PortInstance> out_port,
+        std::shared_ptr<PortInstance> in_port) override;
+
+    void initialize();
+    void deinitialize();
+
+protected:
+    std::vector<std::shared_ptr<PortInstance>> m_port_instances;
+};
+
+} // namespace mmi
+
+#endif //__MMI_PORT_INSTANCE_MANAGER_H__
+
+
diff --git a/src/mmi-manager/mmi-port-instance.cpp b/src/mmi-manager/mmi-port-instance.cpp
new file mode 100644 (file)
index 0000000..d74e0b4
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-port-instance.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+PortInstance::PortInstance() {
+    _D("PortInstance created : %p", this);
+}
+
+PortInstance::~PortInstance() {
+}
+
+void PortInstance::set_port_callbacks(mmi_port_callbacks callbacks) {
+    _D("PortInstance[%p] set_port_callbacks : %p %p", this,
+       callbacks.output_format_requested_cb, callbacks.input_data_received_cb);
+    m_callbacks = callbacks;
+}
+
+void PortInstance::add_data_gateway(std::shared_ptr<DataGateway> gateway) {
+    _D("PortInstance[%p] add_data_gateway : %p", this, gateway.get());
+    m_data_gateways.push_back(gateway);
+}
+
+void PortInstance::add_linked_port_instance(std::shared_ptr<PortInstance> port_instance) {
+    m_linked_port_instances.push_back(port_instance);
+}
+
+void PortInstance::on_output_data_generated(mmi_data_h data) {
+    _D("Data generated : this[%p] data[%p]", this, data);
+
+    for (auto it = m_data_gateways.begin(); it != m_data_gateways.end(); ++it) {
+        (*it)->send_data(data);
+    }
+
+    for (auto it = m_linked_port_instances.begin(); it != m_linked_port_instances.end(); ++it) {
+        (*it)->on_output_data_received(data);
+    }
+}
+
+void PortInstance::on_output_data_received(mmi_data_h data) {
+    _D("Data received : this[%p] data[%p] input_data_received_cb[%p]",
+       this, data, m_callbacks.input_data_received_cb);
+    if (m_callbacks.input_data_received_cb) {
+        m_callbacks.input_data_received_cb(static_cast<mmi_port_instance_h>(this), data);
+    }
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-port-instance.h b/src/mmi-manager/mmi-port-instance.h
new file mode 100644 (file)
index 0000000..e508a6b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_PORT_INSTANCE_H__
+#define __MMI_PORT_INSTANCE_H__
+
+#include "mmi.h"
+#include "mmi-port.h"
+#include "mmi-data-gateway.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mmi {
+
+class PortInstance {
+public:
+    PortInstance();
+    virtual ~PortInstance();
+
+    void set_port_callbacks(mmi_port_callbacks callbacks);
+
+    void add_linked_port_instance(std::shared_ptr<PortInstance> port_instance);
+    void add_data_gateway(std::shared_ptr<DataGateway> gateway);
+
+    /* Output data was generated by a Node module */
+    void on_output_data_generated(mmi_data_h data);
+
+    /* An output data generated by a different Node module was received through a link */
+    void on_output_data_received(mmi_data_h data);
+private:
+    std::string m_name;
+    mmi_port_callbacks m_callbacks{nullptr,};
+
+    std::vector<std::shared_ptr<PortInstance>> m_linked_port_instances;
+    std::vector<std::shared_ptr<DataGateway>> m_data_gateways;
+};
+
+} // namespace mmi
+
+#endif //__MMI_PORT_INSTANCE_H__
+
+
diff --git a/src/mmi-manager/mmi-self-container.cpp b/src/mmi-manager/mmi-self-container.cpp
new file mode 100644 (file)
index 0000000..daffaf9
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-self-container.h"
+#include "mmi-port-instance-manager.h"
+
+#include <map>
+
+namespace mmi {
+
+std::map<mmi_port_instance_h, mmi_plugin_module_node_instance_info_s> g_node_instance_info_map;
+
+TestNodePluginModule::TestNodePluginModule() {
+    default_node_callbacks.initialized_cb = node_initialized_cb;
+    default_node_callbacks.deinitialized_cb = node_deinitialized_cb;
+    default_node_callbacks.attribute_set_cb = node_attribute_set_cb;
+    default_node_callbacks.activated_cb = node_activated_cb;
+    default_node_callbacks.deactivated_cb = node_deactivated_cb;
+    default_node_callbacks.signal_received_cb = node_signal_received_cb;
+
+    default_port_callbacks.output_format_requested_cb = port_output_format_requested_cb;
+    default_port_callbacks.input_data_received_cb = port_input_data_received_cb;
+}
+
+TestNodePluginModule::~TestNodePluginModule() {
+}
+
+int TestNodePluginModule::node_initialized_cb(mmi_node_instance_h instance) {
+    _D("Node initialize callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::node_deinitialized_cb(mmi_node_instance_h instance) {
+    _D("Node deinitialize callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) {
+    _D("Node attribute set callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::node_activated_cb(mmi_node_instance_h instance) {
+    _D("Node activate callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::node_deactivated_cb(mmi_node_instance_h instance) {
+    _D("Node deactivate callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) {
+    _D("Node signal received callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModule::port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) {
+    _D("Port output format request callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+int TestNodePluginModule::port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) {
+    _D("Port input data callback is called for %p", instance);
+    return MMI_ERROR_NONE;
+}
+
+TestNodePluginModuleMIC::TestNodePluginModuleMIC() {
+    m_node_callbacks = default_node_callbacks;
+    m_node_callbacks.activated_cb = node_activated_cb;
+    m_audio_port_callbacks = default_port_callbacks;
+}
+
+int TestNodePluginModuleMIC::node_activated_cb(mmi_node_instance_h instance) {
+    _D("[MIC] Node activate callback is called for %p", instance);
+
+    /* In shared object plugin module, the output data can be generated using the API
+    mmi_port_instance_output_generate() function, after finding the port using
+    the API mmi_node_instance_port_find. However, in this test plugin module, calling
+    mmi library API is not allowed. So, we have to find the output port using
+    the port instance map. */
+
+    std::string output_port_name{"AUDIO"};
+    mmi_port_instance_h output_port = nullptr;
+    for (const auto &elem : g_node_instance_info_map) {
+        const mmi_plugin_module_node_instance_info_s &node_instance_info = elem.second;
+        if (node_instance_info.node == instance) {
+            for (size_t i = 0; i < node_instance_info.port_info_count; ++i) {
+                if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) {
+                    _D("[MIC] Found output port %p", node_instance_info.port_infos[i].port);
+                    output_port = node_instance_info.port_infos[i].port;
+                    break;
+                }
+            }
+        }
+    }
+
+    auto plugin_module = reinterpret_cast<TestNodePluginModuleMIC*>(instance);
+
+    int value = 100;
+    mmi_data_h output_data = reinterpret_cast<mmi_data_h>(&value);
+
+    PortInstance *port_instance = reinterpret_cast<PortInstance*>(output_port);
+    if (port_instance) {
+        _D("[MIC] Generating an output %d to %p", value, port_instance);
+        port_instance->on_output_data_generated(output_data);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+TestNodePluginModuleASR::TestNodePluginModuleASR() {
+    m_node_callbacks = default_node_callbacks;
+    m_audio_port_callbacks = default_port_callbacks;
+    m_audio_port_callbacks.input_data_received_cb = audio_port_input_data_received_cb;
+}
+
+int TestNodePluginModuleASR::audio_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) {
+    /* In shared object plugin module, the output port can be found using the API
+    mmi_node_instance_sibling_port_find() function. However, in this test
+    plugin module, calling mmi library API is not allowed. So, we have to find the
+    output port using the node instance info map. */
+    std::string output_port_name{"FINAL_RESULT"};
+    mmi_port_instance_h output_port = nullptr;
+    const mmi_plugin_module_node_instance_info_s &node_instance_info = g_node_instance_info_map[instance];
+    for (size_t i = 0; i < node_instance_info.port_info_count; ++i) {
+        if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) {
+            output_port = node_instance_info.port_infos[i].port;
+            break;
+        }
+    }
+
+    int value = -1;
+    int *ptr = reinterpret_cast<int*>(data);
+    if (ptr) value = *ptr;
+
+    _D("[ASR] Audio input data callback is called: %p %d", instance, value);
+
+    value++;
+    ptr = &value;
+
+    mmi_data_h output_data = reinterpret_cast<mmi_data_h>(ptr);
+
+    /* In shared object plugin module, the output data can be generated using the API
+    mmi_port_instance_output_generate() function. However, in this test plugin module,
+    calling mmi library API is not allowed. So, we have to generate the output
+    using the port instance handle directly. */
+
+    PortInstance *port_instance = reinterpret_cast<PortInstance*>(output_port);
+    if (port_instance) {
+        _D("[ASR] Generating an output %d to %p", value, port_instance);
+        port_instance->on_output_data_generated(output_data);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int TestNodePluginModuleMatch::text_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) {
+    /* In shared object plugin module, the output port can be found using the API
+    mmi_node_instance_sibling_port_find() function. However, in this test
+    plugin module, calling mmi library API is not allowed. So, we have to find the
+    output port using the node instance info map. */
+
+    std::string output_port_name{"MATCHED_CANDIDATE"};
+    mmi_port_instance_h output_port = nullptr;
+    const mmi_plugin_module_node_instance_info_s &node_instance_info = g_node_instance_info_map[instance];
+    for (size_t i = 0; i < node_instance_info.port_info_count; ++i) {
+        if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) {
+            output_port = node_instance_info.port_infos[i].port;
+            break;
+        }
+    }
+
+    int value = -1;
+    int *ptr = reinterpret_cast<int*>(data);
+    if (ptr) value = *ptr;
+
+    _D("[Match] Text input data callback is called: %p %d", instance, value);
+
+    value++;
+    ptr = &value;
+
+    mmi_data_h output_data = reinterpret_cast<mmi_data_h>(ptr);
+
+    /* In shared object plugin module, the output data can be generated using the API
+    mmi_port_instance_output_generate() function. However, in this test plugin module,
+    calling mmi library API is not allowed. So, we have to generate the output
+    using the port instance handle directly. */
+
+    PortInstance *port_instance = reinterpret_cast<PortInstance*>(output_port);
+    if (port_instance) {
+        _D("[Match] Generating an output %d to %p", value, port_instance);
+        port_instance->on_output_data_generated(output_data);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+TestNodePluginModuleMatch::TestNodePluginModuleMatch() {
+    m_node_callbacks = default_node_callbacks;
+    m_text_port_callbacks = default_port_callbacks;
+    m_text_port_callbacks.input_data_received_cb = text_port_input_data_received_cb;
+}
+
+PluginModuleProxySelfContainerTest::PluginModuleProxySelfContainerTest() {
+}
+
+PluginModuleProxySelfContainerTest::~PluginModuleProxySelfContainerTest() {
+}
+
+function_pointer PluginModuleProxySelfContainerTest::get_function(const std::string& function_name) {
+    return nullptr;
+}
+
+mmi_plugin_module_node_list_s PluginModuleProxySelfContainerTest::load_node_prototypes() {
+    _D("Loading node prototypes from self-contained test plugin module");
+
+    /* Self-contained port information */
+
+    mmi_port_h mic_audio_port = nullptr;
+    mmi_port_create(&mic_audio_port);
+    mmi_port_set_name(mic_audio_port, "AUDIO");
+    mmi_port_set_type(mic_audio_port, MMI_PORT_TYPE_OUT);
+    mmi_port_set_data_type(mic_audio_port, MMI_DATA_TYPE_AUDIO);
+    mmi_port_set_callbacks(mic_audio_port, m_node_mic.m_audio_port_callbacks);
+
+    mmi_node_h mic_node = nullptr;
+    mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &mic_node);
+    mmi_node_add_port(mic_node, mic_audio_port);
+    mmi_node_set_callbacks(mic_node, m_node_mic.m_node_callbacks);
+    mmi_node_register(mic_node);
+
+    mmi_node_destroy(mic_node);
+
+    mmi_port_h asr_audio_port = nullptr;
+    mmi_port_create(&asr_audio_port);
+    mmi_port_set_name(asr_audio_port, "AUDIO");
+    mmi_port_set_type(asr_audio_port, MMI_PORT_TYPE_IN);
+    mmi_port_set_data_type(asr_audio_port, MMI_DATA_TYPE_AUDIO);
+    mmi_port_set_callbacks(asr_audio_port, m_node_asr.m_audio_port_callbacks);
+
+    mmi_port_h asr_partial_result_port = nullptr;
+    mmi_port_create(&asr_partial_result_port);
+    mmi_port_set_name(asr_partial_result_port, "PARTIAL_RESULT");
+    mmi_port_set_type(asr_partial_result_port, MMI_PORT_TYPE_OUT);
+    mmi_port_set_data_type(asr_partial_result_port, MMI_DATA_TYPE_TEXT);
+    mmi_port_set_callbacks(asr_partial_result_port, m_node_asr.m_audio_port_callbacks);
+
+    mmi_port_h asr_final_result_port = nullptr;
+    mmi_port_create(&asr_final_result_port);
+    mmi_port_set_name(asr_final_result_port, "FINAL_RESULT");
+    mmi_port_set_type(asr_final_result_port, MMI_PORT_TYPE_OUT);
+    mmi_port_set_data_type(asr_final_result_port, MMI_DATA_TYPE_TEXT);
+    mmi_port_set_callbacks(asr_final_result_port, m_node_asr.m_audio_port_callbacks);
+
+    mmi_node_h asr_node = nullptr;
+    mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &asr_node);
+    mmi_node_add_port(asr_node, asr_audio_port);
+    mmi_node_add_port(asr_node, asr_partial_result_port);
+    mmi_node_add_port(asr_node, asr_final_result_port);
+    mmi_node_set_callbacks(asr_node, m_node_asr.m_node_callbacks);
+    mmi_node_register(asr_node);
+
+    mmi_node_destroy(asr_node);
+
+    mmi_port_h match_text_port = nullptr;
+    mmi_port_create(&match_text_port);
+    mmi_port_set_name(match_text_port, "TEXT");
+    mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN);
+    mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT);
+    mmi_port_set_callbacks(match_text_port, m_node_match.m_text_port_callbacks);
+
+    mmi_port_h match_candidate_port = nullptr;
+    mmi_port_create(&match_candidate_port);
+    mmi_port_set_name(match_candidate_port, "MATCHED_CANDIDATE");
+    mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT);
+    mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT);
+    mmi_port_set_callbacks(match_candidate_port, m_node_match.m_text_port_callbacks);
+
+    mmi_node_h match_node = nullptr;
+    mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &match_node);
+    mmi_node_add_port(match_node, match_text_port);
+    mmi_node_add_port(match_node, match_candidate_port);
+    mmi_node_set_callbacks(match_node, m_node_match.m_node_callbacks);
+    mmi_node_register(match_node);
+
+    mmi_node_destroy(match_node);
+
+    const char *identifier = mmi_plugin_storage_get_current_module_identifier();
+    mmi_plugin_module_node_list_s list = mmi_plugin_storage_get_node_list(identifier);
+
+    return list;
+}
+
+mmi_plugin_module_workflow_list_s PluginModuleProxySelfContainerTest::load_workflow_prototypes() {
+    _D("Loading workflow prototypes from self-contained test plugin module");
+
+    mmi_workflow_h workflow = nullptr;
+    mmi_workflow_create(&workflow);
+
+    mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    mmi_node_h node_mic = nullptr;
+    mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic);
+    mmi_workflow_node_add(workflow, "MIC", node_mic);
+
+    mmi_node_h node_asr = nullptr;
+    mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr);
+    mmi_workflow_node_add(workflow, "ASR", node_asr);
+
+    mmi_node_h node_match = nullptr;
+    mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match);
+    mmi_workflow_node_add(workflow, "MATCH", node_match);
+
+    mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO");
+    mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT");
+
+    mmi_workflow_output_assign(workflow, "OUTPUT", "MATCH", "MATCHED_CANDIDATE");
+
+    mmi_standard_workflow_register(workflow);
+
+    mmi_node_destroy(node_match);
+    mmi_node_destroy(node_asr);
+    mmi_node_destroy(node_mic);
+
+    mmi_workflow_destroy(workflow);
+
+    const char *identifier = mmi_plugin_storage_get_current_module_identifier();
+    mmi_plugin_module_workflow_list_s list = mmi_plugin_storage_get_workflow_list(identifier);
+
+    return list;
+}
+
+void PluginModuleProxySelfContainerTest::add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) {
+    for (size_t i = 0; i < node_instance_info->port_info_count; i++) {
+        g_node_instance_info_map[node_instance_info->port_infos[i].port] = *node_instance_info;
+    }
+}
+
+void PluginModuleProxySelfContainerTest::remove_node_instance_info(mmi_node_instance_h node_instance) {
+    for (auto it = g_node_instance_info_map.begin(); it != g_node_instance_info_map.end();) {
+        if (it->second.node == node_instance) {
+            it = g_node_instance_info_map.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-self-container.h b/src/mmi-manager/mmi-self-container.h
new file mode 100644 (file)
index 0000000..6daa1d1
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_SELF_CONTAINER__
+#define __MMI_SELF_CONTAINER__
+
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-manager-log.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+/* This file is for testing purpose only */
+
+namespace mmi {
+
+class PluginModuleProxySelfContainerTest;
+
+struct TestWorkflowPluginModule {
+};
+
+class TestNodePluginModule {
+public:
+    TestNodePluginModule();
+    virtual ~TestNodePluginModule();
+
+    static int node_initialized_cb(mmi_node_instance_h instance);
+    static int node_deinitialized_cb(mmi_node_instance_h instance);
+    static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute);
+    static int node_activated_cb(mmi_node_instance_h instance);
+    static int node_deactivated_cb(mmi_node_instance_h instance);
+    static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal);
+    static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format);
+    static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data);
+
+    mmi_node_callbacks default_node_callbacks;
+    mmi_port_callbacks default_port_callbacks;
+};
+
+class TestWorkflowPluginModuleWakeuplessCommand : TestWorkflowPluginModule {
+
+};
+
+class TestNodePluginModuleMIC : public TestNodePluginModule {
+public:
+    TestNodePluginModuleMIC();
+    static int node_activated_cb(mmi_node_instance_h instance);
+
+    mmi_node_callbacks m_node_callbacks;
+    mmi_port_callbacks m_audio_port_callbacks;
+};
+
+class TestNodePluginModuleASR : public TestNodePluginModule {
+public:
+    TestNodePluginModuleASR();
+
+    static int audio_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data);
+
+    mmi_node_callbacks m_node_callbacks;
+    mmi_port_callbacks m_audio_port_callbacks;
+};
+
+class TestNodePluginModuleMatch : public TestNodePluginModule {
+public:
+    TestNodePluginModuleMatch();
+
+    static int text_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data);
+
+    mmi_node_callbacks m_node_callbacks;
+    mmi_port_callbacks m_text_port_callbacks;
+};
+
+class PluginModuleProxySelfContainerTest : public IPluginModuleProxy {
+public:
+    PluginModuleProxySelfContainerTest();
+    virtual ~PluginModuleProxySelfContainerTest();
+
+    virtual function_pointer get_function(const std::string& function_name) override;
+
+    virtual mmi_plugin_module_node_list_s load_node_prototypes() override;
+    virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() override;
+
+    virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) override;
+    virtual void remove_node_instance_info(mmi_node_instance_h node_instance) override;
+
+private:
+    TestWorkflowPluginModuleWakeuplessCommand m_workflow_wakeupless_command;
+    TestNodePluginModuleMIC m_node_mic;
+    TestNodePluginModuleASR m_node_asr;
+    TestNodePluginModuleMatch m_node_match;
+};
+
+class PluginModuleProxyFactorySelfContainerTest : public IPluginModuleProxyFactory {
+public:
+    virtual std::vector<PluginModuleInfo> get_plugin_module_list() override {
+        std::vector<PluginModuleInfo> plugin_module_list;
+        PluginModuleInfo item{mmi_plugin_module_type_e::SELF_CONTAINED, "self-container-test"};
+        plugin_module_list.push_back(item);
+        return plugin_module_list;
+    }
+    virtual std::shared_ptr<IPluginModuleProxy> create(
+        PluginModuleInfo plugin_module_info) override {
+        return std::make_shared<PluginModuleProxySelfContainerTest>();
+    }
+};
+
+} // namespace mmi
+
+#endif //__MMI_SELF_CONTAINER__
+
+
diff --git a/src/mmi-manager/mmi-workflow-instance-manager.cpp b/src/mmi-manager/mmi-workflow-instance-manager.cpp
new file mode 100644 (file)
index 0000000..bfbd1fe
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-workflow-instance-manager.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+WorkflowInstanceManager::WorkflowInstanceManager() {
+}
+
+WorkflowInstanceManager::~WorkflowInstanceManager() {
+    deinitialize();
+}
+
+void WorkflowInstanceManager::initialize() {
+}
+
+void WorkflowInstanceManager::deinitialize() {
+    m_workflow_instances.clear();
+
+    m_node_instance_manager.reset();
+    m_prototype_store.reset();
+}
+
+void WorkflowInstanceManager::set_workflow_prototype_store(
+    std::shared_ptr<IWorkflowPrototypeStore> prototype_store) {
+    m_prototype_store = prototype_store;
+}
+
+void WorkflowInstanceManager::set_plugin_module_proxy_provider(
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider) {
+    m_plugin_module_proxy_provider = plugin_module_proxy_provider;
+}
+
+void WorkflowInstanceManager::set_node_instance_manager(
+    std::shared_ptr<INodeInstanceManager> node_instance_manager) {
+    m_node_instance_manager = node_instance_manager;
+}
+
+std::shared_ptr<WorkflowInstance> WorkflowInstanceManager::get_workflow_instance(
+    std::string client_id, int local_workflow_instance_id) {
+    for (auto &instance : m_workflow_instances) {
+        if (instance->get_client_id() == client_id &&
+                instance->get_local_workflow_instance_id() == local_workflow_instance_id) {
+            return instance;
+        }
+    }
+
+    return nullptr;
+}
+
+void WorkflowInstanceManager::on_observer_event(
+    const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) {
+    try {
+        if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) {
+            std::string client_id = std::any_cast<std::string>(data);
+            handle_client_connected(client_id);
+        } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) {
+            std::string client_id = std::any_cast<std::string>(data);
+            handle_client_disconnected(client_id);
+        } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED) {
+            Message *message = std::any_cast<Message*>(data);
+            if (message->message_group == MESSAGE_GROUP::CLIENT) {
+                handle_client_message(static_cast<ClientMessage*>(message));
+            }
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+void WorkflowInstanceManager::handle_client_message(ClientMessage *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    switch(message->message_type) {
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE: {
+        auto *msg = static_cast<ClientMessageWorkflowInstanceCreate*>(message);
+        handle_create(msg);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY: {
+        auto *msg = static_cast<ClientMessageWorkflowInstanceDestroy*>(message);
+        handle_destroy(msg);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE: {
+        auto *msg = static_cast<ClientMessageWorkflowInstanceSetAttribute*>(message);
+        handle_set_attribute(msg);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE: {
+        auto *msg = static_cast<ClientMessageWorkflowInstanceActivate*>(message);
+        handle_activate(msg);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE: {
+        auto *msg = static_cast<ClientMessageWorkflowInstanceDeactivate*>(message);
+        handle_deactivate(msg);
+    }
+    break;
+    }
+}
+
+void WorkflowInstanceManager::handle_client_connected(std::string client_id) {
+    LOGD("%s connected", client_id.c_str());
+}
+
+void WorkflowInstanceManager::handle_client_disconnected(std::string client_id) {
+    LOGD("%s disconnected", client_id.c_str());
+
+    auto it = m_workflow_instances.begin();
+    while (it != m_workflow_instances.end()) {
+        if ((*it)->get_client_id() == client_id) {
+            (*it)->deinitialize();
+            it = m_workflow_instances.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+
+void WorkflowInstanceManager::handle_create(
+    ClientMessageWorkflowInstanceCreate *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    std::shared_ptr<WorkflowInstance> instance =
+        get_workflow_instance(message->sender, message->local_workflow_instance_id);
+    if (instance) {
+        LOGE("Workflow instance already exists");
+        return;
+    }
+
+    if (nullptr == m_prototype_store) {
+        LOGE("Workflow prototype store is not set");
+        return;
+    }
+
+    if (nullptr == m_node_instance_manager) {
+        LOGE("Node instance manager is not set");
+        return;
+    }
+
+    instance = std::make_shared<WorkflowInstance>();
+    if (instance) {
+        instance->set_client_id(message->sender);
+        instance->set_local_workflow_instance_id(message->local_workflow_instance_id);
+        instance->set_type(message->workflow_type);
+
+        instance->set_workflow_prototype_store(m_prototype_store);
+        instance->set_plugin_module_proxy_provider(m_plugin_module_proxy_provider);
+        instance->set_node_instance_manager(m_node_instance_manager);
+
+        instance->initialize();
+
+        m_workflow_instances.push_back(instance);
+
+        WorkflowEventWorkflowCreated event;
+        event.workflow_instance = instance;
+        notify_observers(WORKFLOW_EVENT_TYPE::WORKFLOW_CREATED, &event);
+    } else {
+        LOGE("Failed to create workflow instance");
+    }
+}
+
+void WorkflowInstanceManager::handle_destroy(
+    ClientMessageWorkflowInstanceDestroy *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    std::shared_ptr<WorkflowInstance> instance =
+        get_workflow_instance(message->sender, message->local_workflow_instance_id);
+    if (instance) {
+        instance->deinitialize();
+    }
+
+    auto it = m_workflow_instances.begin();
+    while (it != m_workflow_instances.end()) {
+        if ((*it)->get_client_id() == message->sender &&
+                (*it)->get_local_workflow_instance_id() == message->local_workflow_instance_id) {
+            it = m_workflow_instances.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+void WorkflowInstanceManager::handle_set_attribute(
+    ClientMessageWorkflowInstanceSetAttribute *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    std::shared_ptr<WorkflowInstance> instance =
+        get_workflow_instance(message->sender, message->local_workflow_instance_id);
+    if (instance) {
+        instance->set_attribute(message->attribute);
+    }
+}
+
+void WorkflowInstanceManager::handle_activate(
+    ClientMessageWorkflowInstanceActivate *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    std::shared_ptr<WorkflowInstance> instance =
+        get_workflow_instance(message->sender, message->local_workflow_instance_id);
+    if (instance) {
+        instance->activate();
+    }
+}
+
+void WorkflowInstanceManager::handle_deactivate(
+    ClientMessageWorkflowInstanceDeactivate *message) {
+    if (nullptr == message) {
+        LOGE("Message is null");
+        return;
+    }
+
+    std::shared_ptr<WorkflowInstance> instance =
+        get_workflow_instance(message->sender, message->local_workflow_instance_id);
+    if (instance) {
+        instance->deactivate();
+    }
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-workflow-instance-manager.h b/src/mmi-manager/mmi-workflow-instance-manager.h
new file mode 100644 (file)
index 0000000..5e32912
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_WORKFLOW_INSTANCE_MANAGER_H__
+#define __MMI_WORKFLOW_INSTANCE_MANAGER_H__
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "mmi-communication-channel.h"
+#include "mmi-event-observer.h"
+#include "mmi-node-instance-manager.h"
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-workflow-instance.h"
+#include "mmi-workflow-prototype-manager.h"
+#include "mmi-workflow-event.h"
+
+namespace mmi {
+
+using namespace communication;
+
+class WorkflowInstanceManager :
+    public SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE>,
+    public SimpleEventObservable<WORKFLOW_EVENT_TYPE> {
+public:
+    WorkflowInstanceManager();
+    virtual ~WorkflowInstanceManager();
+
+    void initialize();
+    void deinitialize();
+
+    void set_workflow_prototype_store(
+        std::shared_ptr<IWorkflowPrototypeStore> prototype_store);
+    void set_plugin_module_proxy_provider(
+        std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider);
+    void set_node_instance_manager(
+        std::shared_ptr<INodeInstanceManager> node_instance_manager);
+
+    std::shared_ptr<WorkflowInstance> get_workflow_instance(
+        std::string client_id, int local_workflow_instance_id);
+
+    void on_observer_event(
+        const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override;
+
+private:
+    void handle_client_message(ClientMessage *message);
+
+    void handle_client_connected(std::string client_id);
+    void handle_client_disconnected(std::string client_id);
+
+    void handle_create(ClientMessageWorkflowInstanceCreate *message);
+    void handle_destroy(ClientMessageWorkflowInstanceDestroy *message);
+    void handle_set_attribute(ClientMessageWorkflowInstanceSetAttribute *message);
+    void handle_activate(ClientMessageWorkflowInstanceActivate *message);
+    void handle_deactivate(ClientMessageWorkflowInstanceDeactivate *message);
+protected:
+    std::shared_ptr<IWorkflowPrototypeStore> m_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> m_plugin_module_proxy_provider;
+    std::shared_ptr<INodeInstanceManager> m_node_instance_manager;
+
+    std::vector<std::shared_ptr<WorkflowInstance>> m_workflow_instances;
+};
+
+} // namespace mmi
+
+#endif //__MMI_WORKFLOW_INSTANCE_MANAGER_H__
+
diff --git a/src/mmi-manager/mmi-workflow-instance.cpp b/src/mmi-manager/mmi-workflow-instance.cpp
new file mode 100644 (file)
index 0000000..e26b78f
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-workflow-instance.h"
+#include "mmi-manager-log.h"
+#include "mmi-workflow-output-event.h"
+
+#include <bundle.h>
+namespace mmi {
+
+WorkflowInstance::WorkflowInstance() {
+}
+
+WorkflowInstance::~WorkflowInstance() {
+}
+
+void WorkflowInstance::set_client_id(std::string client_id) {
+    m_client_id = client_id;
+}
+
+std::string WorkflowInstance::get_client_id() {
+    return m_client_id;
+}
+
+void WorkflowInstance::set_local_workflow_instance_id(int local_workflow_instance_id) {
+    m_local_workflow_instance_id = local_workflow_instance_id;
+}
+
+int WorkflowInstance::get_local_workflow_instance_id() {
+    return m_local_workflow_instance_id;
+}
+
+void WorkflowInstance::set_type(mmi_standard_workflow_type_e type) {
+    m_type = type;
+}
+
+mmi_standard_workflow_type_e WorkflowInstance::get_type() {
+    return m_type;
+}
+
+void WorkflowInstance::set_workflow_prototype_store(
+    std::shared_ptr<IWorkflowPrototypeStore> prototype_store) {
+    m_prototype_store = prototype_store;
+}
+
+void WorkflowInstance::set_plugin_module_proxy_provider(
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider) {
+    m_plugin_module_proxy_provider = plugin_module_proxy_provider;
+}
+
+void WorkflowInstance::set_node_instance_manager(
+    std::shared_ptr<INodeInstanceManager> node_instance_manager) {
+    m_node_instance_manager = node_instance_manager;
+}
+
+
+void WorkflowInstance::initialize() {
+    if (WORKFLOW_INSTANCE_STATE::NONE != m_state) {
+        _E("Workflow instance state is not NONE");
+        return;
+    }
+
+    if (nullptr == m_prototype_store) {
+        _E("Workflow prototype store is not set");
+        return;
+    }
+
+    if (nullptr == m_node_instance_manager) {
+        _E("Node instance manager is not set");
+        return;
+    }
+
+    if (nullptr == m_plugin_module_proxy_provider) {
+        _E("Plugin module proxy provider is not set");
+        return;
+    }
+
+    std::shared_ptr<WorkflowPrototype> prototype = m_prototype_store->get_workflow_prototype(m_type);
+    if (nullptr == prototype) {
+        _E("Could not find prototype information from store");
+        return;
+    }
+
+    /* Owning this shared pointer guarantees that the callback functions are
+     * valid until the node instance is destroyed. */
+    m_plugin_module_proxy = m_plugin_module_proxy_provider->get_plugin_module_proxy(
+                                prototype->get_plugin_module_info());
+
+    /* Create node instances based on prototype information */
+    std::vector<NodeInfo> node_infos = prototype->get_node_infos();
+    for (auto &node_info : node_infos) {
+        std::shared_ptr<NodeInstance> node_instance =
+            m_node_instance_manager->create_node_instance(
+                node_info.node_type, node_info.node_sub_type);
+        if (nullptr == node_instance) {
+            _E("Failed to create node instance : %s", node_info.name.c_str());
+        } else {
+            m_node_instances[node_info.name] = node_instance;
+            _D("Created node instance : %s(%p)",
+               node_info.name.c_str(), node_instance.get());
+            /* Listen to plugin module events */
+            std::shared_ptr<IPluginModuleProxy> proxy = node_instance->get_plugin_module_proxy();
+            if (proxy) {
+                proxy->add_observer(this);
+            }
+        }
+    }
+
+    std::vector<LinkInfo> link_infos = prototype->get_link_infos();
+    for (auto &link_info : link_infos) {
+        try {
+            std::shared_ptr<NodeInstance> from_node_instance =
+                m_node_instances.at(link_info.from_node_name);
+            std::shared_ptr<NodeInstance> to_node_instance =
+                m_node_instances.at(link_info.to_node_name);
+
+            _D("Linking two nodes : %s(%p) -> %s(%p)",
+               link_info.from_node_name.c_str(), from_node_instance.get(),
+               link_info.to_node_name.c_str(), to_node_instance.get());
+            m_node_instance_manager->link_node_instances(
+                from_node_instance, to_node_instance,
+                link_info.from_port_name, link_info.to_port_name);
+        } catch (std::out_of_range& e) {
+            _E("Failed to find node instance : %s -> %s",
+               link_info.from_node_name.c_str(), link_info.to_node_name.c_str());
+        }
+    }
+
+    std::vector<OutputAssignmentInfo> output_assignment_infos =
+        prototype->get_output_assignment_infos();
+
+    for (auto &output_assignment_info : output_assignment_infos) {
+        try {
+            std::shared_ptr<NodeInstance> from_node_instance =
+                m_node_instances.at(output_assignment_info.from_node_name);
+
+            std::shared_ptr<DataGateway> output_data_gateway =
+                std::make_shared<DataGateway>(output_assignment_info.output_name);
+
+
+            _D("Adding Gateway : node(%s), port(%s) -> output name(%s)",
+               output_assignment_info.from_node_name.c_str(),
+               output_assignment_info.from_port_name.c_str(),
+               output_assignment_info.output_name.c_str());
+
+            mmi_data_transfer_callback callback = [this](mmi_data_h data, std::string name) {
+                _D("Data received from Data Gateway");
+                unsigned char *data_bytes = nullptr;
+                size_t data_byte_size = 0;
+                mmi_data_to_bytes(data, &data_bytes, &data_byte_size);
+
+                bundle *encoded = bundle_create();
+                bundle_add_byte(encoded, "data", data_bytes, data_byte_size);
+
+                WorkflowOutputEventOutputGenerated event;
+                event.source_name = name;
+                event.data = encoded;
+                event.client_id = get_client_id();
+                event.local_workflow_instance_id = get_local_workflow_instance_id();
+                this->notify_observers(WORKFLOW_OUTPUT_EVENT_TYPE::OUTPUT_GENERATED, &event);
+            };
+            output_data_gateway->set_callback(callback);
+
+            m_data_gateways.push_back(output_data_gateway);
+
+            from_node_instance->add_data_gateway(
+                output_assignment_info.from_port_name,
+                output_data_gateway);
+        } catch (std::out_of_range& e) {
+            _E("Failed to find node instance : %s",
+               output_assignment_info.from_node_name.c_str());
+        }
+    }
+
+    for (auto &node_instance : m_node_instances) {
+        _D("Node instance in m_node_instances : %s(%p)",
+           node_instance.first.c_str(), node_instance.second.get());
+    }
+
+    set_attribute_default_values();
+
+    m_state = WORKFLOW_INSTANCE_STATE::INITIALIZED;
+}
+
+void WorkflowInstance::deinitialize() {
+    /* If the workflow instance is running, stop it first */
+    if (WORKFLOW_INSTANCE_STATE::ACTIVATED == m_state) {
+        deactivate();
+    }
+    if (WORKFLOW_INSTANCE_STATE::INITIALIZED != m_state) {
+        _E("Workflow instance state is not INITIALIZED");
+        return;
+    }
+
+    for (auto &node_instance : m_node_instances) {
+        std::shared_ptr<IPluginModuleProxy> proxy = (node_instance.second)->get_plugin_module_proxy();
+        if (proxy) {
+            proxy->remove_observer(this);
+        }
+        m_node_instance_manager->destroy_node_instance(node_instance.second);
+    }
+    m_node_instances.clear();
+}
+
+void WorkflowInstance::activate() {
+    if (WORKFLOW_INSTANCE_STATE::INITIALIZED != m_state) {
+        _E("Workflow instance state is not INITIALIZED");
+        return;
+    }
+
+    _D("Activating workflow instance");
+
+    for (auto &node_instance : m_node_instances) {
+        _D("Activating node instance : %s(%p)",
+           node_instance.first.c_str(), node_instance.second.get());
+        (node_instance.second)->activate();
+    }
+
+    m_state = WORKFLOW_INSTANCE_STATE::ACTIVATED;
+}
+
+void WorkflowInstance::deactivate() {
+    if (WORKFLOW_INSTANCE_STATE::ACTIVATED != m_state) {
+        _E("Workflow instance state is not ACTIVATED");
+        return;
+    }
+
+    _D("Deactivating workflow instance");
+
+    for (auto &node_instance : m_node_instances) {
+        (node_instance.second)->deactivate();
+    }
+
+    m_state = WORKFLOW_INSTANCE_STATE::INITIALIZED;
+}
+
+bool WorkflowInstance::set_attribute(mmi_attribute_h attribute) {
+    _D("Setting attribute : %p", attribute);
+
+    std::shared_ptr<WorkflowPrototype> prototype = m_prototype_store->get_workflow_prototype(m_type);
+    if (nullptr == prototype) {
+        _E("Could not find prototype information from store");
+        return false;
+    }
+
+    char *name = nullptr;
+    if (MMI_ERROR_NONE == mmi_attribute_get_name(attribute, &name)) {
+        std::vector<AttributeAssignmentInfo> attribute_assignment_infos =
+            prototype->get_attribute_assignment_infos();
+
+        if (!name) {
+            _E("Attribute name is null");
+            return false;
+        }
+        auto it = std::find_if(attribute_assignment_infos.begin(), attribute_assignment_infos.end(),
+            [name](const AttributeAssignmentInfo& info) {
+            return (info.attribute_name.compare(name) == 0);
+        });
+        if (it != attribute_assignment_infos.end()) {
+            _D("Found attribute assignment info : %s", name);
+            try {
+                std::shared_ptr<NodeInstance> target_node_instance =
+                    m_node_instances.at(it->target_node_name);
+                if (target_node_instance) {
+                    mmi_attribute_set_name(attribute, it->target_attribute_name.c_str());
+                    target_node_instance->set_attribute(attribute);
+                } else {
+                    _E("Could not find target node instance : %s", it->target_node_name.c_str());
+                }
+            } catch (std::out_of_range& e) {
+                _E("Failed to find node instance : %s", it->target_node_name.c_str());
+            }
+        } else {
+            _E("Could not find attribute assignment info : %s", name);
+        }
+        free(name);
+        name = nullptr;
+    }
+
+    return true;
+}
+
+void WorkflowInstance::on_observer_event(
+    const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) {
+    try {
+        if (event == PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED) {
+            PluginModuleEventSwitchEventEmitted *switch_event = std::any_cast<PluginModuleEventSwitchEventEmitted*>(data);
+            if (switch_event != nullptr) {
+                bool found = false;
+                for (auto &node_instance : m_node_instances) {
+                    if (switch_event->node_instance == node_instance.second.get()) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    /* This event is not for this workflow instance */
+                    return;
+                }
+                _D("Switch Event emitted for %s %d", switch_event->controllee.c_str(), switch_event->state);
+                std::shared_ptr<NodeInstance> node_instance = m_node_instances.at(switch_event->controllee);
+                if (node_instance) {
+                    if (switch_event->state) {
+                        node_instance->resume();
+                    } else {
+                        node_instance->suspend();
+                    }
+                } else {
+                    _E("Could not find node instance : %s", switch_event->controllee.c_str());
+                }
+            }
+        }
+    } catch (const std::bad_any_cast &e) {
+        LOGE("Failed to cast message data: %s", e.what());
+    }
+}
+
+bool WorkflowInstance::set_attribute_default_values() {
+    _D("Setting attribute default value");
+
+    std::shared_ptr<WorkflowPrototype> prototype = m_prototype_store->get_workflow_prototype(m_type);
+    if (nullptr == prototype) {
+        _E("Could not find prototype information from store");
+        return false;
+    }
+
+    std::vector<AttributeDefaultValueInfo> attribute_default_value_infos =
+        prototype->get_attribute_default_value_infos();
+
+    for (auto &default_value_info : attribute_default_value_infos) {
+        set_attribute(default_value_info.default_value);
+    }
+
+    return true;
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-workflow-instance.h b/src/mmi-manager/mmi-workflow-instance.h
new file mode 100644 (file)
index 0000000..42ef79c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_WORKFLOW_INSTANCE_H__
+#define __MMI_WORKFLOW_INSTANCE_H__
+
+#include <memory>
+#include <map>
+#include <string>
+
+#include "mmi-plugin-module-event.h"
+#include "mmi-plugin-module-proxy.h"
+#include "mmi-manager-log.h"
+#include "mmi-node-instance-manager.h"
+#include "mmi-workflow-prototype-manager.h"
+#include "mmi-workflow-output-event.h"
+namespace mmi {
+
+enum class WORKFLOW_INSTANCE_STATE {
+    NONE,
+    INITIALIZED,
+    ACTIVATED,
+};
+
+class WorkflowInstance :
+    public SimpleEventObserver<PLUGIN_MODULE_EVENT_TYPE>,
+    public SimpleEventObservable<WORKFLOW_OUTPUT_EVENT_TYPE> {
+public:
+    WorkflowInstance();
+    virtual ~WorkflowInstance();
+
+    void set_client_id(std::string client_id);
+    std::string get_client_id();
+
+    void set_local_workflow_instance_id(int local_workflow_instance_id);
+    int get_local_workflow_instance_id();
+
+    void set_type(mmi_standard_workflow_type_e type);
+    mmi_standard_workflow_type_e get_type();
+
+    void set_workflow_prototype_store(
+        std::shared_ptr<IWorkflowPrototypeStore> prototype_store);
+    void set_plugin_module_proxy_provider(
+        std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider);
+    void set_node_instance_manager(
+        std::shared_ptr<INodeInstanceManager> node_instance_manager);
+
+    void initialize();
+    void deinitialize();
+
+    void activate();
+    void deactivate();
+
+    bool set_attribute(mmi_attribute_h attribute);
+    bool set_attribute_default_values();
+
+    void on_observer_event(
+        const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) override;
+private:
+    std::shared_ptr<IWorkflowPrototypeStore> m_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> m_plugin_module_proxy_provider;
+    std::shared_ptr<INodeInstanceManager> m_node_instance_manager;
+
+    std::shared_ptr<IPluginModuleProxy> m_plugin_module_proxy;
+
+    std::string m_client_id;
+    int m_local_workflow_instance_id{0};
+
+    mmi_standard_workflow_type_e m_type{MMI_STANDARD_WORKFLOW_NONE};
+    WORKFLOW_INSTANCE_STATE m_state{WORKFLOW_INSTANCE_STATE::NONE};
+
+    std::map<std::string, std::shared_ptr<NodeInstance>> m_node_instances;
+
+    std::vector<std::shared_ptr<DataGateway>> m_data_gateways;
+};
+
+} // namespace mmi
+
+#endif //__MMI_WORKFLOW_INSTANCE_H__
+
diff --git a/src/mmi-manager/mmi-workflow-prototype-manager.cpp b/src/mmi-manager/mmi-workflow-prototype-manager.cpp
new file mode 100644 (file)
index 0000000..5aa3977
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-workflow-prototype-manager.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+WorkflowPrototypeManager::WorkflowPrototypeManager() {
+}
+
+WorkflowPrototypeManager::~WorkflowPrototypeManager() {
+}
+
+bool WorkflowPrototypeManager::add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) {
+    if (!prototype->is_valid()) {
+        _E("ERROR: invalid workflow prototype");
+        return false;
+    }
+
+    /* check if we already have a prototype with the same type */
+    for (auto& elem : m_prototypes) {
+        if (elem->get_type() == prototype->get_type()) {
+            _E("ERROR: prototype with the same type already exists : %d", prototype->get_type());
+            return false;
+        }
+    }
+
+    m_prototypes.push_back(prototype);
+
+    return true;
+}
+
+std::shared_ptr<WorkflowPrototype> WorkflowPrototypeManager::get_workflow_prototype(
+    mmi_standard_workflow_type_e type) {
+    std::shared_ptr<WorkflowPrototype> ret;
+
+    for (auto& elem : m_prototypes) {
+        if (elem->get_type() == type) {
+            ret = elem;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+void WorkflowPrototypeManager::initialize() {
+}
+
+void WorkflowPrototypeManager::deinitialize() {
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-workflow-prototype-manager.h b/src/mmi-manager/mmi-workflow-prototype-manager.h
new file mode 100644 (file)
index 0000000..270ed4c
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_WORKFLOW_PROTOTYPE_MANAGER_H__
+#define __MMI_WORKFLOW_PROTOTYPE_MANAGER_H__
+
+#include "mmi-workflow.h"
+#include "mmi-workflow-prototype.h"
+
+#include <memory>
+
+namespace mmi {
+
+struct IWorkflowPrototypeStore {
+    virtual bool add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) = 0;
+    virtual std::shared_ptr<WorkflowPrototype> get_workflow_prototype(mmi_standard_workflow_type_e type) = 0;
+};
+
+class WorkflowPrototypeManager : public IWorkflowPrototypeStore {
+public:
+    WorkflowPrototypeManager();
+    virtual ~WorkflowPrototypeManager();
+
+    virtual bool add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) override;
+    virtual std::shared_ptr<WorkflowPrototype> get_workflow_prototype(mmi_standard_workflow_type_e type) override;
+
+    void initialize();
+    void deinitialize();
+
+protected:
+    std::vector<std::shared_ptr<WorkflowPrototype>> m_prototypes;
+};
+
+} // namespace mmi
+
+#endif //__MMI_WORKFLOW_PROTOTYPE_MANAGER_H__
+
+
diff --git a/src/mmi-manager/mmi-workflow-prototype.cpp b/src/mmi-manager/mmi-workflow-prototype.cpp
new file mode 100644 (file)
index 0000000..ca66168
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mmi-workflow-prototype.h"
+
+#include "mmi-manager-log.h"
+
+namespace mmi {
+
+WorkflowPrototype::WorkflowPrototype() {
+}
+
+WorkflowPrototype::~WorkflowPrototype() {
+    for (auto& attribute_default_value_info : m_attribute_default_value_infos) {
+        mmi_attribute_destroy(attribute_default_value_info.default_value);
+    }
+}
+
+bool WorkflowPrototype::is_valid() {
+    if (!m_type.has_value()) {
+        _E("ERROR: type is not set");
+        return false;
+    }
+
+    if (m_plugin_module_info.plugin_module_identifier.empty()) {
+        _E("ERROR: plugin module identifier is not set");
+        return false;
+    }
+
+    return true;
+}
+
+void WorkflowPrototype::set_type(mmi_standard_workflow_type_e type) {
+    m_type = type;
+}
+
+bool WorkflowPrototype::add_node_info(NodeInfo node_info) {
+    m_node_infos.push_back(node_info);
+    return true;
+}
+
+bool WorkflowPrototype::add_link_info(LinkInfo link_info) {
+    m_link_infos.push_back(link_info);
+    return true;
+}
+
+bool WorkflowPrototype::add_attribute_assignment_info(AttributeAssignmentInfo attribute_assignment_info) {
+    m_attribute_assignment_infos.push_back(attribute_assignment_info);
+    return true;
+}
+
+bool WorkflowPrototype::add_attribute_default_value_info(AttributeDefaultValueInfo attribute_default_value_info) {
+    m_attribute_default_value_infos.push_back(attribute_default_value_info);
+    return true;
+}
+
+bool WorkflowPrototype::add_output_assignment_info(OutputAssignmentInfo output_assignment_info) {
+    m_output_assignment_infos.push_back(output_assignment_info);
+    return true;
+}
+
+mmi_standard_workflow_type_e WorkflowPrototype::get_type() {
+    return m_type.value();
+}
+
+std::vector<NodeInfo> WorkflowPrototype::get_node_infos() {
+    return m_node_infos;
+}
+
+std::vector<LinkInfo> WorkflowPrototype::get_link_infos() {
+    return m_link_infos;
+}
+
+std::vector<AttributeAssignmentInfo> WorkflowPrototype::get_attribute_assignment_infos() {
+    return m_attribute_assignment_infos;
+}
+
+std::vector<AttributeDefaultValueInfo> WorkflowPrototype::get_attribute_default_value_infos() {
+    return m_attribute_default_value_infos;
+}
+
+std::vector<OutputAssignmentInfo> WorkflowPrototype::get_output_assignment_infos() {
+    return m_output_assignment_infos;
+}
+
+} // namespace mmi
+
diff --git a/src/mmi-manager/mmi-workflow-prototype.h b/src/mmi-manager/mmi-workflow-prototype.h
new file mode 100644 (file)
index 0000000..85058b1
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_WORKFLOW_PROTOTYPE_H__
+#define __MMI_WORKFLOW_PROTOTYPE_H__
+
+#include "mmi-workflow.h"
+#include "mmi-node-prototype.h"
+
+#include <memory>
+
+namespace mmi {
+
+typedef struct {
+    std::string name;
+    mmi_standard_node_type_e node_type;
+    mmi_standard_node_sub_type_e node_sub_type;
+} NodeInfo;
+
+typedef struct {
+    std::string from_node_name;
+    std::string from_port_name;
+    std::string to_node_name;
+    std::string to_port_name;
+} LinkInfo;
+
+typedef struct {
+    std::string attribute_name;
+    std::string target_node_name;
+    std::string target_attribute_name;
+} AttributeAssignmentInfo;
+
+typedef struct {
+    mmi_attribute_h default_value;
+} AttributeDefaultValueInfo;
+
+typedef struct {
+    std::string output_name;
+    std::string from_node_name;
+    std::string from_port_name;
+} OutputAssignmentInfo;
+
+class WorkflowPrototype {
+public:
+    WorkflowPrototype();
+    virtual ~WorkflowPrototype();
+
+    bool is_valid();
+
+    void set_plugin_module_info(PluginModuleInfo plugin_module_info) {
+        m_plugin_module_info = plugin_module_info;
+    }
+    PluginModuleInfo get_plugin_module_info() {
+        return m_plugin_module_info;
+    }
+
+    void set_type(mmi_standard_workflow_type_e type);
+
+    bool add_node_info(NodeInfo node_info);
+    bool add_link_info(LinkInfo link_info);
+    bool add_attribute_assignment_info(AttributeAssignmentInfo attribute_assignment_info);
+    bool add_attribute_default_value_info(AttributeDefaultValueInfo attribute_default_value_info);
+    bool add_output_assignment_info(OutputAssignmentInfo output_assignment_info);
+
+    mmi_standard_workflow_type_e get_type();
+
+    std::vector<NodeInfo> get_node_infos();
+    std::vector<LinkInfo> get_link_infos();
+    std::vector<AttributeAssignmentInfo> get_attribute_assignment_infos();
+    std::vector<AttributeDefaultValueInfo> get_attribute_default_value_infos();
+    std::vector<OutputAssignmentInfo> get_output_assignment_infos();
+private:
+    PluginModuleInfo m_plugin_module_info;
+
+    std::optional<mmi_standard_workflow_type_e> m_type;
+
+    std::vector<NodeInfo> m_node_infos;
+    std::vector<LinkInfo> m_link_infos;
+    std::vector<AttributeAssignmentInfo> m_attribute_assignment_infos;
+    std::vector<AttributeDefaultValueInfo> m_attribute_default_value_infos;
+    std::vector<OutputAssignmentInfo> m_output_assignment_infos;
+};
+
+} // namespace mmi
+
+#endif //__MMI_WORKFLOW_PROTOTYPE_H__
+
+
diff --git a/src/mmi.c b/src/mmi.c
deleted file mode 100644 (file)
index 9824c00..0000000
--- a/src/mmi.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "mmi.h"
-#include "mmi-client.h"
-#include "mmi-dbg.h"
-#include "mmi-ipc.h"
-
-MMI_API int mmi_initialize(void)
-{
-       int ret = mmi_client_create();
-       if(ret != MMI_ERROR_NONE) {
-               LOGE("Fail to create mmi client(%d)", ret);
-               return MMI_ERROR_NOT_SUPPORTED;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-MMI_API int mmi_deinitialize(void)
-{
-       int ret = mmi_client_destroy();
-       if(ret != MMI_ERROR_NONE) {
-               LOGE("Fail to destroy mmi client(%d)", ret);
-               return MMI_ERROR_NOT_SUPPORTED;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-MMI_API int mmi_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data)
-{
-       LOGI("Set result cb about input event type(%d)", input_event_type);
-
-       int ret = mmi_client_set_result_cb(input_event_type, callback, user_data);
-       if(ret != MMI_ERROR_NONE) {
-               LOGE("Fail to set result cb(%d)", ret);
-               return ret;
-       }
-
-       return MMI_ERROR_NONE;
-}
-
-MMI_API int mmi_activate_input_event(int input_event_type)
-{
-       LOGI("Activate input event(%d)", input_event_type);
-
-       int ret;
-       GList* iter = NULL;
-       mmi_result_cb_s *data = NULL;
-       mmi_handle client = mmi_client_get();
-
-       if (g_list_length(client->result_cb_list) > 0) {
-               LOGD("Check length of callback lists = %d", g_list_length(client->result_cb_list));
-
-               iter = g_list_first(client->result_cb_list);
-
-               while (iter != NULL) {
-                       data = iter->data;
-
-                       if (data != NULL) {
-                               int type = data->input_event_type;
-
-                               if (type == input_event_type) {
-                                       ret = mmi_ipc_activate_input_event(input_event_type);
-                                       if(ret != MMI_ERROR_NONE) {
-                                               LOGE("Fail to activate input event(%d)", ret);
-                                               return ret;
-                                       }
-
-                                       LOGD("Activate input event(%d)", type);
-                                       return MMI_ERROR_NONE;
-                               }
-                       }
-
-                       iter = g_list_next(iter);
-               }
-       }
-
-       LOGE("No match callback about input event type(%d)", input_event_type);
-       return MMI_ERROR_INVALID_PARAMETER;
-}
-
-MMI_API int mmi_deactivate_input_event(int input_event_type)
-{
-       LOGI("Deactivate input event(%d)", input_event_type);
-
-       int ret;
-       GList* iter = NULL;
-       mmi_result_cb_s *data = NULL;
-       mmi_handle client = mmi_client_get();
-
-       if (g_list_length(client->result_cb_list) > 0) {
-               LOGD("Check length of callback lists = %d", g_list_length(client->result_cb_list));
-
-               iter = g_list_first(client->result_cb_list);
-
-               while (NULL != iter) {
-                       data = iter->data;
-
-                       if (NULL != data) {
-                               int type = data->input_event_type;
-
-                               if (type == input_event_type) {
-                                       ret = mmi_ipc_deactivate_input_event(input_event_type);
-                                       if(ret != MMI_ERROR_NONE) {
-                                               LOGE("Fail to deactivate input event(%d)", ret);
-                                               return ret;
-                                       }
-
-                                       return MMI_ERROR_NONE;
-                               }
-                       }
-
-                       iter = g_list_next(iter);
-               }
-       }
-
-       LOGE("Fail to deactivate input event type(%d)", input_event_type);
-       return MMI_ERROR_INVALID_PARAMETER;
-}
diff --git a/src/mmi.h b/src/mmi.h
deleted file mode 100644 (file)
index 0a8683b..0000000
--- a/src/mmi.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#ifndef __MMI_H__
-#define __MMI_H__
-
-#include <tizen.h>
-
-#define MMI_API __attribute__ ((visibility("default")))
-
-typedef enum {
-       MMI_RESULT_NONE,
-       MMI_RESULT_FAIL,
-       MMI_RESULT_SUCCESS
-} mmi_result_e;
-
-#define TIZEN_ERROR_MMI        -0x030F0000
-
-typedef enum {
-       MMI_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
-       MMI_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of Memory */
-       MMI_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR, /**< I/O error */
-       MMI_ERROR_INVALID_PARAMETER     = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
-       MMI_ERROR_OUT_OF_NETWORK = TIZEN_ERROR_NETWORK_DOWN, /**< Network is down */
-       MMI_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< No answer from the daemon */
-       MMI_ERROR_PERMISSION_DENIED     = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */
-       MMI_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< MMI NOT supported */
-       MMI_ERROR_OPERATION_FAILED = TIZEN_ERROR_MMI | 0x01, /**< Operation failed */
-} mmi_error_e;
-
-typedef void (*mmi_result_cb)(int input_event_type, const char *result_out, void *user_data);
-
-typedef enum {
-       MMI_INPUT_EVENT_TYPE_NONE,
-       MMI_INPUT_EVENT_TYPE_VOICE_TOUCH,
-       MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION,
-} mmi_input_event_type_e;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-MMI_API int mmi_initialize(void);
-MMI_API int mmi_deinitialize(void);
-MMI_API int mmi_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data);
-MMI_API int mmi_activate_input_event(int input_event_type);
-MMI_API int mmi_deactivate_input_event(int input_event_type);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //__MMI_H__
diff --git a/src/mmi/meson.build b/src/mmi/meson.build
new file mode 100644 (file)
index 0000000..4260c90
--- /dev/null
@@ -0,0 +1,69 @@
+mmi_srcs = [
+       'mmi.cpp',
+       'mmi-workflow.cpp',
+       'mmi-workflow-instance.cpp',
+       'mmi-workflow-script.cpp',
+       'mmi-ipc-tidl.h',
+       'mmi-ipc-tidl.cpp',
+       'mmi-log.h',
+       'mmi_proxy.h',
+       'mmi_proxy.c',
+       'mmi-primitive-value.cpp',
+       'mmi-data.cpp',
+       'mmi-attribute.cpp',
+       'mmi-node.cpp',
+       'mmi-node-source.cpp',
+       'mmi-node-processor.cpp',
+       'mmi-node-logic.cpp',
+       'mmi-node-controller.cpp',
+       'mmi-port.cpp',
+       'mmi-signal.cpp',
+       'mmi-plugin-storage.cpp',
+       ]
+
+glib_dep = dependency('glib-2.0', method : 'pkg-config')
+gio_dep = dependency('gio-2.0', method : 'pkg-config')
+bundle_dep = dependency('bundle', method : 'pkg-config')
+dlog_dep = dependency('dlog', method : 'pkg-config')
+rpc_port_dep = dependency('rpc-port', method : 'pkg-config')
+libtzplatform_config_dep = dependency('libtzplatform-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_deps = [
+       ecore_dep,
+       glib_dep,
+       gio_dep,
+       bundle_dep,
+       dlog_dep,
+       rpc_port_dep,
+       libtzplatform_config_dep]
+
+mmi_include_dirs = include_directories(
+       '.',
+       '../common',
+       '../../capi'
+       )
+
+mmi_lib = shared_library(
+       'mmi',
+       mmi_srcs,
+       dependencies : [mmi_deps],
+       include_directories : [mmi_include_dirs],
+       version : meson.project_version(),
+       install : true
+       )
+
+pkgconfig = import('pkgconfig')
+pkgconfig.generate(
+       filebase : 'mmi',
+       name : 'mmi',
+       description : 'Multi-modal Interaction Framework Library',
+       version : meson.project_version(),
+       libraries : mmi_lib
+       )
+
+mmi_declared_dep = declare_dependency(
+       link_with : mmi_lib,
+       dependencies : [mmi_deps],
+       include_directories : [mmi_include_dirs]
+       )
diff --git a/src/mmi/mmi-attribute.cpp b/src/mmi/mmi-attribute.cpp
new file mode 100644 (file)
index 0000000..f49bc0c
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-error.h>
+#include <mmi-primitive-value.h>
+#include <mmi-attribute.h>
+
+#include "mmi-log.h"
+
+#include <cstdio>
+
+constexpr size_t MAX_ATTRIBUTE_NAME_SIZE = 256;
+
+struct mmi_attribute_s {
+    char name[MAX_ATTRIBUTE_NAME_SIZE];
+    mmi_primitive_value_h value;
+};
+
+
+static constexpr size_t g_unit_size = sizeof(mmi_primitive_value_h);
+
+
+int mmi_attribute_create(mmi_primitive_value_h value, const char *name,  mmi_attribute_h *attribute) {
+    if (nullptr == value || nullptr == name || nullptr == attribute) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    const size_t length = strlen(name);
+    if (0 == length || MAX_ATTRIBUTE_NAME_SIZE <= length) {
+        _E("[ERROR] Length of name is invalid. length(%zu)", length);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_attribute_h new_attribute = reinterpret_cast<mmi_attribute_s *>(calloc(1, sizeof(mmi_attribute_s)));
+    if (nullptr == new_attribute) {
+        _E("[ERROR] Fail to allocate memory for attribute");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    snprintf(new_attribute->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", name);
+    int ret = mmi_primitive_value_clone(value, &(new_attribute->value));
+    if (MMI_ERROR_NONE != ret) {
+        free(new_attribute);
+        _E("[ERROR] Fail to clone primitve value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *attribute = new_attribute;
+
+    _D("[DEBUG] Success to create attribute");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_set_name(mmi_attribute_h attribute, const char *name) {
+    if (nullptr == attribute || nullptr == name) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto attribute_ptr = static_cast<mmi_attribute_s *>(attribute);
+    snprintf(attribute_ptr->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", name);
+
+    _D("[DEBUG] Success to set name (%s)", attribute_ptr->name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_get_name(mmi_attribute_h attribute, char **name) {
+    if (nullptr == attribute || nullptr == name) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *name = strdup(attribute->name);
+    if (nullptr == *name) {
+        _E("[ERROR] Fail to allocate memory for name string");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    _D("[DEBUG] Success to get name (%s)", *name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_get_value(mmi_attribute_h attribute, mmi_primitive_value_h *value) {
+    if (nullptr == attribute || nullptr == value) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = mmi_primitive_value_clone(attribute->value, value);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to clone primitve value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    _D("[DEBUG] Success to get primitive value");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_clone(mmi_attribute_h attribute, mmi_attribute_h *cloned) {
+    if (nullptr == attribute || nullptr == cloned) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_attribute_h new_attribute = reinterpret_cast<mmi_attribute_s *>(calloc(1, sizeof(mmi_attribute_s)));
+    if (nullptr == new_attribute) {
+        _E("[ERROR] Fail to allocate memory for attribute");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    snprintf(new_attribute->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", attribute->name);
+    int ret = mmi_primitive_value_clone(attribute->value, &(new_attribute->value));
+    if (MMI_ERROR_NONE != ret) {
+        free(new_attribute);
+        _E("[ERROR] Fail to clone primitve value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *cloned = new_attribute;
+
+    _D("[DEBUG] Success to clone attribute");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_destroy(mmi_attribute_h attribute) {
+    if (nullptr == attribute) {
+        _E("[ERROR] Parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = mmi_primitive_value_destroy(attribute->value);
+    if (MMI_ERROR_NONE != ret) {
+        _W("[WARN] Fail to destroy primitive value");
+    }
+
+    free(attribute);
+
+    _D("[DEBUG] Success to destroy attribute");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_to_bytes(mmi_attribute_h attribute, unsigned char **bytes, size_t *length) {
+    if (nullptr == attribute || nullptr == bytes || nullptr == length) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t name_length = strlen(attribute->name);
+    size_t value_length = 0;
+    unsigned char *value_bytes = nullptr;
+    int ret = mmi_primitive_value_to_bytes(attribute->value, &value_bytes, &value_length);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to convert primitive value to bytes");
+        free(value_bytes);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *length = sizeof(name_length) + name_length + sizeof(value_length) + value_length;
+    *bytes = reinterpret_cast<unsigned char *>(calloc(1, *length));
+    if (nullptr == *bytes) {
+        _E("[ERROR] Fail to allocate memory for bytes");
+        free(value_bytes);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    unsigned char *pos = *bytes;
+    memcpy(pos, &name_length, sizeof(name_length));
+    pos += sizeof(name_length);
+    memcpy(pos, attribute->name, name_length);
+    pos += name_length;
+    memcpy(pos, &value_length, sizeof(value_length));
+    pos += sizeof(value_length);
+    memcpy(pos, value_bytes, value_length);
+
+    free(value_bytes);
+
+    _D("[DEBUG] Success to convert attribute to bytes");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_from_bytes(const unsigned char *bytes, size_t length, mmi_attribute_h *attribute) {
+    if (nullptr == bytes || 0 == length || nullptr == attribute) {
+        _E("[ERROR] Some parameters are null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t name_length = 0;
+    char *name = nullptr;
+    unsigned char *pos = const_cast<unsigned char *>(bytes);
+    memcpy(&name_length, pos, sizeof(name_length));
+    pos += sizeof(name_length);
+    name = reinterpret_cast<char *>(calloc(1, name_length + 1));
+    if (nullptr == name) {
+        _E("[ERROR] Fail to allocate memory for name string");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+    memcpy(name, pos, name_length);
+    pos += name_length;
+
+    size_t value_length = 0;
+    unsigned char *value_bytes = nullptr;
+    memcpy(&value_length, pos, sizeof(value_length));
+    pos += sizeof(value_length);
+    value_bytes = reinterpret_cast<unsigned char *>(calloc(1, value_length));
+    if (nullptr == value_bytes) {
+        _E("[ERROR] Fail to allocate memory for value bytes");
+        free(name);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+    memcpy(value_bytes, pos, value_length);
+    pos += value_length;
+
+    mmi_primitive_value_h value = nullptr;
+    int ret = mmi_primitive_value_from_bytes(value_bytes, value_length, &value);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to convert bytes to primitive value");
+        free(name);
+        free(value_bytes);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    free(value_bytes);
+
+    ret = mmi_attribute_create(value, name, attribute);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to create attribute");
+        free(name);
+        mmi_primitive_value_destroy(value);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    mmi_primitive_value_destroy(value);
+
+    free(name);
+
+    _D("[DEBUG] Success to convert bytes to attribute");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_attribute_create_string_array(const char *name, const char *strings[], size_t count, mmi_attribute_h *attribute) {
+    if (nullptr == attribute || nullptr == name || nullptr == strings || 0 == count) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_primitive_value_h array = nullptr;
+    int ret = mmi_primitive_value_create_array(&array);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to allocate memory for array primitive");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    for (size_t i = 0; i < count; i++) {
+        mmi_primitive_value_h element = nullptr;
+        int ret = mmi_primitive_value_create_string(strings[i], &element);
+        if (MMI_ERROR_NONE != ret) {
+            _E("[ERROR] Fail to allocate memory for array attribute");
+            mmi_primitive_value_destroy(array);
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+
+        ret = mmi_primitive_value_add_array_element(array, element);
+        if (MMI_ERROR_NONE != ret) {
+            _E("[ERROR] Fail to allocate memory for array attribute");
+            mmi_primitive_value_destroy(array);
+            mmi_primitive_value_destroy(element);
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+    }
+
+    ret = mmi_attribute_create(array, name, attribute);
+    if (MMI_ERROR_NONE != ret) {
+        mmi_primitive_value_destroy(array);
+        _E("[ERROR] Fail to allocate memory for array attribute");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    ret = mmi_primitive_value_destroy(array);
+    if (MMI_ERROR_NONE != ret) {
+        _W("[WARN] Fail to destroy temporary primitive value");
+    }
+
+    _D("[DEBUG] Success to create attribute");
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi-client-manager.h b/src/mmi/mmi-client-manager.h
new file mode 100644 (file)
index 0000000..c8b921b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "mmi.h"
+
+#include "mmi-event-observer.h"
+#include "mmi-communication-channel.h"
+#include "mmi-workflow-instance-manager.h"
+
+using namespace mmi;
+using namespace mmi::communication;
+
+struct ICommunicationChannelFactory {
+    virtual CommunicationChannel* create_channel() = 0;
+    virtual void destroy_channel(CommunicationChannel* channel) = 0;
+};
+
+class ClientManager : public SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE> {
+public:
+    ClientManager(
+        ICommunicationChannelFactory *factory,
+        SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE> *observer)
+        : m_communication_channel_factory(factory),
+          m_communication_channel_observer(observer) {
+    }
+    virtual ~ClientManager() {
+    }
+
+    bool initialize() {
+        if (m_reference_count == 0) {
+            m_communication_channel =
+                m_communication_channel_factory->create_channel();
+            if (nullptr == m_communication_channel) {
+                return false;
+            } else {
+                m_communication_channel->add_observer(m_communication_channel_observer);
+                m_communication_channel->add_observer(this);
+                m_communication_channel->connect();
+            }
+
+            m_workflow_instance_manager.initialize();
+        }
+        m_reference_count++;
+        return true;
+    }
+    bool deinitialize() {
+        if (m_reference_count == 0) {
+            return false;
+        }
+        m_reference_count--;
+        if (m_reference_count == 0) {
+            if (m_communication_channel) {
+                m_communication_channel->disconnect();
+                m_communication_channel_factory->destroy_channel(m_communication_channel);
+            }
+            m_communication_channel = nullptr;
+
+            m_workflow_instance_manager.deinitialize();
+        }
+        return true;
+    }
+
+    CommunicationChannel* get_communication_channel() {
+        return m_communication_channel;
+    }
+    WorkflowInstanceManager* get_workflow_instance_manager() {
+        return &m_workflow_instance_manager;
+    }
+
+    void on_observer_event(
+        const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override {
+    }
+
+private:
+    ICommunicationChannelFactory* m_communication_channel_factory{nullptr};
+    CommunicationChannel *m_communication_channel{nullptr};
+    SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE>
+    *m_communication_channel_observer;
+    WorkflowInstanceManager m_workflow_instance_manager;
+
+    int m_reference_count{0};
+};
+
diff --git a/src/mmi/mmi-data.cpp b/src/mmi/mmi-data.cpp
new file mode 100644 (file)
index 0000000..7119506
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <new>
+#include <string>
+#include <vector>
+
+#include "mmi-log.h"
+
+#include <mmi-error.h>
+#include <mmi-data.h>
+
+
+struct mmi_data_timestamp_s {
+    bool valid;
+    void *data;
+};
+
+struct mmi_data_s {
+    mmi_data_type_e type;
+    void *data;
+    size_t datalen;
+};
+
+
+static constexpr size_t g_unit_size = sizeof(mmi_data_h);
+
+
+static constexpr size_t get_count_of_elements(mmi_data_h array) {
+    return array->datalen / g_unit_size;
+}
+
+static constexpr size_t get_count_of_struct_elements(mmi_data_h struct_handle) {
+    return get_count_of_elements(struct_handle) / 2;
+}
+
+static inline mmi_data_h *get_element_pointer_in_array(mmi_data_h array, size_t index) {
+    return reinterpret_cast<mmi_data_h *>(array->data) + index;
+}
+
+static inline mmi_data_h *get_element_name_pointer_in_struct(mmi_data_h struct_handle, size_t index) {
+    return get_element_pointer_in_array(struct_handle, index * 2);
+}
+
+static inline mmi_data_h *get_element_value_pointer_in_struct(mmi_data_h struct_handle, size_t index) {
+    return get_element_pointer_in_array(struct_handle, index * 2 + 1);
+}
+
+
+int mmi_data_create_bool(bool value, mmi_data_h *data) {
+    if (nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_BOOLEAN;
+    new_data->data = malloc(sizeof(bool));
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<bool *>(new_data->data)) = value;
+    new_data->datalen = sizeof(bool);
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for bool(%d)", value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_int(int value, mmi_data_h *data) {
+    if (nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_INTEGER;
+    new_data->data = malloc(sizeof(int));
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<int *>(new_data->data)) = value;
+    new_data->datalen = sizeof(int);
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for int(%d)", value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_float(float value, mmi_data_h *data) {
+    if (nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_FLOAT;
+    new_data->data = malloc(sizeof(float));
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<float *>(new_data->data)) = value;
+    new_data->datalen = sizeof(float);
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for float(%f)", value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_text(const char *value, mmi_data_h *data) {
+    if (nullptr == data || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_TEXT;
+    new_data->data = reinterpret_cast<void *>(strdup(value));
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->datalen = strlen(value);
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for string(%s)", value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_audio(const void *ptr, size_t len, mmi_data_h *data) {
+    if (nullptr == data || nullptr == ptr || 0 == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_AUDIO;
+    new_data->data = malloc(len);
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    memcpy(new_data->data, ptr, len);
+    new_data->datalen = len;
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for audio data. len(%zu)", len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_video(const void *ptr, size_t len, mmi_data_h *data) {
+    if (nullptr == data || nullptr == ptr || 0 == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_VIDEO;
+    new_data->data = malloc(len);
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    memcpy(new_data->data, ptr, len);
+    new_data->datalen = len;
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for video data. len(%zu)", len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_user_identification(const void *ptr, size_t len, mmi_data_h *data) {
+    if (nullptr == data || nullptr == ptr || 0 == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for mmi data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_USER_IDENTIFICATION;
+    new_data->data = malloc(len);
+    if (nullptr == new_data->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(new_data);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    memcpy(new_data->data, ptr, len);
+    new_data->datalen = len;
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create for user identification data. len(%zu)", len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_array(mmi_data_h *data) {
+    if (nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_ARRAY;
+    new_data->data = nullptr;
+    new_data->datalen = 0;
+
+    *data = new_data;
+
+    _D("[DEBUG] Success to create empty array");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_add_array_element(mmi_data_h array, mmi_data_h element) {
+    if (nullptr == array || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_DATA_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_count_of_elements(array);
+    if (count > 0) {
+        mmi_data_h first = *(get_element_pointer_in_array(array, 0));
+        if (first->type != element->type) {
+            _E("[ERROR] Element type is not matched");
+            return MMI_ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    auto array_data = reinterpret_cast<mmi_data_h *>(array->data);
+    array_data = reinterpret_cast<mmi_data_h *>(realloc(array_data, g_unit_size * (count + 1)));
+    if (array_data) {
+        array_data[count] = element;
+
+        array->data = array_data;
+        array->datalen = array->datalen + g_unit_size;
+    } else {
+        _E("[ERROR] Fail to allocate memory for array data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    _D("[DEBUG] Success to add element(%zu)", get_count_of_elements(array));
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_create_struct(mmi_data_h *struct_handle) {
+    if (nullptr == struct_handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto new_data = reinterpret_cast<mmi_data_s *>(malloc(sizeof(mmi_data_s)));
+    if (nullptr == new_data) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    new_data->type = MMI_DATA_TYPE_STRUCT;
+    new_data->data = nullptr;
+    new_data->datalen = 0;
+
+    *struct_handle = new_data;
+
+    _D("[DEBUG] Success to create empty array");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_set_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h element) {
+    if (nullptr == struct_handle || nullptr == name || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (struct_handle->type != MMI_DATA_TYPE_STRUCT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_data_h name_element = nullptr;
+    int ret = mmi_data_create_text(name, &name_element);
+    if (MMI_ERROR_NONE != ret) {
+        _E("[ERROR] Fail to allocate memory for name element");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    size_t count = get_count_of_elements(struct_handle);
+    auto struct_data = reinterpret_cast<mmi_data_h *>(struct_handle->data);
+    struct_data = reinterpret_cast<mmi_data_h *>(realloc(struct_data, g_unit_size * (count + 2)));
+    if (struct_data) {
+        struct_data[count] = name_element;
+        struct_data[count + 1] = element;
+
+        struct_handle->data = struct_data;
+        struct_handle->datalen = struct_handle->datalen + g_unit_size * 2;
+    } else {
+        mmi_data_destroy(name_element);
+        _E("[ERROR] Fail to allocate memory for struct data");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    _D("[DEBUG] Success to add element with name(%s)", name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_type(mmi_data_h data, mmi_data_type_e *type) {
+    if (nullptr == data || nullptr == type) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *type = data->type;
+
+    _D("[DEBUG] Success to get type(%d)", *type);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_bool(mmi_data_h data, bool *value) {
+    if (nullptr == data || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_BOOLEAN) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *value = *(reinterpret_cast<bool *>(data->data));
+
+    _D("[DEBUG] Success to get value: bool(%d)", *value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_int(mmi_data_h data, int *value) {
+    if (nullptr == data || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_INTEGER) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *value = *(reinterpret_cast<int *>(data->data));
+
+    _D("[DEBUG] Success to get value: int(%d)", *value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_float(mmi_data_h data, float *value) {
+    if (nullptr == data || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_FLOAT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *value = *(reinterpret_cast<float *>(data->data));
+
+    _D("[DEBUG] Success to get value: float(%f)", *value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_text(mmi_data_h data, const char **string) {
+    if (nullptr == data || nullptr == string) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_TEXT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *string = reinterpret_cast<char *>(data->data);
+
+    _D("[DEBUG] Success to get value: string(%s)", *string);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_audio(mmi_data_h data, const void **ptr, size_t *len) {
+    if (nullptr == data || nullptr == ptr || nullptr == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_AUDIO) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *len = data->datalen;
+    *ptr = data->data;
+
+    _D("[DEBUG] Success to get value: audio(%zu)", *len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_video(mmi_data_h data, const void **ptr, size_t *len) {
+    if (nullptr == data || nullptr == ptr || nullptr == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_VIDEO) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *len = data->datalen;
+    *ptr = data->data;
+
+    _D("[DEBUG] Success to get value: video(%zu)", *len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_user_identification(mmi_data_h data, const void **ptr, size_t *len) {
+    if (nullptr == data || nullptr == ptr || nullptr == len) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type != MMI_DATA_TYPE_USER_IDENTIFICATION) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *len = data->datalen;
+    *ptr = data->data;
+
+    _D("[DEBUG] Success to get value: user_identification(%zu)", *len);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_array_count(mmi_data_h array, size_t *count) {
+    if (nullptr == array || nullptr == count) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_DATA_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *count = get_count_of_elements(array);
+
+    _D("[DEBUG] Success to get array count(%zu)", *count);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_array_element(mmi_data_h array, size_t index, mmi_data_h *element) {
+    if (nullptr == array || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_DATA_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_count_of_elements(array);
+    if (index >= count) {
+        _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *element = *(get_element_pointer_in_array(array, index));
+
+    _D("[DEBUG] Success to get array element on index(%zu)", index);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h *element) {
+    if (nullptr == struct_handle || nullptr == name || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (struct_handle->type != MMI_DATA_TYPE_STRUCT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_count_of_elements(struct_handle);
+    if (count % 2 != 0) {
+        _E("[ERROR] Element size is invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_data_h target_element = nullptr;
+    auto struct_data = reinterpret_cast<mmi_data_h *>(struct_handle->data);
+    for (size_t i = 0; i < count; i += 2) {
+        const char *element_name = nullptr;
+        mmi_data_get_text(struct_data[i], &element_name);
+        if (0 == strcmp(name, element_name)) {
+            target_element = struct_data[i + 1];
+            break;
+        }
+    }
+
+    if (nullptr == target_element) {
+        _E("[ERROR] No matched element with name (%s)", name);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *element = target_element;
+
+    _D("[DEBUG] Success to get element on name(%s)", name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_struct_count(mmi_data_h struct_handle, size_t *count) {
+    if (nullptr == struct_handle || nullptr == count) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (struct_handle->type != MMI_DATA_TYPE_STRUCT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *count = get_count_of_struct_elements(struct_handle);
+
+    _D("[DEBUG] Success to get struct count(%zu)", *count);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_struct_element_name(mmi_data_h struct_handle, size_t index, const char **string) {
+    if (nullptr == struct_handle || nullptr == string) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (struct_handle->type != MMI_DATA_TYPE_STRUCT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_count_of_struct_elements(struct_handle);
+    if (index >= count) {
+        _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_data_h *element = get_element_name_pointer_in_struct(struct_handle, index);
+    mmi_data_get_text(*element, string);
+
+    _D("[DEBUG] Success to get struct element name on index(%zu)", index);
+    return MMI_ERROR_NONE;
+}
+
+
+int mmi_data_get_struct_element_value(mmi_data_h struct_handle, size_t index, mmi_data_h *element) {
+    if (nullptr == struct_handle || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (struct_handle->type != MMI_DATA_TYPE_STRUCT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_count_of_struct_elements(struct_handle);
+    if (index >= count) {
+        _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *element = *(get_element_value_pointer_in_struct(struct_handle, index));
+
+    _D("[DEBUG] Success to get struct element value on index(%zu)", index);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_destroy(mmi_data_h data) {
+    if (nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (data->type == MMI_DATA_TYPE_ARRAY || data->type == MMI_DATA_TYPE_STRUCT) {
+        size_t count = get_count_of_elements(data);
+        for (size_t i = 0; i < count; i++) {
+            auto element = get_element_pointer_in_array(data, i);
+            mmi_data_destroy(*element);
+        }
+    }
+
+    free(data->data);
+    data->data = nullptr;
+    data->datalen = 0;
+    data->type = MMI_DATA_TYPE_ANY;
+    free(data);
+
+    _D("[DEBUG] Success to destroy");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_node_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp) {
+    if (nullptr == data || nullptr == timestamp) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *timestamp = (mmi_data_timestamp_s*)(data->data);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_get_source_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp) {
+    if (nullptr == data || nullptr == timestamp) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *timestamp = (mmi_data_timestamp_s*)(data->data);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_set_source_timestamp(mmi_data_h data, mmi_data_timestamp_h timestamp) {
+    if (nullptr == data || nullptr == timestamp) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    data->data = timestamp;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_to_bytes(mmi_data_h data, unsigned char **bytes, size_t *length) {
+    if (nullptr == data || nullptr == bytes || nullptr == length) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_data_type_e type;
+    mmi_data_get_type(data, &type);
+    // TODO:: add data type, separate to function
+
+    if (type == MMI_DATA_TYPE_ARRAY || type == MMI_DATA_TYPE_STRUCT) {
+        size_t current_size = sizeof(mmi_data_type_e) + sizeof(size_t);
+        unsigned char *buffer = reinterpret_cast<unsigned char *>(malloc(current_size));
+
+        size_t count = get_count_of_elements(data);
+        unsigned char *current = buffer;
+        memcpy(current, &(data->type), sizeof(mmi_data_type_e));
+        current += sizeof(mmi_data_type_e);
+        /* This size will be updated after getting element bytes */
+        memcpy(current, &count, sizeof(size_t));
+        current += sizeof(size_t);
+        for (size_t i = 0; i < count; i++) {
+            auto element = get_element_pointer_in_array(data, i);
+            unsigned char *element_bytes = nullptr;
+            size_t element_size = 0;
+            mmi_data_to_bytes(*element, &element_bytes, &element_size);
+            buffer = reinterpret_cast<unsigned char *>(realloc(buffer, current_size + element_size + sizeof(size_t)));
+            if (nullptr == buffer) {
+                _E("[ERROR] Failed to allocate memory");
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+            current = buffer + current_size;
+            memcpy(current, &element_size, sizeof(size_t));
+            current += sizeof(size_t);
+            current_size += sizeof(size_t);
+            memcpy(current, element_bytes, element_size);
+            current_size += element_size;
+            free(element_bytes);
+        }
+        size_t total_element_size = current_size - sizeof(mmi_data_type_e) - sizeof(size_t);
+        memcpy(buffer + sizeof(mmi_data_type_e), &total_element_size, sizeof(size_t));
+
+        *bytes = buffer;
+        *length = current_size;
+    } else {
+        size_t total_size = sizeof(mmi_data_type_e) + sizeof(size_t) + data->datalen;
+        unsigned char *buffer = reinterpret_cast<unsigned char *>(malloc(total_size));
+        if (nullptr == buffer) {
+            _E("[ERROR] Failed to allocate memory");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        unsigned char *current = buffer;
+        memcpy(current, &(data->type), sizeof(mmi_data_type_e));
+        current += sizeof(mmi_data_type_e);
+        memcpy(current, &(data->datalen), sizeof(size_t));
+        current += sizeof(size_t);
+        memcpy(current, data->data, data->datalen);
+
+        *bytes = buffer;
+        *length = total_size;
+    }
+
+    _D("[DEBUG] Success to convert data to bytes");
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_data_from_bytes(unsigned char *bytes, size_t length, mmi_data_h *data) {
+    if (nullptr == data || nullptr == bytes || 0 == length) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    unsigned char *current = bytes;
+    mmi_data_type_e type = MMI_DATA_TYPE_ANY;
+    memcpy(&type, current, sizeof(mmi_data_type_e));
+    current += sizeof(mmi_data_type_e);
+    if (type == MMI_DATA_TYPE_ARRAY || type == MMI_DATA_TYPE_STRUCT) {
+        size_t datalen = 0;
+        memcpy(&datalen, current, sizeof(size_t));
+        current += sizeof(size_t);
+        void *buf = nullptr;
+        auto value = reinterpret_cast<mmi_data_h>(malloc(sizeof(mmi_data_s)));
+        value->type = type;
+        value->datalen = 0;
+        value->data = nullptr;
+
+        size_t num_elements = 0;
+        while (current < bytes + length) {
+            size_t element_size;
+            memcpy(&element_size, current, sizeof(size_t));
+            current += sizeof(size_t);
+            unsigned char *element_bytes = reinterpret_cast<unsigned char *>(malloc(element_size));
+            if (nullptr == element_bytes) {
+                _E("[ERROR] Failed to allocate memory");
+                mmi_data_destroy(value);
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+
+            memcpy(element_bytes, current, element_size);
+            current += element_size;
+            mmi_data_h element = nullptr;
+            mmi_data_from_bytes(element_bytes, element_size, &element);
+            free(element_bytes);
+            if (nullptr == element) {
+                _E("[ERROR] Failed to convert bytes to data");
+                mmi_data_destroy(value);
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+
+            auto array_data = reinterpret_cast<mmi_data_h *>(value->data);
+            array_data = reinterpret_cast<mmi_data_h *>(realloc(array_data, g_unit_size * (num_elements + 1)));
+            if (array_data) {
+                array_data[num_elements] = element;
+
+                value->data = array_data;
+                value->datalen += g_unit_size;
+            } else {
+                _E("[ERROR] Fail to allocate memory for array data");
+                mmi_data_destroy(element);
+                mmi_data_destroy(value);
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+
+            num_elements++;
+        }
+
+        *data = value;
+    } else {
+        size_t datalen;
+        memcpy(&datalen, current, sizeof(size_t));
+        current += sizeof(size_t);
+        void *buf = calloc(1, datalen);
+        if (nullptr == buf) {
+            _E("[ERROR] Failed to allocate memory");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        memcpy(buf, current, datalen);
+
+        auto value = reinterpret_cast<mmi_data_h>(malloc(sizeof(mmi_data_s)));
+        value->type = type;
+        value->datalen = datalen;
+        value->data = buf;
+
+        *data = value;
+    }
+
+    _D("[DEBUG] Success to convert from bytes. type(%d), size(%zu)", type, length);
+
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi-ipc-tidl.cpp b/src/mmi/mmi-ipc-tidl.cpp
new file mode 100644 (file)
index 0000000..a58fc8f
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include "mmi-ipc-tidl.h"
+#include "mmi-log.h"
+#include "mmi-workflow-instance-manager.h"
+
+#include <tzplatform_config.h>
+#include <unistd.h>
+#include <rpc-port.h>
+#include <rpc-port-internal.h>
+
+namespace mmi {
+
+namespace communication {
+
+CommunicationChannelTIDL::CommunicationChannelTIDL() {
+}
+
+CommunicationChannelTIDL::~CommunicationChannelTIDL() {
+}
+
+static void result_cb(void *user_data, const char *source_name, bundle *data) {
+    mmi_data_h restored_data = nullptr;
+
+    if (nullptr == user_data) {
+        LOGE("[ERROR] user_data is null");
+        return;
+    }
+
+    WorkflowInstance *workflow_instance = static_cast<WorkflowInstance *>(user_data);
+    unsigned char *bytes = nullptr;
+    size_t returned_size = 0;
+
+    if (BUNDLE_ERROR_NONE != bundle_get_byte(data, "data", (void**)&bytes, &returned_size)) {
+        LOGE("[ERROR] Failed to get bytes from bundle");
+        return;
+    }
+
+    if (nullptr == bytes || 0 == returned_size) {
+        LOGE("[ERROR] bytes is null or returned_size is 0");
+        return;
+    }
+
+    if (MMI_ERROR_NONE != mmi_data_from_bytes(bytes, returned_size, &restored_data)) {
+        LOGE("[ERROR] Failed to restore data from bytes");
+        return;
+    }
+
+    workflow_instance->call_output_callback(source_name, restored_data);
+}
+
+void CommunicationChannelTIDL::on_connected(rpc_port_proxy_mmi_h h, void *user_data) {
+    LOGI("[ENTER]");
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+    if (channel) {
+        channel->m_connected = true;
+        channel->notify_observers(
+            COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED);
+    }
+}
+
+void CommunicationChannelTIDL::on_disconnected(rpc_port_proxy_mmi_h h, void *user_data) {
+    LOGI("[ENTER]");
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+    if (channel) {
+        channel->m_connected = false;
+        channel->notify_observers(
+            COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED);
+    }
+}
+
+void CommunicationChannelTIDL::on_rejected(rpc_port_proxy_mmi_h h, void *user_data) {
+    LOGI("[ENTER]");
+    CommunicationChannelTIDL *channel = static_cast<CommunicationChannelTIDL*>(user_data);
+    if (channel) {
+        channel->m_connected = false;
+        channel->notify_observers(
+            COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED);
+    }
+}
+
+int CommunicationChannelTIDL::connect() {
+    LOGI("[ENTER]");
+
+    if (m_rpc_h) {
+        LOGI("RPC Handle already exists");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    rpc_port_proxy_mmi_callback_s callback;
+    callback.connected = on_connected;
+    callback.disconnected = on_disconnected;
+    callback.rejected = on_rejected;
+
+    int ret = rpc_port_proxy_mmi_create(m_stub_appid.c_str(), &callback, this, &m_rpc_h);
+    if (ret != RPC_PORT_ERROR_NONE) {
+        LOGE("Failed to create mmi proxy handle ! (error:%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    ret = rpc_port_proxy_mmi_connect_sync(m_rpc_h);
+    if (ret != RPC_PORT_ERROR_NONE) {
+        LOGE("Try to connect again");
+        //Retry
+        ret = retry_connection(m_rpc_h);
+        if (ret != RPC_PORT_ERROR_NONE) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    LOGI("Connected to mmi manager");
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::disconnect(void) {
+    if (!m_rpc_h) {
+        LOGE("RPC Handle does not exist");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    rpc_port_proxy_mmi_destroy(m_rpc_h);
+    rpc_port_deregister_proc_info();
+    m_rpc_h = nullptr;
+
+    m_connected = false;
+
+    LOGI("disconnected from mmi manager");
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::send(Message *message) {
+    auto clientMessage = static_cast<ClientMessage*>(message);
+    CLIENT_MESSAGE_TYPE type = clientMessage->message_type;
+
+    switch(type) {
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE: {
+        auto subclass = static_cast<ClientMessageWorkflowInstanceCreate*>(message);
+        workflow_instance_create(subclass->local_workflow_instance_id, subclass->workflow_type, subclass->user_data);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY: {
+        auto subclass = static_cast<ClientMessageWorkflowInstanceDestroy*>(message);
+        workflow_instance_destroy(subclass->local_workflow_instance_id);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE: {
+        auto subclass = static_cast<ClientMessageWorkflowInstanceSetAttribute*>(message);
+        workflow_instance_set_attribute(subclass->local_workflow_instance_id, subclass->attribute);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE: {
+        auto subclass = static_cast<ClientMessageWorkflowInstanceActivate*>(message);
+        workflow_instance_activate(subclass->local_workflow_instance_id);
+    }
+    break;
+    case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE: {
+        auto subclass = static_cast<ClientMessageWorkflowInstanceDeactivate*>(message);
+        workflow_instance_deactivate(subclass->local_workflow_instance_id);
+    }
+    break;
+    };
+
+    return MMI_ERROR_NONE;
+}
+
+bool CommunicationChannelTIDL::is_connected(void) {
+    return m_connected ? true : false;
+}
+
+int CommunicationChannelTIDL::retry_connection(rpc_port_proxy_mmi_h h) {
+    LOGI("Retry connection");
+
+    int ret = rpc_port_proxy_mmi_connect_sync(h);
+    if (ret != RPC_PORT_ERROR_NONE) {
+        LOGE("Fail to retry connection(%d)", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_create(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type, void *user_data) {
+    LOGI("Creating workflow instance of type (%d) for id (%d)",
+         workflow_type, local_workflow_instance_id);
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    int ret = rpc_port_proxy_mmi_invoke_workflow_instance_create(
+                  rpc_h, local_workflow_instance_id, workflow_type);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to create workflow instance(%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    ret = workflow_instance_register_result_callback(local_workflow_instance_id, user_data);
+    if (MMI_ERROR_NONE != ret) {
+        LOGE("Failed to register result callback(%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_destroy(int local_workflow_instance_id) {
+    LOGI("Destroying workflow instance of id (%d)", local_workflow_instance_id);
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    int ret = rpc_port_proxy_mmi_invoke_workflow_instance_destroy(
+                  rpc_h, local_workflow_instance_id);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to destroy workflow instance(%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_set_attribute(int local_workflow_instance_id, mmi_attribute_h attribute) {
+    LOGI("Setting attribute for workflow instance (%d)", local_workflow_instance_id);
+    int ret = MMI_ERROR_NONE;
+
+    if (NULL == attribute) {
+        LOGE("Parameter is NULL or empty");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    unsigned char *attribute_bytes = nullptr;
+    size_t attribute_byte_size = 0;
+    mmi_attribute_to_bytes(attribute, &attribute_bytes, &attribute_byte_size);
+
+    bundle *encoded = bundle_create();
+    bundle_add_byte(encoded, "data", attribute_bytes, attribute_byte_size);
+
+    if (encoded) {
+        int ret = rpc_port_proxy_mmi_invoke_workflow_instance_set_attribute(rpc_h, local_workflow_instance_id, encoded);
+        bundle_free(encoded);
+
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Failed to set workflow attribute(%d)\n", ret);
+            ret = MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    return ret;
+}
+
+int CommunicationChannelTIDL::workflow_instance_activate(int local_workflow_instance_id) {
+    LOGI("Activating workflow instance of id (%d)", local_workflow_instance_id);
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    int ret = rpc_port_proxy_mmi_invoke_workflow_instance_activate(
+                  rpc_h, local_workflow_instance_id);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to activate workflow instance(%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_deactivate(int local_workflow_instance_id) {
+    LOGI("Deactivating workflow instance of id (%d)", local_workflow_instance_id);
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    int ret = rpc_port_proxy_mmi_invoke_workflow_instance_deactivate(
+                  rpc_h, local_workflow_instance_id);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to deactivate workflow instance(%d)\n", ret);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+int CommunicationChannelTIDL::workflow_instance_register_result_callback(int local_workflow_instance_id, void *user_data) {
+    LOGI("Registering result callback for workflow instance of id (%d)", local_workflow_instance_id);
+
+    if (nullptr == user_data) {
+        LOGE("Parameter is NULL or empty");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    rpc_port_proxy_mmi_h rpc_h = static_cast<rpc_port_proxy_mmi_h>(m_rpc_h);
+    if (NULL == rpc_h) {
+        LOGE("Fail to get tidl rpc info");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    if (false == is_connected()) {
+        LOGE("Try to connect again");
+        int ret = retry_connection(rpc_h);
+        if (RPC_PORT_ERROR_NONE != ret) {
+            LOGE("Fail to retry connection(%d)", ret);
+            return MMI_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    rpc_port_proxy_mmi_result_cb_h result_cb_h = make_callback_handle(result_cb, user_data);
+
+    int ret = rpc_port_proxy_mmi_invoke_workflow_instance_register_result_callback(
+                  rpc_h, local_workflow_instance_id, result_cb_h);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to register result callback(%d)\n", ret);
+        rpc_port_proxy_mmi_result_cb_destroy(result_cb_h);
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    LOGI("register result callback done");
+    return MMI_ERROR_NONE;
+}
+
+rpc_port_proxy_mmi_result_cb_h CommunicationChannelTIDL::make_callback_handle(rpc_port_proxy_mmi_result_cb_cb callback, void *user_data) {
+    rpc_port_proxy_mmi_result_cb_h result_cb_h;
+    int ret = rpc_port_proxy_mmi_result_cb_create(&result_cb_h);
+    if (RPC_PORT_ERROR_NONE != ret) {
+        LOGE("Failed to create result callback handle (error:%d)\n", ret);
+        return nullptr;
+    }
+    rpc_port_proxy_mmi_result_cb_set_callback(result_cb_h, callback, user_data);
+    rpc_port_proxy_mmi_result_cb_set_once(result_cb_h, false);
+    if (NULL == result_cb_h) {
+        LOGE("Failed to set result callback handle\n");
+        return nullptr;
+    }
+    return result_cb_h;
+}
+
+}; // namespace communication
+
+}; // namespace mmi
diff --git a/src/mmi/mmi-ipc-tidl.h b/src/mmi/mmi-ipc-tidl.h
new file mode 100644 (file)
index 0000000..8f794c7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_IPC_TIDL_H__
+#define __MMI_IPC_TIDL_H__
+
+#include "mmi.h"
+
+#include "mmi-communication-channel.h"
+
+#include "mmi_proxy.h"
+
+namespace mmi {
+
+namespace communication {
+
+class CommunicationChannelTIDL : public CommunicationChannelClient {
+public:
+    CommunicationChannelTIDL();
+    ~CommunicationChannelTIDL();
+
+    int connect() override;
+    int disconnect(void) override;
+    int send(communication::Message *message) override;
+
+private:
+    int workflow_instance_create(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type, void *user_data);
+    int workflow_instance_destroy(int local_workflow_instance_id);
+    int workflow_instance_set_attribute(int local_workflow_instance_id, mmi_attribute_h attribute);
+    int workflow_instance_activate(int local_workflow_instance_id);
+    int workflow_instance_deactivate(int local_workflow_instance_id);
+    int workflow_instance_register_result_callback(int local_workflow_instance_id, void *user_data);
+
+    bool is_connected(void);
+    int retry_connection(rpc_port_proxy_mmi_h h);
+
+    static void on_connected(rpc_port_proxy_mmi_h h, void *user_data);
+    static void on_disconnected(rpc_port_proxy_mmi_h h, void *user_data);
+    static void on_rejected(rpc_port_proxy_mmi_h h, void *user_data);
+
+    static rpc_port_proxy_mmi_result_cb_h make_callback_handle(rpc_port_proxy_mmi_result_cb_cb callback, void *user_data);
+
+private:
+    rpc_port_proxy_mmi_h m_rpc_h{nullptr};
+    const std::string m_stub_appid{"mmi-manager"};
+    bool m_connected{false};
+};
+
+}; // namespace communication
+
+}; // namespace mmi
+
+#endif //__MMI_IPC_TIDL_H__
similarity index 92%
rename from src/mmi-dbg.h
rename to src/mmi/mmi-log.h
index 398875e..7718fcd 100644 (file)
@@ -15,8 +15,8 @@
  *
  */
 
-#ifndef __MMI_DBG_H__
-#define __MMI_DBG_H__
+#ifndef __MMI_LOG_H__
+#define __MMI_LOG_H__
 
 #include <dlog.h>
 
@@ -41,4 +41,4 @@
 #define _W LOGW
 #endif
 
-#endif // __MMI_DBG_H__ 
+#endif // __MMI_LOG_H__
diff --git a/src/mmi/mmi-node-logic.cpp b/src/mmi/mmi-node-logic.cpp
new file mode 100644 (file)
index 0000000..36e003d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-node-logic.h"
+
+#include "mmi-log.h"
+
+int mmi_standard_node_create_logic(mmi_standard_node_logic_type_e type, mmi_node_h *node) {
+    if (nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *node = new mmi_node_s;
+    (*node)->type = MMI_STANDARD_NODE_TYPE_LOGIC;
+    (*node)->sub_type = (int)type;
+    (*node)->ports = nullptr;
+    (*node)->port_count = 0;
+    memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks));
+    return MMI_ERROR_NONE;
+}
+
+int mmi_standard_node_get_logic_type(mmi_node_h node, mmi_standard_node_logic_type_e *type) {
+    if (nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *type = (mmi_standard_node_logic_type_e)node->sub_type;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_standard_node_register_logic(mmi_standard_node_logic_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node) {
+    if (nullptr == callbacks || nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
diff --git a/src/mmi/mmi-node-processor.cpp b/src/mmi/mmi-node-processor.cpp
new file mode 100644 (file)
index 0000000..8634a81
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-node-processor.h"
+
+#include "mmi-log.h"
+
+int mmi_standard_node_create_processor(mmi_standard_node_processor_type_e type, mmi_node_h *node) {
+    if (nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *node = new mmi_node_s;
+    (*node)->type = MMI_STANDARD_NODE_TYPE_PROCESSOR;
+    (*node)->sub_type = (int)type;
+    (*node)->ports = nullptr;
+    (*node)->port_count = 0;
+    memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks));
+    return MMI_ERROR_NONE;
+}
+
+int mmi_standard_node_get_processor_type(mmi_node_h node, mmi_standard_node_processor_type_e *type) {
+    if (nullptr == node || nullptr == type) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *type = (mmi_standard_node_processor_type_e)node->sub_type;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_standard_node_register_processor(mmi_standard_node_processor_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node) {
+    if (nullptr == callbacks || nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
diff --git a/src/mmi/mmi-node-source.cpp b/src/mmi/mmi-node-source.cpp
new file mode 100644 (file)
index 0000000..30abd0d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-node-source.h"
+
+#include "mmi-log.h"
+
+int mmi_standard_node_create_source(mmi_standard_node_source_type_e type, mmi_node_h *node) {
+    if (nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *node = new mmi_node_s;
+    (*node)->type = MMI_STANDARD_NODE_TYPE_SOURCE;
+    (*node)->sub_type = (int)type;
+    (*node)->ports = nullptr;
+    (*node)->port_count = 0;
+    memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks));
+    return MMI_ERROR_NONE;
+}
+
+int mmi_standard_node_get_source_type(mmi_node_h node, mmi_standard_node_source_type_e *type) {
+    if (nullptr == type || nullptr == node) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *type = (mmi_standard_node_source_type_e)node->sub_type;
+    return MMI_ERROR_NONE;
+}
+
diff --git a/src/mmi/mmi-node.cpp b/src/mmi/mmi-node.cpp
new file mode 100644 (file)
index 0000000..1faffa9
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-node.h"
+#include "mmi-plugin-storage.h"
+
+#include "mmi-log.h"
+
+#include <new>
+
+int mmi_node_add_port(mmi_node_h node, mmi_port_h port) {
+    if (nullptr == node || nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_node_s *node_ptr = static_cast<mmi_node_s*>(node);
+
+    mmi_port_h *ports = node_ptr->ports;
+    size_t port_count = node_ptr->port_count;
+
+    node_ptr->ports = new(std::nothrow) mmi_port_h[port_count + 1];
+    if (nullptr == node_ptr->ports) {
+        node_ptr->ports = ports;
+        LOGE("[ERROR] failed to allocate memory");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+    if (port_count > 0) {
+        memcpy(node_ptr->ports, ports, sizeof(mmi_port_h) * port_count);
+        delete [] ports;
+    }
+
+    node_ptr->ports[port_count] = port;
+    node_ptr->port_count = port_count + 1;
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_find_port(mmi_node_h node, mmi_port_type_e port_type, const char *port_name, mmi_port_h *port) {
+    if (nullptr == node || nullptr == port_name || nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_link(mmi_node_h from_node, mmi_node_h to_node) {
+    if (nullptr == from_node || nullptr == to_node) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_link_by_name(mmi_node_h from_node, const char *from_port_name, mmi_node_h to_node, const char *to_port_name) {
+    if (nullptr == from_node || nullptr == from_port_name || nullptr == to_node || nullptr == to_port_name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_get_type(mmi_node_h node, mmi_standard_node_type_e *type) {
+    if (nullptr == node || nullptr == type) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *type = (mmi_standard_node_type_e)node->type;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_get_port_count(mmi_node_h node, size_t *port_count) {
+    if (nullptr == node || nullptr == port_count) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *port_count = node->port_count;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_get_port(mmi_node_h node, size_t index, mmi_port_h *port) {
+    if (nullptr == node || nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    if (index >= node->port_count) {
+        LOGE("[ERROR] index is out of range");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+    *port = node->ports[index];
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_get_callbacks(mmi_node_h node, mmi_node_callbacks *callbacks) {
+    if (nullptr == node || nullptr == callbacks) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *callbacks = node->callbacks;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_set_callbacks(mmi_node_h node, mmi_node_callbacks callbacks) {
+    if (nullptr == node) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    node->callbacks = callbacks;
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_register(mmi_node_h node) {
+    if (nullptr == node) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_standard_node_type_e node_type;
+    mmi_node_get_type(node, &node_type);
+
+    const char *identifier = mmi_plugin_storage_get_current_module_identifier();
+    if (nullptr == identifier) {
+        LOGE("[ERROR] identifier is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_plugin_module_node_list_s list = mmi_plugin_storage_get_node_list(identifier);
+
+    mmi_node_h *nodes = list.nodes;
+    size_t node_count = list.node_count;
+
+    bool duplicated = false;
+    for (size_t i = 0; i < node_count; i++) {
+        mmi_standard_node_type_e type;
+        mmi_node_get_type(nodes[i], &type);
+
+        if (type == node_type) {
+            switch (type) {
+            case MMI_STANDARD_NODE_TYPE_SOURCE: {
+                mmi_standard_node_source_type_e node_source_type;
+                mmi_standard_node_get_source_type(node, &node_source_type);
+                mmi_standard_node_source_type_e source_type;
+                mmi_standard_node_get_source_type(nodes[i], &source_type);
+                if (node_source_type == source_type) {
+                    duplicated = true;
+                }
+                break;
+            }
+            case MMI_STANDARD_NODE_TYPE_PROCESSOR: {
+                mmi_standard_node_processor_type_e node_processor_type;
+                mmi_standard_node_get_processor_type(node, &node_processor_type);
+                mmi_standard_node_processor_type_e processor_type;
+                mmi_standard_node_get_processor_type(nodes[i], &processor_type);
+                if (node_processor_type == processor_type) {
+                    duplicated = true;
+                }
+                break;
+            }
+            case MMI_STANDARD_NODE_TYPE_LOGIC: {
+                mmi_standard_node_logic_type_e node_logic_type;
+                mmi_standard_node_get_logic_type(node, &node_logic_type);
+                mmi_standard_node_logic_type_e logic_type;
+                mmi_standard_node_get_logic_type(nodes[i], &logic_type);
+                if (node_logic_type == logic_type) {
+                    duplicated = true;
+                }
+                break;
+            }
+            case MMI_STANDARD_NODE_TYPE_CONTROLLER: {
+                mmi_standard_node_controller_type_e node_controller_type;
+                mmi_standard_node_get_controller_type(node, &node_controller_type);
+                mmi_standard_node_controller_type_e controller_type;
+                mmi_standard_node_get_controller_type(nodes[i], &controller_type);
+                if (node_controller_type == controller_type) {
+                    duplicated = true;
+                }
+                break;
+            }
+            }
+        }
+        if (duplicated) {
+            mmi_node_h cloned;
+            mmi_node_clone(node, &cloned);
+            mmi_node_destroy(nodes[i]);
+            nodes[i] = cloned;
+            break;
+        }
+    }
+
+    if (!duplicated) {
+        list.nodes = new(std::nothrow) mmi_node_h[node_count + 1];
+        if (nullptr == list.nodes) {
+            LOGE("[ERROR] failed to allocate memory");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        if (node_count > 0) {
+            memcpy(list.nodes, nodes, sizeof(mmi_node_h) * node_count);
+            delete [] nodes;
+        }
+        mmi_node_h cloned;
+        mmi_node_clone(node, &cloned);
+        list.nodes[node_count] = cloned;
+        list.node_count = node_count + 1;
+    }
+
+    mmi_plugin_storage_set_node_list(list);
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_clone(mmi_node_h node, mmi_node_h *cloned) {
+    if (nullptr == node || nullptr == cloned) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_node_s *node_ptr = static_cast<mmi_node_s*>(node);
+
+    mmi_node_s *cloned_ptr = new(std::nothrow) mmi_node_s;
+    if (nullptr == cloned_ptr) {
+        LOGE("[ERROR] failed to allocate memory");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    cloned_ptr->type = node_ptr->type;
+    cloned_ptr->sub_type = node_ptr->sub_type;
+    cloned_ptr->port_count = node_ptr->port_count;
+    cloned_ptr->callbacks = node_ptr->callbacks;
+
+    cloned_ptr->ports = new(std::nothrow) mmi_port_h[node_ptr->port_count];
+    if (nullptr == cloned_ptr->ports) {
+        LOGE("[ERROR] failed to allocate memory");
+        delete cloned_ptr;
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    for (int i = 0; i < node_ptr->port_count; i++) {
+        mmi_port_clone(node_ptr->ports[i], &cloned_ptr->ports[i]);
+    }
+
+    *cloned = cloned_ptr;
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_destroy(mmi_node_h node) {
+    if (nullptr == node) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    for (int i = 0; i < node->port_count; i++) {
+        mmi_port_destroy(node->ports[i]);
+    }
+    delete [] node->ports;
+    delete node;
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_instance_set_attribute(mmi_node_instance_h instance, mmi_attribute_h attribute) {
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_instance_find_port(mmi_node_instance_h instance, mmi_port_type_e port_type, const char *port_name, mmi_port_instance_h *port) {
+    if (instance == NULL || port_name == NULL || port == NULL) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *port = mmi_plugin_storage_find_port_instance(instance, port_name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_instance_find_sibling_port(mmi_port_instance_h instance, const char *port_name, mmi_port_instance_h *sibling) {
+    if (instance == NULL || port_name == NULL || sibling == NULL) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    mmi_node_instance_h node_instance = mmi_plugin_storage_find_node_instance_by_port_instance(instance);
+    if (node_instance == NULL) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    *sibling = mmi_plugin_storage_find_port_instance(node_instance, port_name);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_instance_emit_signal(mmi_node_instance_h instance, mmi_signal_h signal) {
+    return MMI_ERROR_NONE;
+}
+
+int mmi_node_instance_update_pending_activation_result(mmi_error_e result) {
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi-plugin-storage-impl.h b/src/mmi/mmi-plugin-storage-impl.h
new file mode 100644 (file)
index 0000000..da08c77
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_PLUGIN_STORAGE_IMPL_H__
+#define __MMI_PLUGIN_STORAGE_IMPL_H__
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "mmi-log.h"
+
+class PluginStorageImpl {
+public:
+    PluginStorageImpl() {
+    }
+
+    ~PluginStorageImpl() {
+        for (auto &it : m_node_lists) {
+            for (int loop = 0; loop < it.second.node_count; loop++) {
+                mmi_node_destroy(it.second.nodes[loop]);
+            }
+            delete [] it.second.nodes;
+        }
+        for (auto &it : m_workflow_lists) {
+            for (int loop = 0; loop < it.second.workflow_count; loop++) {
+                mmi_workflow_destroy(it.second.workflows[loop]);
+            }
+            delete [] it.second.workflows;
+        }
+    }
+
+    void set_current_identifier(const char *identifier) {
+        if (identifier == nullptr) {
+            LOGE("Identifier is null");
+            return;
+        }
+        m_current_identifier = identifier;
+    }
+
+    const char* get_current_identifier() {
+        return m_current_identifier.c_str();
+    }
+
+    void set_node_list(mmi_plugin_module_node_list_s node_list) {
+        m_node_lists[m_current_identifier] = node_list;
+    }
+
+    void set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list) {
+        m_workflow_lists[m_current_identifier] = workflow_list;
+    }
+
+    mmi_plugin_module_node_list_s get_node_list(const char *identifier) {
+        return m_node_lists[identifier];
+    }
+
+    mmi_plugin_module_workflow_list_s get_workflow_list(const char *identifier) {
+        return m_workflow_lists[identifier];
+    }
+
+    void add_node_instance_info(mmi_plugin_module_node_instance_info_s *info) {
+        m_node_instance_infos.push_back(*info);
+    }
+
+    void remove_node_instance_info(mmi_node_instance_h node_instance) {
+        for (auto it = m_node_instance_infos.begin(); it != m_node_instance_infos.end(); ++it) {
+            if (it->node == node_instance) {
+                m_node_instance_infos.erase(it);
+                break;
+            }
+        }
+    }
+
+    mmi_port_instance_h find_port_instance(mmi_node_instance_h node_instance, const char *port_name) {
+        for (auto &info : m_node_instance_infos) {
+            if (info.node == node_instance) {
+                for (int loop = 0; loop < info.port_info_count; loop++) {
+                    if (strncmp(info.port_infos[loop].name, port_name, MMI_NAME_MAX_LENGTH) == 0) {
+                        return info.port_infos[loop].port;
+                    }
+                }
+            }
+        }
+        return nullptr;
+    }
+
+    mmi_node_instance_h find_node_instance_by_port_instance(mmi_port_instance_h port_instance) {
+        for (auto &info : m_node_instance_infos) {
+            for (int loop = 0; loop < info.port_info_count; loop++) {
+                if (info.port_infos[loop].port == port_instance) {
+                    return info.node;
+                }
+            }
+        }
+        return nullptr;
+    }
+private:
+    std::string m_current_identifier;
+
+    std::map<std::string, mmi_plugin_module_node_list_s> m_node_lists;
+    std::map<std::string, mmi_plugin_module_workflow_list_s> m_workflow_lists;
+
+    /* Better to use std::map since find_port_instance() would be called frequently */
+    std::vector<mmi_plugin_module_node_instance_info_s> m_node_instance_infos;
+};
+
+
+#endif /* __MMI_PLUGIN_STORAGE_IMPL_H__ */
diff --git a/src/mmi/mmi-plugin-storage.cpp b/src/mmi/mmi-plugin-storage.cpp
new file mode 100644 (file)
index 0000000..b9aa92f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include "mmi.h"
+#include "mmi-plugin-storage.h"
+#include "mmi-plugin-storage-impl.h"
+
+PluginStorageImpl g_plugin_storage_impl;
+
+void mmi_plugin_storage_set_current_module_identifier(const char *identifier) {
+    g_plugin_storage_impl.set_current_identifier(identifier);
+}
+
+const char* mmi_plugin_storage_get_current_module_identifier() {
+    return g_plugin_storage_impl.get_current_identifier();
+}
+
+void mmi_plugin_storage_set_node_list(mmi_plugin_module_node_list_s node_list) {
+    g_plugin_storage_impl.set_node_list(node_list);
+}
+
+void mmi_plugin_storage_set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list) {
+    g_plugin_storage_impl.set_workflow_list(workflow_list);
+}
+
+mmi_plugin_module_node_list_s mmi_plugin_storage_get_node_list(const char *identifier) {
+    return g_plugin_storage_impl.get_node_list(identifier);
+}
+
+mmi_plugin_module_workflow_list_s mmi_plugin_storage_get_workflow_list(const char *identifier) {
+    return g_plugin_storage_impl.get_workflow_list(identifier);
+}
+
+void mmi_plugin_storage_add_node_instance_info(mmi_plugin_module_node_instance_info_s *info) {
+    g_plugin_storage_impl.add_node_instance_info(info);
+}
+
+void mmi_plugin_storage_remove_node_instance_info(mmi_node_instance_h node_instance) {
+    g_plugin_storage_impl.remove_node_instance_info(node_instance);
+}
+
+mmi_port_instance_h mmi_plugin_storage_find_port_instance(
+    mmi_node_instance_h node_instance, const char *port_name) {
+    return g_plugin_storage_impl.find_port_instance(node_instance, port_name);
+}
+
+mmi_node_instance_h mmi_plugin_storage_find_node_instance_by_port_instance(mmi_port_instance_h port_instance) {
+    return g_plugin_storage_impl.find_node_instance_by_port_instance(port_instance);
+}
+
+static mmi_plugin_module_event_handler_info_s g_event_handler_info{nullptr, };
+static void* g_event_handler_user_data = nullptr;
+
+void mmi_plugin_storage_set_plugin_module_event_handler(
+    mmi_plugin_module_event_handler_info_s handler_info, void *user_data) {
+    g_event_handler_info = handler_info;
+    g_event_handler_user_data = user_data;
+}
+
+void mmi_plugin_storage_unset_plugin_module_event_handler() {
+    memset(&g_event_handler_info, 0x00, sizeof(g_event_handler_info));
+    g_event_handler_user_data = nullptr;
+}
+
+mmi_plugin_module_event_handler_info_s mmi_plugin_storage_get_plugin_module_event_handler() {
+    return g_event_handler_info;
+}
+
+void* mmi_plugin_storage_get_plugin_module_event_handler_user_data() {
+    return g_event_handler_user_data;
+}
diff --git a/src/mmi/mmi-port.cpp b/src/mmi/mmi-port.cpp
new file mode 100644 (file)
index 0000000..2358a9e
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-log.h"
+#include "mmi-port.h"
+#include "mmi-plugin-storage.h"
+
+#include <new>
+#include <cstdio>
+
+MMI_API int mmi_port_create(mmi_port_h *port) {
+    if (nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = new (std::nothrow) mmi_port_s;
+    if (nullptr == port_s) {
+        LOGE("[ERROR] failed to create port");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+    memset(port_s, 0, sizeof(mmi_port_s));
+
+    *port = port_s;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_get_name(mmi_port_h port, char **name, size_t *length) {
+    if (nullptr == port || nullptr == name || nullptr == length) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    *name = port_s->name;
+    *length = strlen(*name);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_get_type(mmi_port_h port, mmi_port_type_e *type) {
+    if (nullptr == port || nullptr == type) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    *type = port_s->type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_get_data_type(mmi_port_h port, mmi_data_type_e *data_type) {
+    if (nullptr == port || nullptr == data_type) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    *data_type = port_s->data_type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_get_callbacks(mmi_port_h port, mmi_port_callbacks *callbacks) {
+    if (nullptr == port || nullptr == callbacks) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    *callbacks = port_s->callbacks;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_set_name(mmi_port_h port, const char *name) {
+    if (nullptr == port || nullptr == name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    std::snprintf(port_s->name, MMI_NAME_MAX_LENGTH, "%s", name);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_set_type(mmi_port_h port, mmi_port_type_e type) {
+    if (nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    port_s->type = type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_set_data_type(mmi_port_h port, mmi_data_type_e data_type) {
+    if (nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    port_s->data_type = data_type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_set_callbacks(mmi_port_h port, mmi_port_callbacks callbacks) {
+    if (nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    port_s->callbacks = callbacks;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_clone(mmi_port_h port, mmi_port_h *cloned) {
+    if (nullptr == port || nullptr == cloned) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_port_s *port_s = static_cast<mmi_port_s*>(port);
+
+    *cloned = new (std::nothrow) mmi_port_s;
+    if (nullptr == *cloned) {
+        LOGE("[ERROR] failed to clone port");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    strncpy((*cloned)->name, port_s->name, MMI_NAME_MAX_LENGTH);
+    (*cloned)->type = port_s->type;
+    (*cloned)->data_type = port_s->data_type;
+    (*cloned)->callbacks = port_s->callbacks;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_destroy(mmi_port_h port) {
+    if (nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    delete port;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_instance_generate_output(mmi_port_instance_h instance, mmi_data_h data) {
+    mmi_plugin_module_event_handler_info_s event_handler_info = mmi_plugin_storage_get_plugin_module_event_handler();
+    void *user_data = mmi_plugin_storage_get_plugin_module_event_handler_user_data();
+
+    mmi_plugin_module_port_instance_output_handler_func output_handler = event_handler_info.port_instance_output_handler;
+    if (output_handler) {
+        output_handler(instance, data, user_data);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_instance_request_auto_transform(mmi_port_instance_h instance, const char *from_format, const char *to_format) {
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_instance_request_input_data_format(mmi_port_instance_h instance, const char *format) {
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_port_instance_announce_output_data_format(mmi_port_instance_h instance, const char *format) {
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi-primitive-value.cpp b/src/mmi/mmi-primitive-value.cpp
new file mode 100644 (file)
index 0000000..529086b
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <mmi-error.h>
+#include <mmi-primitive-value.h>
+
+#include "mmi-log.h"
+
+
+struct mmi_primitive_value_s {
+    mmi_primitive_value_type_e type;
+    void *data;
+    size_t datalen;
+};
+
+static const size_t g_unit_size = sizeof(mmi_primitive_value_h);
+
+
+static inline size_t get_array_count(mmi_primitive_value_h array) {
+    if (nullptr != array) {
+        return array->datalen / g_unit_size;
+    }
+
+    return 0;
+}
+
+static inline mmi_primitive_value_h *get_element_pointer_in_array(mmi_primitive_value_h array, size_t index) {
+    return reinterpret_cast<mmi_primitive_value_h *>(array->data) + index;
+}
+
+int mmi_primitive_value_create_int(int data, mmi_primitive_value_h *handle) {
+    if (nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto primitive_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    if (nullptr == primitive_value) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_INT;
+    primitive_value->data = reinterpret_cast<void *>(malloc(sizeof(int)));
+    if (nullptr == primitive_value->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(primitive_value);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<int *>(primitive_value->data)) = data;
+    primitive_value->datalen = sizeof(int);
+
+    *handle = primitive_value;
+
+    _D("[DEBUG] Success to create for int(%d)", data);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_create_float(float data, mmi_primitive_value_h *handle) {
+    if (nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto primitive_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    if (nullptr == primitive_value) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_FLOAT;
+    primitive_value->data = reinterpret_cast<void *>(malloc(sizeof(float)));
+    if (nullptr == primitive_value->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(primitive_value);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<float *>(primitive_value->data)) = data;
+    primitive_value->datalen = sizeof(float);
+
+    *handle = primitive_value;
+
+    _D("[DEBUG] Success to create for float(%f)", data);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_create_string(const char *data, mmi_primitive_value_h *handle) {
+    if (nullptr == handle || nullptr == data) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto primitive_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    if (nullptr == primitive_value) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_STRING;
+    primitive_value->data = reinterpret_cast<void *>(strdup(data));
+    primitive_value->datalen = strlen(data) + 1; /* including null character */
+
+    *handle = primitive_value;
+
+    _D("[DEBUG] Success to create for string(%s)", data);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_create_bool(bool data, mmi_primitive_value_h *handle) {
+    if (nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto primitive_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    if (nullptr == primitive_value) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    primitive_value->data = reinterpret_cast<void *>(malloc(sizeof(int)));
+    if (nullptr == primitive_value->data) {
+        _E("[ERROR] Fail to allocate memory for raw data");
+        free(primitive_value);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    *(reinterpret_cast<int *>(primitive_value->data)) = static_cast<int>(data);
+    primitive_value->datalen = sizeof(int);
+
+    *handle = primitive_value;
+
+    _D("[DEBUG] Success to create for boolean(%s)", data ? "true" : "false");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_create_array(mmi_primitive_value_h *handle) {
+    if (nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto primitive_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    if (nullptr == primitive_value) {
+        _E("[ERROR] Fail to allocate memory for primitive value");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+    primitive_value->data = nullptr;
+    primitive_value->datalen = 0;
+
+    *handle = primitive_value;
+
+    _D("[DEBUG] Success to create empty array");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_add_array_element(mmi_primitive_value_h array, mmi_primitive_value_h element) {
+    if (nullptr == array || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_array_count(array);
+    if (count > 0) {
+        mmi_primitive_value_h first = nullptr;
+        mmi_primitive_value_get_array_element(array, 0, &first);
+
+        if (first->type != element->type) {
+            _E("[ERROR] Element type is not matched");
+            return MMI_ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    auto array_data = reinterpret_cast<mmi_primitive_value_h *>(array->data);
+    array_data = reinterpret_cast<mmi_primitive_value_h *>(realloc(array_data, g_unit_size * (count + 1)));
+    if (array_data) {
+        array_data[count] = element;
+
+        array->data = array_data;
+        array->datalen = array->datalen + g_unit_size;
+    } else {
+        _E("[ERROR] Fail to allocate memory for array element");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    _D("[DEBUG] Success to add element(%zu)", get_array_count(array));
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_type(mmi_primitive_value_h handle, mmi_primitive_value_type_e *type) {
+    if (nullptr == handle || nullptr == type) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *type = handle->type;
+
+    _D("[DEBUG] Success to get type(%d)", *type);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_int(mmi_primitive_value_h handle, int *value) {
+    if (nullptr == handle || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_INT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *value = *(reinterpret_cast<int *>(handle->data));
+
+    _D("[DEBUG] Success to get value: int(%d)", *value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_float(mmi_primitive_value_h handle, float *value) {
+    if (nullptr == handle || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_FLOAT) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *value = *(reinterpret_cast<float *>(handle->data));
+
+    _D("[DEBUG] Success to get value: float(%f)", *value);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_string(mmi_primitive_value_h handle, const char **string) {
+    if (nullptr == handle || nullptr == string) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_STRING) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *string = reinterpret_cast<char *>(handle->data);
+
+    _D("[DEBUG] Success to get value: string(%s)", *string);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_bool(mmi_primitive_value_h handle, bool *value) {
+    if (nullptr == handle || nullptr == value) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_BOOL) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    int data = *(reinterpret_cast<int *>(handle->data));
+    *value = (data != 0);
+
+    _D("[DEBUG] Success to get value: boolean(%s)", *value ? "true" : "false");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_array_count(mmi_primitive_value_h array, size_t *count) {
+    if (nullptr == array || nullptr == count) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *count = get_array_count(array);
+
+    _D("[DEBUG] Success to get array count(%zu)", *count);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_get_array_element(mmi_primitive_value_h array, size_t index, mmi_primitive_value_h *element) {
+    if (nullptr == array || nullptr == element) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        _E("[ERROR] Data type is not matched");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    size_t count = get_array_count(array);
+    if (index >= count) {
+        _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count);
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    *element = *(get_element_pointer_in_array(array, index));
+
+    _D("[DEBUG] Success to get array element on index(%zu)", index);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_clone(mmi_primitive_value_h handle, mmi_primitive_value_h *cloned) {
+    if (nullptr == handle || nullptr == cloned) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    auto cloned_value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+    cloned_value->type = handle->type;
+    cloned_value->datalen = handle->datalen;
+    cloned_value->data = calloc(1, handle->datalen);
+    if (nullptr == cloned_value->data) {
+        _E("[ERROR] Fail to allocate memory for data");
+        free(cloned_value);
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (handle->type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        const size_t count = get_array_count(handle);
+        for (size_t i = 0; i < count; i++) {
+            auto element = get_element_pointer_in_array(handle, i);
+            if (nullptr == element) {
+                _E("[ERROR] Fail to get element");
+                mmi_primitive_value_destroy(cloned_value);
+                return MMI_ERROR_OPERATION_FAILED;
+            }
+
+            mmi_primitive_value_clone(*element, get_element_pointer_in_array(cloned_value, i));
+        }
+    } else {
+        memcpy(cloned_value->data, handle->data, handle->datalen);
+    }
+
+    *cloned = cloned_value;
+
+    _D("[DEBUG] Success to clone data. type(%d)", cloned_value->type);
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_destroy(mmi_primitive_value_h handle) {
+    if (nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (handle->type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        size_t count = get_array_count(handle);
+        for (size_t i = 0; i < count; i++) {
+            auto element = get_element_pointer_in_array(handle, i);
+            mmi_primitive_value_destroy(*element);
+        }
+    }
+
+    if (handle->data != NULL) {
+        free(handle->data);
+    }
+
+    free(handle);
+
+    _D("[DEBUG] Success to destroy");
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_to_bytes(mmi_primitive_value_h handle, unsigned char **bytes, size_t *size)
+{
+    if (nullptr == handle || nullptr == bytes || nullptr == size) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_primitive_value_type_e type;
+    mmi_primitive_value_get_type(handle, &type);
+    if (type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        size_t current_size = sizeof(mmi_primitive_value_type_e) + sizeof(size_t);
+        unsigned char *buffer = reinterpret_cast<unsigned char *>(malloc(current_size));
+        size_t count = get_array_count(handle);
+        unsigned char *current = buffer;
+        memcpy(current, &type, sizeof(mmi_primitive_value_type_e));
+        current += sizeof(mmi_primitive_value_type_e);
+        /* This size will be updated after getting element bytes */
+        memcpy(current, &count, sizeof(size_t));
+        current += sizeof(size_t);
+        for (size_t i = 0; i < count; i++) {
+            auto element = get_element_pointer_in_array(handle, i);
+            unsigned char *element_bytes = nullptr;
+            size_t element_size = 0;
+            mmi_primitive_value_to_bytes(*element, &element_bytes, &element_size);
+            buffer = reinterpret_cast<unsigned char *>(realloc(buffer, current_size + element_size + sizeof(size_t)));
+            if (nullptr == buffer) {
+                _E("[ERROR] Fail to allocate memory for buffer");
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+            current = buffer + current_size;
+            memcpy(current, &element_size, sizeof(size_t));
+            current += sizeof(size_t);
+            current_size += sizeof(size_t);
+            memcpy(current, element_bytes, element_size);
+            current_size += element_size;
+            free(element_bytes);
+        }
+        size_t total_element_size = current_size - sizeof(mmi_primitive_value_type_e) - sizeof(size_t);
+        memcpy(buffer + sizeof(mmi_primitive_value_type_e), &total_element_size, sizeof(size_t));
+
+        *bytes = buffer;
+        *size = current_size;
+    } else {
+        size_t total_size = sizeof(mmi_primitive_value_type_e) + sizeof(size_t) + handle->datalen;
+        unsigned char *buffer = reinterpret_cast<unsigned char *>(malloc(total_size));
+        if (nullptr == buffer) {
+            _E("[ERROR] Fail to allocate memory for buffer");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        unsigned char *current = buffer;
+        memcpy(current, &(handle->type), sizeof(mmi_primitive_value_type_e));
+        current += sizeof(mmi_primitive_value_type_e);
+        memcpy(current, &(handle->datalen), sizeof(size_t));
+        current += sizeof(size_t);
+        memcpy(current, handle->data, handle->datalen);
+
+        *bytes = buffer;
+        *size = total_size;
+    }
+
+    _D("[DEBUG] Success to convert to bytes");
+
+    return MMI_ERROR_NONE;
+}
+
+int mmi_primitive_value_from_bytes(unsigned char *bytes, size_t size, mmi_primitive_value_h *handle)
+{
+    if (nullptr == bytes || nullptr == handle) {
+        _E("[ERROR] Some parameters are invalid");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    unsigned char *current = bytes;
+    mmi_primitive_value_type_e type;
+    memcpy(&type, current, sizeof(mmi_primitive_value_type_e));
+    current += sizeof(mmi_primitive_value_type_e);
+    if (type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) {
+        size_t datalen;
+        /* This datalen variable will not be used for array type */
+        memcpy(&datalen, current, sizeof(size_t));
+        current += sizeof(size_t);
+        void *data = nullptr;
+        auto value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+        value->type = type;
+        value->datalen = 0;
+        value->data = nullptr;
+
+        size_t num_elements = 0;
+        while (current < bytes + size) {
+            bool failed = false;
+            mmi_primitive_value_h element = nullptr;
+
+            size_t element_size;
+            memcpy(&element_size, current, sizeof(size_t));
+            current += sizeof(size_t);
+
+            unsigned char *element_bytes = reinterpret_cast<unsigned char *>(malloc(element_size));
+            if (element_bytes) {
+                memcpy(element_bytes, current, element_size);
+                current += element_size;
+                mmi_primitive_value_from_bytes(element_bytes, element_size, &element);
+                free(element_bytes);
+            } else {
+                _E("[ERROR] Fail to allocate memory for element_bytes");
+                failed = true;
+            }
+            if (nullptr == element) {
+                _E("[ERROR] Fail to convert element from bytes");
+                failed = true;
+            }
+
+            if (failed) {
+                /* Since we are in a while loop, value->data should also be freed */
+                size_t num_elements = value->datalen / sizeof(mmi_primitive_value_h);
+                for (size_t i = 0; i < num_elements; i++) {
+                    auto element = get_element_pointer_in_array(value, i);
+                    mmi_primitive_value_destroy(*element);
+                }
+                free(value->data);
+                free(value);
+                return MMI_ERROR_OUT_OF_MEMORY;
+            }
+
+            mmi_primitive_value_add_array_element(value, element);
+            num_elements++;
+        }
+
+        *handle = value;
+    } else {
+        size_t datalen;
+        memcpy(&datalen, current, sizeof(size_t));
+        current += sizeof(size_t);
+        void *data = calloc(1, datalen);
+        if (nullptr == data) {
+            _E("[ERROR] Fail to allocate memory for data");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        memcpy(data, current, datalen);
+
+        auto value = reinterpret_cast<mmi_primitive_value_s *>(malloc(sizeof(mmi_primitive_value_s)));
+        value->type = type;
+        value->datalen = datalen;
+        value->data = data;
+
+        *handle = value;
+    }
+
+    _D("[DEBUG] Success to convert from bytes. type(%d), size(%zu)", type, size);
+
+    return MMI_ERROR_NONE;
+}
+
diff --git a/src/mmi/mmi-signal.cpp b/src/mmi/mmi-signal.cpp
new file mode 100644 (file)
index 0000000..1d2c97c
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-signal.h"
+
+MMI_API int mmi_signal_parameter_create(mmi_primitive_value_h value, const char *name,  mmi_signal_parameter_h *signal_parameter) {
+    if (signal_parameter == NULL) {
+        return -1;
+    }
+
+    *signal_parameter = (mmi_signal_parameter_s*)calloc(1, sizeof(mmi_signal_parameter_s));
+    if (*signal_parameter == NULL) return -1;
+
+    strncpy((*signal_parameter)->name, name, MMI_NAME_MAX_LENGTH - 1);
+    (*signal_parameter)->name[MMI_NAME_MAX_LENGTH - 1] = '\0';
+    mmi_primitive_value_clone(value, &((*signal_parameter)->value));
+
+    return 0;
+}
+
+MMI_API int mmi_signal_parameter_get_name(mmi_signal_parameter_h signal_parameter, char **name) {
+    if (signal_parameter == NULL || name == NULL) {
+        return -1;
+    }
+    *name = strdup(signal_parameter->name);
+    return 0;
+}
+
+MMI_API int mmi_signal_parameter_get_value(mmi_signal_parameter_h signal_parameter, mmi_primitive_value_h *value) {
+    if (signal_parameter == NULL || value == NULL) {
+        return -1;
+    }
+    mmi_primitive_value_clone(signal_parameter->value, value);
+    return 0;
+}
+
+MMI_API int mmi_signal_parameter_clone(mmi_signal_parameter_h signal_parameter, mmi_signal_parameter_h *cloned) {
+    if (cloned == NULL) {
+        return -1;
+    }
+
+    *cloned = (mmi_signal_parameter_s*)calloc(1, sizeof(mmi_signal_parameter_s));
+    if (*cloned == NULL) return -1;
+
+    strncpy((*cloned)->name, signal_parameter->name, MMI_NAME_MAX_LENGTH - 1);
+    (*cloned)->name[MMI_NAME_MAX_LENGTH - 1] = '\0';
+    mmi_primitive_value_clone(signal_parameter->value, &((*cloned)->value));
+
+    return 0;
+}
+
+MMI_API int mmi_signal_parameter_destroy(mmi_signal_parameter_h signal_parameter) {
+    if (signal_parameter == NULL) {
+        return -1;
+    }
+    mmi_primitive_value_destroy(signal_parameter->value);
+    free(signal_parameter);
+    return 0;
+}
+
+MMI_API int mmi_signal_create(const char *name, mmi_signal_h *handle) {
+    if (handle == NULL) {
+        return -1;
+    }
+
+    *handle = (mmi_signal_s*)calloc(1, sizeof(mmi_signal_s));
+    if (*handle == NULL) return -1;
+
+    strncpy((*handle)->name, name, MMI_NAME_MAX_LENGTH - 1);
+    (*handle)->name[MMI_NAME_MAX_LENGTH - 1] = '\0';
+
+    return 0;
+}
+
+MMI_API int mmi_signal_add_parameter(mmi_signal_h handle, mmi_signal_parameter_h parameter) {
+    if (handle == NULL || parameter == NULL) {
+        return -1;
+    }
+
+    if (handle->parameter_count >= MMI_PARAMETER_MAX_COUNT) {
+        return -1;
+    }
+
+    mmi_signal_parameter_clone(parameter, &handle->parameters[handle->parameter_count]);
+    handle->parameter_count++;
+
+    return 0;
+}
+
+MMI_API int mmi_signal_get_name(mmi_signal_h handle, char **name) {
+    if (handle == NULL || name == NULL) {
+        return -1;
+    }
+    *name = handle->name;
+    return 0;
+}
+
+MMI_API int mmi_signal_get_parameter_count(mmi_signal_h handle, int *count) {
+    if (handle == NULL || count == NULL) {
+        return -1;
+    }
+    *count = handle->parameter_count;
+    return 0;
+}
+
+MMI_API int mmi_signal_get_parameter(mmi_signal_h handle, int index, mmi_signal_parameter_h *parameter) {
+    if (handle == NULL || parameter == NULL) {
+        return -1;
+    }
+
+    if (index < 0 || index >= handle->parameter_count) {
+        return -1;
+    }
+
+    mmi_signal_parameter_clone(handle->parameters[index], parameter);
+
+    return 0;
+}
+
+MMI_API int mmi_signal_destroy(mmi_signal_h handle) {
+    if (handle == NULL) {
+        return -1;
+    }
+
+    for (int i = 0; i < handle->parameter_count; i++) {
+        mmi_signal_parameter_destroy(handle->parameters[i]);
+    }
+
+    free(handle);
+    return 0;
+}
diff --git a/src/mmi/mmi-workflow-instance-manager.h b/src/mmi/mmi-workflow-instance-manager.h
new file mode 100644 (file)
index 0000000..ae6ff6e
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#ifndef __MMI_WORKFLOW_INSTANCE_MANAGER_H__
+#define __MMI_WORKFLOW_INSTANCE_MANAGER_H__
+
+#include "mmi.h"
+
+#include <map>
+#include <memory>
+
+namespace mmi {
+
+class WorkflowInstance {
+public:
+    WorkflowInstance(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type)
+        : m_local_workflow_instance_id(local_workflow_instance_id), m_workflow_type(workflow_type) {
+    }
+    virtual ~WorkflowInstance() {}
+
+    bool initialize() {
+        return true;
+    }
+
+    bool deinitialize() {
+        return true;
+    }
+
+    int get_local_workflow_instance_id() {
+        return m_local_workflow_instance_id;
+    }
+
+    mmi_standard_workflow_type_e get_workflow_type() {
+        return m_workflow_type;
+    }
+
+    bool add_output_callback(std::string key, mmi_workflow_output_cb callback, void *user_data) {
+        bool ret = false;
+        if (callback) {
+            callbacks[key] = std::make_pair(callback, user_data);
+            ret = true;
+        }
+        return ret;
+    }
+
+    bool call_output_callback(std::string key, mmi_data_h data) {
+        bool ret = false;
+        if (callbacks.find(key) != callbacks.end()) {
+            auto callback = callbacks[key];
+            callback.first(static_cast<mmi_workflow_instance_h>(this), key.c_str(), data, callback.second);
+            ret = true;
+        }
+        return ret;
+    }
+
+private:
+    int m_local_workflow_instance_id;
+    mmi_standard_workflow_type_e m_workflow_type;
+    // TODO: make duplicate possible
+    std::map<std::string, std::pair<mmi_workflow_output_cb, void *>> callbacks;
+};
+
+class WorkflowInstanceManager {
+public:
+    WorkflowInstanceManager() {}
+    virtual ~WorkflowInstanceManager() {}
+
+    bool initialize() {
+        return true;
+    }
+    bool deinitialize() {
+        for (auto instance : m_instances) {
+            instance->deinitialize();
+            delete instance;
+        }
+        m_instances.clear();
+        return true;
+    }
+
+    WorkflowInstance* create(mmi_standard_workflow_type_e type) {
+        WorkflowInstance *newInstance = new(std::nothrow) WorkflowInstance(m_last_local_workflow_instance_id++, type);
+        m_instances.push_back(newInstance);
+        return newInstance;
+    }
+
+    bool destroy(WorkflowInstance *instance) {
+        bool ret = false;
+        auto it = std::find(m_instances.begin(), m_instances.end(), instance);
+        if (it != m_instances.end()) {
+            m_instances.erase(it);
+            instance->deinitialize();
+            delete instance;
+            ret = true;
+        }
+        return ret;
+    }
+
+private:
+    int m_last_local_workflow_instance_id{0};
+    std::vector<WorkflowInstance*> m_instances;
+};
+
+} // namespace mmi
+//
+#endif //__MMI_WORKFLOW_INSTANCE_MANAGER_H__
diff --git a/src/mmi/mmi-workflow-instance.cpp b/src/mmi/mmi-workflow-instance.cpp
new file mode 100644 (file)
index 0000000..74d5844
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <algorithm>
+#include <vector>
+#include <map>
+#include <string>
+
+#include <Ecore.h>
+
+#include "mmi.h"
+#include "mmi-plugin-storage.h"
+
+#include "mmi-client-manager.h"
+#include "mmi-log.h"
+#include "mmi-workflow-instance-manager.h"
+#include "mmi_proxy.h"
+
+using namespace mmi;
+using namespace mmi::communication;
+
+extern ClientManager g_mmi_client_manager;
+
+MMI_API int mmi_standard_workflow_instance_create(mmi_standard_workflow_type_e type, mmi_workflow_instance_h *instance) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    WorkflowInstanceManager *workflow_instance_manager = g_mmi_client_manager.get_workflow_instance_manager();
+    if (nullptr == workflow_instance_manager) {
+        LOGE("[ERROR] Failed to get workflow instance manager");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    WorkflowInstance *workflow_instance = workflow_instance_manager->create(type);
+    if (workflow_instance) {
+        *instance = static_cast<void*>(workflow_instance);
+
+        CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel();
+        if (channel) {
+            ClientMessageWorkflowInstanceCreate msg;
+            msg.local_workflow_instance_id =
+                workflow_instance->get_local_workflow_instance_id();
+            msg.workflow_type = type;
+            msg.user_data = workflow_instance;
+
+            channel->send(&msg);
+        }
+    } else {
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_instance_destroy(mmi_workflow_instance_h instance) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    WorkflowInstanceManager *workflow_instance_manager = g_mmi_client_manager.get_workflow_instance_manager();
+    if (nullptr == workflow_instance_manager) {
+        LOGE("[ERROR] Failed to get workflow instance manager");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel();
+    if (channel) {
+        ClientMessageWorkflowInstanceDestroy msg;
+        WorkflowInstance *workflow_instance = static_cast<WorkflowInstance*>(instance);
+        msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id();
+
+        channel->send(&msg);
+
+        workflow_instance_manager->destroy(workflow_instance);
+    }
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_instance_activate(mmi_workflow_instance_h instance) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel();
+    if (nullptr == channel) {
+        LOGE("[ERROR] Failed to get channel");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    WorkflowInstance *workflow_instance = static_cast<WorkflowInstance*>(instance);
+
+    ClientMessageWorkflowInstanceActivate msg;
+    msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id();
+
+    channel->send(&msg);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_instance_deactivate(mmi_workflow_instance_h instance) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel();
+    if (nullptr == channel) {
+        LOGE("[ERROR] Failed to get channel");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    WorkflowInstance *workflow_instance = static_cast<WorkflowInstance*>(instance);
+
+    ClientMessageWorkflowInstanceDeactivate msg;
+    msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id();
+
+    channel->send(&msg);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_instance_set_attribute(mmi_workflow_instance_h instance, mmi_attribute_h attribute) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel();
+    if (nullptr == channel) {
+        LOGE("[ERROR] Failed to get channel");
+        return MMI_ERROR_OPERATION_FAILED;
+    }
+
+    WorkflowInstance *workflow_instance = static_cast<WorkflowInstance*>(instance);
+
+    ClientMessageWorkflowInstanceSetAttribute msg;
+    msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id();
+    msg.attribute = attribute;
+
+    channel->send(&msg);
+
+    return MMI_ERROR_NONE;
+}
+
+
+MMI_API int mmi_workflow_instance_set_output_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_output_cb callback, void *user_data) {
+    if (nullptr == instance) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (nullptr == name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    if (nullptr == callback) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    WorkflowInstance *workflow_instance = static_cast<WorkflowInstance*>(instance);
+    workflow_instance->add_output_callback(std::string{name}, callback, user_data);
+
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi-workflow-script-parser.h b/src/mmi/mmi-workflow-script-parser.h
new file mode 100644 (file)
index 0000000..d2e035d
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#ifndef __MMI_WORKFLOW_SCRIPT_PARSER_H__
+#define __MMI_WORKFLOW_SCRIPT_PARSER_H__
+
+#include <string>
+#include <regex>
+#include <vector>
+
+#include "mmi-log.h"
+#include "mmi-node-source.h"
+#include "mmi-node-processor.h"
+#include "mmi-node-logic.h"
+#include "mmi-node-controller.h"
+
+enum class WorkflowScriptSection {
+    NONE,
+    WORKFLOW_INFO,
+    NODE_LIST,
+    LINK_LIST,
+    ATTRIBUTE_LIST,
+    OUTPUT_LIST,
+};
+
+typedef struct {
+    WorkflowScriptSection section;
+    std::regex pattern;
+} WorkflowScriptSectionPattern;
+
+typedef struct {
+    WorkflowScriptSection section;
+    std::string name;
+} WorkflowScriptSectionName;
+
+typedef struct {
+    mmi_standard_workflow_type_e type;
+    std::string name;
+} StandardWorkflowName;
+
+typedef struct {
+    mmi_standard_node_source_type_e type;
+    std::string name;
+} StandardNodeSourceName;
+
+typedef struct {
+    mmi_standard_node_processor_type_e type;
+    std::string name;
+} StandardNodeProcessorName;
+
+typedef struct {
+    mmi_standard_node_logic_type_e type;
+    std::string name;
+} StandardNodeLogicName;
+
+typedef struct {
+    mmi_standard_node_controller_type_e type;
+    std::string name;
+} StandardNodeControllerName;
+
+// trim from start (in place)
+static inline void ltrim(std::string &s) {
+    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
+        return !std::isspace(ch);
+    }));
+}
+
+// trim from end (in place)
+static inline void rtrim(std::string &s) {
+    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
+        return !std::isspace(ch);
+    }).base(), s.end());
+}
+
+// trim from both ends (in place)
+static inline void trim(std::string &s) {
+    rtrim(s);
+    ltrim(s);
+}
+
+class WorkflowScriptParser {
+public:
+    WorkflowScriptParser() {
+        m_section_names.push_back({WorkflowScriptSection::WORKFLOW_INFO, "@workflow"});
+        m_section_names.push_back({WorkflowScriptSection::NODE_LIST, "@node-list"});
+        m_section_names.push_back({WorkflowScriptSection::LINK_LIST, "@link-list"});
+        m_section_names.push_back({WorkflowScriptSection::ATTRIBUTE_LIST, "@attribute-list"});
+        m_section_names.push_back({WorkflowScriptSection::OUTPUT_LIST, "@output-list"});
+
+        m_section_patterns.push_back({WorkflowScriptSection::WORKFLOW_INFO, std::regex("^name : ([a-zA-Z_]+)$")});
+        m_section_patterns.push_back({WorkflowScriptSection::NODE_LIST, std::regex("^\\[([a-zA-Z]+)\\] ([a-zA-Z_]+) as ([a-zA-Z0-9_]+)$")});
+        m_section_patterns.push_back({WorkflowScriptSection::LINK_LIST, std::regex("^([a-zA-Z0-9_.]+) -> ([a-zA-Z0-9_.]+)$")});
+        m_section_patterns.push_back({WorkflowScriptSection::ATTRIBUTE_LIST, std::regex("^([a-zA-Z0-9_.]+) as ([a-zA-Z0-9_]+)$")});
+        m_section_patterns.push_back({WorkflowScriptSection::OUTPUT_LIST, std::regex("^([a-zA-Z0-9_.]+) as ([a-zA-Z0-9_]+)$")});
+
+        m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, "WAKEUPLESS_COMMAND"});
+        m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_VOICE_TOUCH, "VOICE_TOUCH"});
+        m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_USER_RECOGNITION, "USER_RECOGNITION"});
+
+        m_standard_node_source_names.push_back({MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, "MIC_AMBIENT"});
+        m_standard_node_source_names.push_back({MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA, "CAMERA"});
+
+        m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, "ASR"});
+        m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION, "FACE_RECOGNITION"});
+        m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_SPEAKER_RECOGNITION, "SPEAKER_RECOGNITION"});
+
+        m_standard_node_logic_names.push_back({MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, "FIXED_STRING_MATCH"});
+        m_standard_node_logic_names.push_back({MMI_STANDARD_NODE_LOGIC_TYPE_USER_COMPARISON, "USER_COMPARISON"});
+
+        m_standard_node_controller_names.push_back({MMI_STANDARD_NODE_CONTROLLER_TYPE_SWITCH, "SWITCH"});
+    }
+    virtual ~WorkflowScriptParser() {
+    }
+
+    bool process_section_declaration(const std::string &line) {
+        for (auto &section_name : m_section_names) {
+            if (line == section_name.name) {
+                m_current_section = section_name.section;
+                LOGD("section changed to %d, [%s]", static_cast<int>(m_current_section), line.c_str());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool process_workflow_info(std::smatch matches, mmi_workflow_h workflow) {
+        if (matches.size() == 2) {
+            std::ssub_match sub_match = matches[1];
+            std::string name = sub_match.str();
+            for (auto &standard_workflow_name : m_standard_workflow_names) {
+                if (name == standard_workflow_name.name) {
+                    LOGD("standard workflow name matched: %s", name.c_str());
+                    mmi_standard_workflow_type_e type = standard_workflow_name.type;
+                    if (mmi_workflow_set_type(workflow, type) != MMI_ERROR_NONE) {
+                        LOGE("[ERROR] Failed to set standard workflow type");
+                        return false;
+                    }
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+    bool process_node_section(std::smatch matches, mmi_workflow_h workflow) {
+        if (matches.size() == 4) {
+            std::ssub_match sub_match_type = matches[1];
+            std::string type = sub_match_type.str();
+
+            if (type.compare("Source") == 0) {
+                std::ssub_match sub_match_name = matches[2];
+                std::string name = sub_match_name.str();
+
+                std::ssub_match sub_match_alias = matches[3];
+                std::string alias = sub_match_alias.str();
+
+                for (auto &standard_node_source_name : m_standard_node_source_names) {
+                    if (name == standard_node_source_name.name) {
+                        LOGD("standard node source name matched: %s", name.c_str());
+                        mmi_standard_node_source_type_e sub_type = standard_node_source_name.type;
+                        mmi_node_h node = nullptr;
+                        if (mmi_standard_node_create_source(sub_type, &node) != MMI_ERROR_NONE) {
+                            LOGE("[ERROR] Failed to create standard node source");
+                            return false;
+                        }
+                        LOGD("standard node source created: %p", node);
+                        mmi_workflow_node_add(workflow, alias.c_str(), node);
+                        break;
+                    }
+                }
+            } else if (type.compare("Processor") == 0) {
+                std::ssub_match sub_match_name = matches[2];
+                std::string name = sub_match_name.str();
+
+                std::ssub_match sub_match_alias = matches[3];
+                std::string alias = sub_match_alias.str();
+
+                for (auto &standard_node_processor_name : m_standard_node_processor_names) {
+                    if (name == standard_node_processor_name.name) {
+                        LOGD("standard node processor name matched: %s", name.c_str());
+                        mmi_standard_node_processor_type_e sub_type = standard_node_processor_name.type;
+                        mmi_node_h node = nullptr;
+                        if (mmi_standard_node_create_processor(sub_type, &node) != MMI_ERROR_NONE) {
+                            LOGE("[ERROR] Failed to create standard node processor");
+                            return false;
+                        }
+                        LOGD("Adding processor node %s as %s", name.c_str(), alias.c_str());
+                        mmi_workflow_node_add(workflow, alias.c_str(), node);
+                        mmi_node_destroy(node);
+                        break;
+                    }
+                }
+            } else if (type.compare("Logic") == 0) {
+                std::ssub_match sub_match_name = matches[2];
+                std::string name = sub_match_name.str();
+
+                std::ssub_match sub_match_alias = matches[3];
+                std::string alias = sub_match_alias.str();
+
+                for (auto &standard_node_logic_name : m_standard_node_logic_names) {
+                    if (name == standard_node_logic_name.name) {
+                        LOGD("standard node logic name matched: %s", name.c_str());
+                        mmi_standard_node_logic_type_e sub_type = standard_node_logic_name.type;
+                        mmi_node_h node = nullptr;
+                        if (mmi_standard_node_create_logic(sub_type, &node) != MMI_ERROR_NONE) {
+                            LOGE("[ERROR] Failed to create standard node logic");
+                            return false;
+                        }
+                        LOGD("Adding logic node %s as %s", name.c_str(), alias.c_str());
+                        mmi_workflow_node_add(workflow, alias.c_str(), node);
+                        mmi_node_destroy(node);
+                        break;
+                    }
+                }
+            } else if (type.compare("Controller") == 0) {
+                std::ssub_match sub_match_name = matches[2];
+                std::string name = sub_match_name.str();
+
+                std::ssub_match sub_match_alias = matches[3];
+                std::string alias = sub_match_alias.str();
+
+                for (auto &standard_node_controller_name : m_standard_node_controller_names) {
+                    if (name == standard_node_controller_name.name) {
+                        LOGD("standard node controller name matched: %s", name.c_str());
+                        mmi_standard_node_controller_type_e sub_type = standard_node_controller_name.type;
+                        mmi_node_h node = nullptr;
+                        if (mmi_standard_node_create_controller(sub_type, &node) != MMI_ERROR_NONE) {
+                            LOGE("[ERROR] Failed to create standard node controller");
+                            return false;
+                        }
+                        LOGD("Adding controller node %s as %s", name.c_str(), alias.c_str());
+                        mmi_workflow_node_add(workflow, alias.c_str(), node);
+                        mmi_node_destroy(node);
+                        break;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    bool process_link_section(std::smatch matches, mmi_workflow_h workflow) {
+        if (matches.size() == 3) {
+            std::ssub_match sub_match_from = matches[1];
+            std::string from = sub_match_from.str();
+
+            std::ssub_match sub_match_to = matches[2];
+            std::string to = sub_match_to.str();
+
+            std::string from_node_name;
+            std::string from_port_name;
+
+            std::string to_node_name;
+            std::string to_port_name;
+
+            size_t from_pos = from.find(".");
+            if (from_pos != std::string::npos) {
+                from_node_name = from.substr(0, from_pos);
+                from_port_name = from.substr(from_pos + 1);
+            } else {
+                LOGE("[ERROR] Invalid 'from' format: %s", from.c_str());
+                return false;
+            }
+
+            size_t to_pos = to.find(".");
+            if (to_pos != std::string::npos) {
+                to_node_name = to.substr(0, to_pos);
+                to_port_name = to.substr(to_pos + 1);
+            } else {
+                LOGE("[ERROR] Invalid 'to' format: %s", to.c_str());
+                return false;
+            }
+
+            LOGD("from_node_name: %s, from_port_name: %s, to_node_name: %s, to_port_name: %s",
+                 from_node_name.c_str(), from_port_name.c_str(), to_node_name.c_str(), to_port_name.c_str());
+
+            if (mmi_workflow_link_nodes_by_names(workflow,
+                                                 from_node_name.c_str(), from_port_name.c_str(),
+                                                 to_node_name.c_str(), to_port_name.c_str()) != MMI_ERROR_NONE) {
+                LOGE("[ERROR] Failed to link nodes by names");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool process_attribute_section(std::smatch matches, mmi_workflow_h workflow) {
+        if (matches.size() == 3) {
+            std::ssub_match sub_match_name = matches[1];
+            std::string identifier = sub_match_name.str();
+
+            std::ssub_match sub_match_value = matches[2];
+            std::string alias = sub_match_value.str();
+
+            LOGD("identifier: %s, alias: %s", identifier.c_str(), alias.c_str());
+
+            size_t pos = identifier.find(".");
+            if (pos != std::string::npos) {
+                std::string node_name = identifier.substr(0, pos);
+                std::string attribute_name = identifier.substr(pos + 1);
+
+                LOGD("node_name: %s, attribute_name: %s", node_name.c_str(), attribute_name.c_str());
+
+                mmi_workflow_attribute_assign(workflow, alias.c_str(), node_name.c_str(), attribute_name.c_str());
+            }
+        }
+        return true;
+    }
+
+    bool process_output_section(std::smatch matches, mmi_workflow_h workflow) {
+        if (matches.size() == 3) {
+            std::ssub_match sub_match_name = matches[1];
+            std::string identifier = sub_match_name.str();
+
+            std::ssub_match sub_match_value = matches[2];
+            std::string alias = sub_match_value.str();
+
+            LOGD("identifier: %s, alias: %s", identifier.c_str(), alias.c_str());
+
+            size_t pos = identifier.find(".");
+            if (pos != std::string::npos) {
+                std::string node_name = identifier.substr(0, pos);
+                std::string port_name = identifier.substr(pos + 1);
+
+                LOGD("node_name: %s, port_name: %s", node_name.c_str(), port_name.c_str());
+
+                mmi_workflow_output_assign(workflow, alias.c_str(), node_name.c_str(), port_name.c_str());
+            }
+        }
+        return true;
+    }
+
+    bool process_section_line(const std::string &line, mmi_workflow_h workflow) {
+        for (auto &section_pattern : m_section_patterns) {
+            if (m_current_section == section_pattern.section) {
+                if (std::regex_match(line, section_pattern.pattern)) {
+                    LOGD("section line matched: %s", line.c_str());
+                    std::smatch matches;
+                    if (std::regex_search(line, matches, section_pattern.pattern)) {
+                        switch (m_current_section) {
+                        case WorkflowScriptSection::WORKFLOW_INFO:
+                            if (process_workflow_info(matches, workflow) == false) {
+                                LOGE("[ERROR] Failed to process workflow info");
+                                return false;
+                            }
+                            break;
+                        case WorkflowScriptSection::NODE_LIST:
+                            if (process_node_section(matches, workflow) == false) {
+                                LOGE("[ERROR] Failed to process node section");
+                                return false;
+                            }
+                            break;
+                        case WorkflowScriptSection::LINK_LIST:
+                            if (process_link_section(matches, workflow) == false) {
+                                LOGE("[ERROR] Failed to process link section");
+                                return false;
+                            }
+                            break;
+                        case WorkflowScriptSection::ATTRIBUTE_LIST:
+                            if (process_attribute_section(matches, workflow) == false) {
+                                LOGE("[ERROR] Failed to process attribute section");
+                                return false;
+                            }
+                            break;
+                        case WorkflowScriptSection::OUTPUT_LIST:
+                            if (process_output_section(matches, workflow) == false) {
+                                LOGE("[ERROR] Failed to process output section");
+                                return false;
+                            }
+                            break;
+                        default:
+                            break;
+                        }
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    bool parse(std::vector<std::string> &lines, mmi_workflow_h *workflow) {
+        if (nullptr == workflow) {
+            LOGE("[ERROR] parameter is null");
+            return false;
+        }
+        create_workflow(workflow);
+        if (nullptr == *workflow) {
+            LOGE("[ERROR] Failed to create workflow");
+            return false;
+        }
+
+        for (auto &line : lines) {
+            trim(line);
+
+            if (line.empty()) {
+                continue;
+            }
+
+            if (std::regex_match(line, m_section_declaration_regex)) {
+                if (process_section_declaration(line) == false) {
+                    LOGE("invalid section name: %s", line.c_str());
+                    return false;
+                }
+            } else {
+                if (process_section_line(line, *workflow) == false) {
+                    LOGE("invalid section line: %s", line.c_str());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+private:
+    void create_workflow(mmi_workflow_h *workflow) {
+        if (nullptr == workflow) {
+            LOGE("[ERROR] parameter is null");
+            return;
+        }
+
+        mmi_workflow_s *workflow_s = new(std::nothrow) mmi_workflow_s;
+        if (nullptr == workflow_s) {
+            LOGE("[ERROR] Failed to create workflow");
+            return;
+        }
+
+        workflow_s->type = MMI_STANDARD_WORKFLOW_NONE;
+        workflow_s->node_infos = nullptr;
+        workflow_s->node_info_count = 0;
+        workflow_s->link_infos = nullptr;
+        workflow_s->link_info_count = 0;
+        workflow_s->attribute_assignment_infos = nullptr;
+        workflow_s->attribute_assignment_info_count = 0;
+        workflow_s->attribute_default_value_infos = nullptr;
+        workflow_s->attribute_default_value_info_count = 0;
+        workflow_s->output_assignment_infos = nullptr;
+        workflow_s->output_assignment_info_count = 0;
+
+        *workflow = static_cast<mmi_workflow_h>(workflow_s);
+    }
+    void destroy_workflow(mmi_workflow_h workflow) {
+        if (nullptr == workflow) {
+            LOGE("[ERROR] parameter is null");
+            return;
+        }
+
+        mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s*>(workflow);
+        delete workflow_s;
+    }
+
+    WorkflowScriptSection m_current_section{WorkflowScriptSection::NONE};
+
+    std::regex m_section_declaration_regex{"^@[a-zA-Z0-9_-]+$"};
+
+    std::vector<WorkflowScriptSectionName> m_section_names;
+    std::vector<WorkflowScriptSectionPattern> m_section_patterns;
+
+    std::vector<StandardWorkflowName> m_standard_workflow_names;
+
+    std::vector<StandardNodeSourceName> m_standard_node_source_names;
+    std::vector<StandardNodeProcessorName> m_standard_node_processor_names;
+    std::vector<StandardNodeLogicName> m_standard_node_logic_names;
+    std::vector<StandardNodeControllerName> m_standard_node_controller_names;
+
+};
+
+#endif /* __MMI_WORKFLOW_SCRIPT_PARSER_H__ */
diff --git a/src/mmi/mmi-workflow-script.cpp b/src/mmi/mmi-workflow-script.cpp
new file mode 100644 (file)
index 0000000..9be277a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi.h"
+#include "mmi-log.h"
+#include "mmi-workflow.h"
+
+#include "mmi-workflow-script-parser.h"
+
+#include <fstream>
+
+MMI_API int mmi_workflow_create_from_script(const char *script_path, mmi_workflow_h *workflow) {
+    if (script_path == nullptr || workflow == nullptr) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::ifstream script_file(script_path);
+    if (!script_file.is_open()) {
+        LOGE("[ERROR] Failed to open script file");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string line;
+    std::vector<std::string> lines;
+    while (std::getline(script_file, line)) {
+        lines.push_back(line);
+    }
+
+    WorkflowScriptParser parser;
+    *workflow = nullptr;
+    bool result = parser.parse(lines, workflow);
+    if (!result || *workflow == nullptr) {
+        LOGE("[ERROR] Failed to create workflow from data");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    return MMI_ERROR_NONE;
+}
+
diff --git a/src/mmi/mmi-workflow.cpp b/src/mmi/mmi-workflow.cpp
new file mode 100644 (file)
index 0000000..dcc7260
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <algorithm>
+#include <vector>
+#include <map>
+#include <string>
+
+#include <Ecore.h>
+
+#include "mmi.h"
+#include "mmi-plugin-storage.h"
+
+#include "mmi-log.h"
+
+MMI_API int mmi_workflow_create(mmi_workflow_h *workflow) {
+    if (nullptr == workflow) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = new(std::nothrow) mmi_workflow_s;
+    if (nullptr == workflow_s) {
+        LOGE("[ERROR] Failed to create workflow");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    workflow_s->type = MMI_STANDARD_WORKFLOW_NONE;
+    workflow_s->node_infos = nullptr;
+    workflow_s->node_info_count = 0;
+    workflow_s->link_infos = nullptr;
+    workflow_s->link_info_count = 0;
+    workflow_s->attribute_assignment_infos = nullptr;
+    workflow_s->attribute_assignment_info_count = 0;
+    workflow_s->attribute_default_value_infos = nullptr;
+    workflow_s->attribute_default_value_info_count = 0;
+    workflow_s->output_assignment_infos = nullptr;
+    workflow_s->output_assignment_info_count = 0;
+
+    *workflow = static_cast<mmi_workflow_h>(workflow_s);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_set_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e type) {
+    if (nullptr == workflow) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    workflow_s->type = type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_get_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e *type) {
+    if (nullptr == workflow || nullptr == type) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    *type = workflow_s->type;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_node_add(mmi_workflow_h workflow, const char *node_name, mmi_node_h node) {
+    if (nullptr == workflow || nullptr == node_name || nullptr == node) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    mmi_workflow_node_info_s *node_infos = workflow_s->node_infos;
+    size_t node_info_count = workflow_s->node_info_count;
+    workflow_s->node_infos = new(std::nothrow) mmi_workflow_node_info_s[node_info_count + 1];
+    if (node_info_count > 0) {
+        memcpy(workflow_s->node_infos, node_infos, sizeof(mmi_workflow_node_info_s) * node_info_count);
+        delete [] node_infos;
+    }
+
+    snprintf(workflow_s->node_infos[node_info_count].name, MMI_NAME_MAX_LENGTH, "%s", node_name);
+    mmi_node_clone(node, &(workflow_s->node_infos[node_info_count].node));
+    workflow_s->node_info_count++;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_link_nodes_by_names(mmi_workflow_h workflow,
+        const char *from_node_name, const char *from_port_name, const char *to_node_name, const char *to_port_name) {
+    if (nullptr == from_node_name || nullptr == from_port_name || nullptr == to_node_name || nullptr == to_port_name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    mmi_workflow_link_info_s *link_infos = workflow_s->link_infos;
+    size_t link_info_count = workflow_s->link_info_count;
+    workflow_s->link_infos = new(std::nothrow) mmi_workflow_link_info_s[link_info_count + 1];
+    if (link_info_count > 0) {
+        memcpy(workflow_s->link_infos, link_infos, sizeof(mmi_workflow_link_info_s) * link_info_count);
+        delete [] link_infos;
+    }
+
+    snprintf(workflow_s->link_infos[link_info_count].from_node_name, MMI_NAME_MAX_LENGTH, "%s", from_node_name);
+    snprintf(workflow_s->link_infos[link_info_count].from_port_name, MMI_NAME_MAX_LENGTH, "%s", from_port_name);
+    snprintf(workflow_s->link_infos[link_info_count].to_node_name, MMI_NAME_MAX_LENGTH, "%s", to_node_name);
+    snprintf(workflow_s->link_infos[link_info_count].to_port_name, MMI_NAME_MAX_LENGTH, "%s", to_port_name);
+
+    workflow_s->link_info_count = link_info_count + 1;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_attribute_assign(mmi_workflow_h workflow, const char *attribute_name, const char *target_node_name, const char *target_attribute_name) {
+    if (nullptr == workflow || nullptr == attribute_name || nullptr == target_node_name || nullptr == target_attribute_name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    mmi_workflow_attribute_assignment_info_s *attribute_assignment_infos = workflow_s->attribute_assignment_infos;
+    size_t attribute_assignment_info_count = workflow_s->attribute_assignment_info_count;
+    workflow_s->attribute_assignment_infos = new(std::nothrow) mmi_workflow_attribute_assignment_info_s[attribute_assignment_info_count + 1];
+    if (attribute_assignment_info_count > 0) {
+        memcpy(workflow_s->attribute_assignment_infos, attribute_assignment_infos, sizeof(mmi_workflow_attribute_assignment_info_s) * attribute_assignment_info_count);
+        delete [] attribute_assignment_infos;
+    }
+
+    snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].attribute_name, MMI_NAME_MAX_LENGTH, "%s", attribute_name);
+    snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].target_node_name, MMI_NAME_MAX_LENGTH, "%s", target_node_name);
+    snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].target_attribute_name, MMI_NAME_MAX_LENGTH, "%s", target_attribute_name);
+
+    workflow_s->attribute_assignment_info_count = attribute_assignment_info_count + 1;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_attribute_set_default_value(mmi_workflow_h workflow, mmi_attribute_h default_value) {
+    if (nullptr == workflow || nullptr == default_value) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    mmi_workflow_attribute_default_value_info_s *attribute_default_value_infos = workflow_s->attribute_default_value_infos;
+    size_t attribute_default_value_info_count = workflow_s->attribute_default_value_info_count;
+    workflow_s->attribute_default_value_infos = new(std::nothrow) mmi_workflow_attribute_default_value_info_s[attribute_default_value_info_count + 1];
+    if (attribute_default_value_info_count > 0) {
+        memcpy(workflow_s->attribute_default_value_infos, attribute_default_value_infos, sizeof(mmi_workflow_attribute_default_value_info_s) * attribute_default_value_info_count);
+        delete [] attribute_default_value_infos;
+    }
+
+    mmi_attribute_to_bytes(default_value,
+        &(workflow_s->attribute_default_value_infos[attribute_default_value_info_count].serialized_default_value),
+        &(workflow_s->attribute_default_value_infos[attribute_default_value_info_count].serialized_default_value_size));
+
+    workflow_s->attribute_default_value_info_count = attribute_default_value_info_count + 1;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_signal_assign(mmi_workflow_h workflow, const char *workflow_signal, mmi_node_h node, const char *node_signal) {
+    if (nullptr == workflow || nullptr == workflow_signal || nullptr == node || nullptr == node_signal) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_output_assign(mmi_workflow_h workflow, const char *workflow_output, const char *out_node_name, const char *node_out_port_name) {
+    if (nullptr == workflow || nullptr == workflow_output || nullptr == out_node_name || nullptr == node_out_port_name) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    mmi_workflow_output_assignment_info_s *output_assignment_infos = workflow_s->output_assignment_infos;
+    size_t output_assignment_info_count = workflow_s->output_assignment_info_count;
+    workflow_s->output_assignment_infos = new(std::nothrow) mmi_workflow_output_assignment_info_s[output_assignment_info_count + 1];
+    if (output_assignment_info_count > 0) {
+        memcpy(workflow_s->output_assignment_infos, output_assignment_infos, sizeof(mmi_workflow_output_assignment_info_s) * output_assignment_info_count);
+        delete [] output_assignment_infos;
+    }
+
+    snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].output_name,MMI_NAME_MAX_LENGTH, "%s", workflow_output);
+    snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].from_node_name, MMI_NAME_MAX_LENGTH, "%s", out_node_name);
+    snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].from_port_name, MMI_NAME_MAX_LENGTH, "%s", node_out_port_name);
+
+    workflow_s->output_assignment_info_count = output_assignment_info_count + 1;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_output_assign_by_port(mmi_workflow_h workflow, const char *workflow_output, mmi_port_h port) {
+    if (nullptr == workflow || nullptr == workflow_output || nullptr == port) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_standard_workflow_register(mmi_workflow_h workflow) {
+    if (nullptr == workflow) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_standard_workflow_type_e workflow_type;
+    mmi_workflow_get_type(workflow, &workflow_type);
+
+    const char *identifier = mmi_plugin_storage_get_current_module_identifier();
+    if (nullptr == identifier) {
+        LOGE("[ERROR] identifier is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_plugin_module_workflow_list_s list = mmi_plugin_storage_get_workflow_list(identifier);
+
+    mmi_workflow_h *workflows = list.workflows;
+    size_t workflow_count = list.workflow_count;
+
+    bool duplicated = false;
+
+    for (size_t i = 0; i < workflow_count; i++) {
+        mmi_standard_workflow_type_e type;
+        mmi_workflow_get_type(workflows[i], &type);
+        if (type == workflow_type) {
+            duplicated = true;
+            mmi_workflow_destroy(workflows[i]);
+
+            mmi_workflow_h cloned;
+            mmi_workflow_clone(workflow, &cloned);
+            workflows[i] = cloned;
+        }
+    }
+
+    if (!duplicated) {
+        list.workflows = new(std::nothrow) mmi_workflow_h[workflow_count + 1];
+        if (nullptr == list.workflows) {
+            LOGE("[ERROR] failed to allocate memory");
+            return MMI_ERROR_OUT_OF_MEMORY;
+        }
+        if (workflow_count > 0) {
+            memcpy(list.workflows, workflows, sizeof(mmi_workflow_h) * workflow_count);
+            delete [] workflows;
+        }
+
+        mmi_workflow_h cloned;
+        mmi_workflow_clone(workflow, &cloned);
+        list.workflows[workflow_count] = cloned;
+        list.workflow_count = workflow_count + 1;
+    }
+
+    mmi_plugin_storage_set_workflow_list(list);
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_clone(mmi_workflow_h workflow, mmi_workflow_h *cloned) {
+    if (nullptr == workflow || nullptr == cloned) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *cloned_s = new(std::nothrow) mmi_workflow_s;
+    if (nullptr == cloned_s) {
+        LOGE("[ERROR] new mmi_workflow_s failed");
+        return MMI_ERROR_OUT_OF_MEMORY;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s *>(workflow);
+    memcpy(cloned_s, workflow_s, sizeof(mmi_workflow_s));
+
+    cloned_s->node_infos = new(std::nothrow) mmi_workflow_node_info_s[workflow_s->node_info_count];
+    memcpy(cloned_s->node_infos, workflow_s->node_infos, sizeof(mmi_workflow_node_info_s) * workflow_s->node_info_count);
+    for (int i = 0; i < workflow_s->node_info_count; i++) {
+        mmi_node_clone(workflow_s->node_infos[i].node, &(cloned_s->node_infos[i].node));
+    }
+
+    if (workflow_s->link_info_count > 0) {
+        cloned_s->link_infos = new(std::nothrow) mmi_workflow_link_info_s[workflow_s->link_info_count];
+        memcpy(cloned_s->link_infos, workflow_s->link_infos, sizeof(mmi_workflow_link_info_s) * workflow_s->link_info_count);
+    }
+
+    if (workflow_s->attribute_assignment_info_count > 0) {
+        cloned_s->attribute_assignment_infos = new(std::nothrow) mmi_workflow_attribute_assignment_info_s[workflow_s->attribute_assignment_info_count];
+        memcpy(cloned_s->attribute_assignment_infos, workflow_s->attribute_assignment_infos,
+               sizeof(mmi_workflow_attribute_assignment_info_s) * workflow_s->attribute_assignment_info_count);
+    }
+
+    if (workflow_s->attribute_default_value_info_count > 0) {
+        cloned_s->attribute_default_value_infos = new(std::nothrow) mmi_workflow_attribute_default_value_info_s[workflow_s->attribute_default_value_info_count];
+        for (int i = 0; i < workflow_s->attribute_default_value_info_count; i++) {
+            cloned_s->attribute_default_value_infos[i].serialized_default_value =
+                new(std::nothrow) unsigned char[workflow_s->attribute_default_value_infos[i].serialized_default_value_size];
+            memcpy(cloned_s->attribute_default_value_infos[i].serialized_default_value,
+                   workflow_s->attribute_default_value_infos[i].serialized_default_value,
+                   workflow_s->attribute_default_value_infos[i].serialized_default_value_size);
+        }
+    }
+
+    if (workflow_s->output_assignment_info_count > 0) {
+        cloned_s->output_assignment_infos = new(std::nothrow) mmi_workflow_output_assignment_info_s[workflow_s->output_assignment_info_count];
+        memcpy(cloned_s->output_assignment_infos, workflow_s->output_assignment_infos,
+               sizeof(mmi_workflow_output_assignment_info_s) * workflow_s->output_assignment_info_count);
+    }
+
+    *cloned = cloned_s;
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_workflow_destroy(mmi_workflow_h workflow) {
+    if (nullptr == workflow) {
+        LOGE("[ERROR] parameter is null");
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    mmi_workflow_s *workflow_s = static_cast<mmi_workflow_s*>(workflow);
+
+    for (int i = 0; i < workflow_s->node_info_count; i++) {
+        mmi_node_destroy(workflow_s->node_infos[i].node);
+    }
+    delete [] workflow_s->node_infos;
+    delete [] workflow_s->link_infos;
+    delete [] workflow_s->attribute_assignment_infos;
+    delete [] workflow_s->attribute_default_value_infos;
+    delete [] workflow_s->output_assignment_infos;
+
+    delete workflow_s;
+
+    return MMI_ERROR_NONE;
+}
diff --git a/src/mmi/mmi.cpp b/src/mmi/mmi.cpp
new file mode 100644 (file)
index 0000000..e49ec6e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <algorithm>
+#include <vector>
+#include <map>
+#include <string>
+
+#include <Ecore.h>
+
+#include "mmi-log.h"
+#include "mmi-ipc-tidl.h"
+
+#include "mmi-client-manager.h"
+
+#include "mmi.h"
+
+using namespace mmi;
+using namespace mmi::communication;
+
+static mmi_state_e g_mmi_state{MMI_STATE_NONE};
+static std::vector<std::pair<mmi_state_changed_cb, void*>> g_state_changed_cb_list;
+
+struct CommunicationChannelFactoryTIDL : public ICommunicationChannelFactory {
+    CommunicationChannel* create_channel() override {
+        return new(std::nothrow) CommunicationChannelTIDL();
+    }
+    void destroy_channel(CommunicationChannel* channel) override {
+        delete channel;
+    }
+};
+static CommunicationChannelFactoryTIDL g_communication_channel_factory_tidl;
+
+class CommunicationChannelObserver :
+    public SimpleEventObserver<COMMUNICATION_CHANNEL_EVENT_TYPE> {
+public:
+    void on_observer_event(
+        const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override {
+        mmi_state_e previous_state = g_mmi_state;
+
+        if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) {
+            g_mmi_state = MMI_STATE_READY;
+        } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) {
+            g_mmi_state = MMI_STATE_NONE;
+        }
+
+        if (previous_state != g_mmi_state) {
+            for (auto& item : g_state_changed_cb_list) {
+                item.first(g_mmi_state, item.second);
+            }
+        }
+    }
+};
+static CommunicationChannelObserver g_communication_channel_observer;
+
+ClientManager g_mmi_client_manager{
+    &g_communication_channel_factory_tidl,
+    &g_communication_channel_observer
+};
+
+MMI_API int mmi_initialize() {
+    LOGE("[SUCCESS] Succeeded to create client");
+    g_mmi_client_manager.initialize();
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_deinitialize() {
+    g_mmi_client_manager.deinitialize();
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_set_state_changed_cb(mmi_state_changed_cb callback, void *user_data) {
+    if (callback == nullptr) {
+        return MMI_ERROR_INVALID_PARAMETER;
+    }
+
+    callback(g_mmi_state, user_data);
+
+    g_state_changed_cb_list.push_back(std::make_pair(callback, user_data));
+
+    return MMI_ERROR_NONE;
+}
+
+MMI_API int mmi_unset_state_changed_cb(mmi_state_changed_cb callback) {
+    auto it = std::find_if(g_state_changed_cb_list.begin(), g_state_changed_cb_list.end(),
+    [callback](const std::pair<mmi_state_changed_cb, void*>& item) {
+        return item.first == callback;
+    });
+
+    if (it != g_state_changed_cb_list.end()) {
+        g_state_changed_cb_list.erase(it);
+    }
+
+    return MMI_ERROR_NONE;
+}
index 4cc2d49..d8c6f3a 100644 (file)
@@ -1,17 +1,2 @@
-mmi_tests_srcs = [
-       'mmi-tests.cpp',
-       'mmi-main-test.cpp',
-       'mmi-ipc-test.cpp',
-       'wait-helper.cpp'
-       ]
-
-gmock_dep = dependency('gmock', method : 'pkg-config')
-ecore_dep = dependency('ecore', method : 'pkg-config')
-
-executable(
-       'mmi-tests',
-       mmi_tests_srcs,
-       dependencies : [mmi_declared_dep, gmock_dep, ecore_dep],
-       install_dir : mmi_prefix_bindir,
-       install : true
-       )
+subdir('mmi')
+subdir('mmi-manager')
diff --git a/tests/mmi-ipc-test.cpp b/tests/mmi-ipc-test.cpp
deleted file mode 100644 (file)
index 21c5514..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "mmi.h"
-#include "mmi-tests.h"
-#include "mmi-ipc.h"
-#include "mmi-client.h"
-
-#include <Ecore.h>
-#include <rpc-port-internal.h>
-
-class MMIIpcTest : public ::testing::Test
-{
-public:
-       void SetUp(void) override
-       {
-               ecore_init();
-       }
-
-       void TearDown(void) override
-       {
-               ecore_shutdown();
-       }
-};
-
-TEST_F(MMIIpcTest, MMIFWIpcInitSuccess)
-{
-       int res = mmi_initialize();
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIIpcInitFail)
-{
-       int res = mmi_initialize();
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-static void voice_touch_callback(int input_event_type, const char *result_out, void *user_data)
-{
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcRegisterSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoEvent)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_NONE;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(MMI_INPUT_EVENT_TYPE_NONE, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoCallback)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, NULL);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoClient)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_client_destroy();
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcActivateSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcActivateFailNoEvent)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(MMI_INPUT_EVENT_TYPE_NONE);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcActivateFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcActivateFailAlreadyDone)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcDeactivateSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_deactivate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcDeactivateFailNoEvent)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_deactivate_input_event(MMI_INPUT_EVENT_TYPE_NONE);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIIpcTest, MMIFWIpcDeactivateFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_ipc_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_ipc_deactivate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
diff --git a/tests/mmi-main-test.cpp b/tests/mmi-main-test.cpp
deleted file mode 100644 (file)
index a437ddb..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *               http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "mmi.h"
-#include "mmi-client.h"
-#include "mmi-tests.h"
-#include "mmi-ipc.h"
-
-#include <Ecore.h>
-#include <rpc-port-internal.h>
-
-class MMIMainTest : public ::testing::Test
-{
-public:
-       void SetUp(void) override
-       {
-               ecore_init();
-       }
-
-       void TearDown(void) override
-       {
-               ecore_shutdown();
-       }
-};
-
-TEST_F(MMIMainTest, MMIMainInit)
-{
-       int res = mmi_initialize();
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MmiClientCreateSuccess)
-{
-       int res = mmi_client_create();
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_client_destroy();
-}
-
-static void voice_touch_callback(int input_event_type, const char *result_out, void *user_data)
-{
-}
-
-static void voice_recognition_callback(int input_event_type, const char *result_out, void *user_data)
-{
-}
-
-TEST_F(MMIMainTest, MMIClientSetResultCbSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_client_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_handle mmi_client = mmi_client_get();
-
-       GList* iter = NULL;
-       mmi_result_cb_s *data = NULL;
-
-       iter = g_list_first(mmi_client->result_cb_list);
-       if(NULL != iter) {
-               data = (mmi_result_cb_s*)iter->data;
-               EXPECT_NE(data->result_callback, nullptr);
-               EXPECT_EQ(data->result_callback, voice_touch_callback);
-               EXPECT_EQ(data->input_event_type, input_event_type);
-       }
-       else {
-               EXPECT_TRUE(false);
-       }
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIClientSetResultCbFail)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_client_set_result_cb(input_event_type, NULL, NULL);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMISetResultCbSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMISetResultCbFail)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, NULL, NULL);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIClientDestroyRemoveGList)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION, voice_recognition_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_client_destroy();
-}
-
-TEST_F(MMIMainTest, MMISetResultCbFailNoClient)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_client_destroy();
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMISetResultCbFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIActivateInputEventSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIActivateInputEventFailNoEvent)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIActivateInputEventFailNoCallback)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIActivateInputEventFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIActivateInputEventFailAlreadyDone)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIDeactivateInputEventSuccess)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_deactivate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIDeactivateInputEventFailNoEvent)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_deactivate_input_event(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION);
-       EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER);
-
-       mmi_deinitialize();
-}
-
-TEST_F(MMIMainTest, MMIDeactivateInputEventFailNoRpcHandle)
-{
-       int res = mmi_initialize();
-       mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH;
-
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       res = mmi_activate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_NONE);
-
-       mmi_ipc_deinitialize();
-
-       mmi_handle mmi_client = mmi_client_get();
-       mmi_client->rpc_h = NULL;
-
-       res = mmi_deactivate_input_event(input_event_type);
-       EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED);
-
-       mmi_deinitialize();
-}
diff --git a/tests/mmi-manager/ecore-event-dispatcher.cpp b/tests/mmi-manager/ecore-event-dispatcher.cpp
new file mode 100644 (file)
index 0000000..768860f
--- /dev/null
@@ -0,0 +1,20 @@
+#include <Ecore.h>
+
+Ecore_Idle_Enterer *_idle_before = nullptr;
+bool g_flag_wait_done = false;
+
+static Eina_Bool _e_cb_idle_before(void *data) {
+    (void) data;
+
+    g_flag_wait_done = true;
+    return ECORE_CALLBACK_RENEW;
+}
+
+void wait_for_dispatch() {
+    g_flag_wait_done = false;
+    _idle_before = ecore_idle_enterer_before_add(_e_cb_idle_before, NULL);
+    while (!g_flag_wait_done) {
+        ecore_main_loop_iterate();
+    }
+    ecore_idle_enterer_del(_idle_before);
+}
\ No newline at end of file
diff --git a/tests/mmi-manager/meson.build b/tests/mmi-manager/meson.build
new file mode 100644 (file)
index 0000000..7c0bb17
--- /dev/null
@@ -0,0 +1,34 @@
+mmi_manager_tests_srcs = [
+       'mmi-manager-tests.cpp',
+       'mmi-client-tests.cpp',
+       'mmi-manager-main-tests.cpp',
+       'ecore-event-dispatcher.cpp',
+       'port-instance-manager/mmi-port-instance-manager-tests.cpp',
+       'node-prototype/mmi-node-prototype-tests.cpp',
+       'node-prototype-manager/mmi-node-prototype-manager-tests.cpp',
+       'node-instance-manager/mmi-node-instance-manager-tests.cpp',
+       'workflow-prototype/mmi-workflow-prototype-tests.cpp',
+       'workflow-instance/mmi-workflow-instance-tests.cpp',
+       'workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp',
+       'plugin-module-registry/mmi-plugin-module-registry-tests.cpp',
+       ]
+
+gmock_dep = dependency('gmock', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+mmi_manager_tests_include_dirs = include_directories(
+       '.',
+       '../../src/common/',
+       '../../src/mmi-manager/',
+       )
+
+tc = executable(
+       'mmi-manager-tests',
+       mmi_manager_tests_srcs,
+       include_directories : [ mmi_manager_tests_include_dirs ],
+       dependencies : [mmi_manager_declared_dep, gmock_dep, ecore_dep],
+       install_dir : mmi_prefix_bindir,
+       install : true
+       )
+
+test('mmi-manager-tests', tc, args : ['--gtest_output=xml:./mmi-manager-tests.xml'])
diff --git a/tests/mmi-manager/mmi-client-tests.cpp b/tests/mmi-manager/mmi-client-tests.cpp
new file mode 100644 (file)
index 0000000..ddb4216
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-client.h"
+#include "mmi-manager.h"
+#include "mmi-manager-tests.h"
+
+using namespace mmi;
+
+class MMIClientTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+    }
+
+    void TearDown(void) override {
+    }
+
+    ClientManager client_manager;
+};
+
+
+TEST_F(MMIClientTest, MMIClientInitShutdown) {
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIClientTest, MMIClientAddGetClient) {
+    std::string app_id{"org.tizen.mmi-system-ux-test"};
+    Client *client = nullptr, *client_retrieved = nullptr;
+
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client = client_manager.add_client(app_id);
+    EXPECT_NE(client, nullptr);
+
+    client_retrieved = client_manager.get_client(app_id);
+    EXPECT_EQ(client_retrieved, client);
+
+    res = client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIClientTest, MMIClientAddRemoveClient) {
+    std::string app_id{"org.tizen.mmi-system-ux-test"};
+    Client *client = nullptr;
+
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client = client_manager.add_client(app_id);
+    EXPECT_NE(client, nullptr);
+
+    res = client_manager.remove_client(app_id);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIClientTest, MMIClientSetGetClientUID) {
+    std::string app_id{"org.tizen.mmi-system-ux-test"};
+    Client *client = nullptr;
+    uid_t uid = 1;
+
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client = client_manager.add_client(app_id);
+
+    res = client_manager.set_client_uid(client, uid);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    uid_t retrieved = client_manager.get_client_uid(client);
+    EXPECT_EQ(uid, retrieved);
+
+    res = client_manager.remove_client(app_id);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIClientTest, MMIClientSetGetClientPID) {
+    std::string app_id{"org.tizen.mmi-system-ux-test"};
+    Client *client = nullptr;
+    pid_t pid = 1;
+
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client = client_manager.add_client(app_id);
+
+    res = client_manager.set_client_pid(client, pid);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    pid_t retrieved = client_manager.get_client_pid(client);
+    EXPECT_EQ(pid, retrieved);
+
+    res = client_manager.remove_client(app_id);
+
+    res = client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIClientTest, MMIClientGetClientSender) {
+    std::string app_id{"org.tizen.mmi-system-ux-test"};
+    std::string sender;
+    Client *client = nullptr;
+
+    int res = client_manager.initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    client = client_manager.add_client(app_id);
+
+    sender = client_manager.get_client_sender(client);
+    EXPECT_EQ(sender, app_id);
+
+    res = client_manager.remove_client(app_id);
+    EXPECT_EQ(res, 0);
+
+    res = client_manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
diff --git a/tests/mmi-manager/mmi-manager-main-tests.cpp b/tests/mmi-manager/mmi-manager-main-tests.cpp
new file mode 100644 (file)
index 0000000..863b439
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-manager.h"
+#include "mmi-manager-tests.h"
+#include "mmi-self-container.h"
+
+namespace {
+
+using namespace mmi;
+using namespace mmi::communication;
+
+class CommunicationChannelManagerDummy : public CommunicationChannelManager {
+public:
+    int connect() override {
+        return MMI_ERROR_NONE;
+    }
+    int disconnect() override {
+        return MMI_ERROR_NONE;
+    }
+    int send(communication::Message *message) override {
+        return MMI_ERROR_NONE;
+    }
+
+    void on_observer_event(
+        const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) override {
+        return;
+    }
+};
+
+struct CommunicationChannelFactoryDummy : public CommunicationChannelManagerFactory {
+    std::shared_ptr<CommunicationChannelManager> create_channel() override {
+        return std::make_shared<CommunicationChannelManagerDummy>();
+    }
+};
+
+class MMIMANAGERMainTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+    }
+
+    void TearDown(void) override {
+    }
+
+};
+
+
+TEST_F(MMIMANAGERMainTest, MMIMANAGERMainInit) {
+    int res = 0;
+
+    mmi::Manager manager(
+        std::make_shared<CommunicationChannelFactoryDummy>(),
+        std::make_shared<PluginModuleProxyFactorySelfContainerTest>());
+    res = manager.initialize();
+
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = manager.deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+} // namespace
diff --git a/tests/mmi-manager/mmi-manager-tests.cpp b/tests/mmi-manager/mmi-manager-tests.cpp
new file mode 100644 (file)
index 0000000..98b0c6c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mmi-manager-tests.h"
+
+int main(int argc, char **argv) {
+    auto testResults = false;
+
+#ifdef TIZEN_TEST_GCOV
+    setenv("GCOV_PREFIX", "/tmp", 1);
+#endif
+    try {
+        ::testing::InitGoogleMock(&argc, argv);
+        ::testing::FLAGS_gtest_death_test_style = "fast";
+    } catch ( ... ) {
+        PRINT("Error occurred while trying to initialize GoogleTest.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    try {
+        testResults = (RUN_ALL_TESTS() == 0) ? true : false;
+    } catch (const ::testing::internal::GoogleTestFailureException &e) {
+        testResults = false;
+        PRINT("GoogleTestFailureException has been thrown: %s\n", e.what());
+    }
+
+#ifdef TIZEN_TEST_GCOV
+    __gcov_flush();
+#endif
+    return (testResults ? 0 : 1);
+}
+
diff --git a/tests/mmi-manager/mmi-manager-tests.h b/tests/mmi-manager/mmi-manager-tests.h
new file mode 100644 (file)
index 0000000..4034144
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice (including the next
+* paragraph) shall be included in all copies or substantial portions of the
+* Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __MMI_MANAGER_TESTS_H__
+#define __MMI_MANAGER_TESTS_H__
+
+#include <stdio.h>
+#include <gmock/gmock.h>
+#include <Ecore.h>
+
+#define PRINT printf
+
+#ifdef TIZEN_TEST_GCOV
+extern "C" void __gcov_flush(void);
+#endif
+extern void wait_for_dispatch();
+
+using ::testing::TestWithParam;
+using ::testing::Bool;
+using ::testing::Values;
+using ::testing::Combine;
+
+#endif //__MMI_MANAGER_TESTS_H__
diff --git a/tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp b/tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp
new file mode 100644 (file)
index 0000000..28cfeee
--- /dev/null
@@ -0,0 +1,101 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-node-instance-manager.h"
+#include "mmi-self-container.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class NodePrototypeStoreDummy : public INodePrototypeStore {
+public:
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) override {
+        return true;
+    }
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override {
+        last_query = std::make_pair(type, sub_type);
+        return nullptr;
+    }
+
+    std::pair<mmi_standard_node_type_e, mmi_standard_node_sub_type_e> last_query;
+};
+
+class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider {
+public:
+    PluginModuleProxyProviderDummy() {
+        proxy = factory.create(
+                    PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"});
+    }
+    virtual std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy(
+        PluginModuleInfo plugin_module_info) override {
+        return proxy;
+    }
+
+    PluginModuleProxyFactorySelfContainerTest factory;
+    std::shared_ptr<IPluginModuleProxy> proxy;
+};
+
+class NodeInstanceManagerTest : public testing::Test {
+public:
+    NodeInstanceManagerTest() {
+    }
+    virtual ~NodeInstanceManagerTest() {
+    }
+    void SetUp() override {
+        prototype_store = std::make_shared<NodePrototypeStoreDummy>();
+        plugin_module_proxy_provider = std::make_shared<PluginModuleProxyProviderDummy>();
+
+        manager.set_node_prototype_store(prototype_store);
+        manager.set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+
+        ASSERT_NE(prototype_store, nullptr);
+        ASSERT_NE(plugin_module_proxy_provider, nullptr);
+    }
+    void TearDown() override {
+    }
+
+    NodeInstanceManager manager;
+
+    std::shared_ptr<NodePrototypeStoreDummy> prototype_store;
+    std::shared_ptr<PluginModuleProxyProviderDummy> plugin_module_proxy_provider;
+};
+
+TEST_F(NodeInstanceManagerTest, ProperNodePrototypeQueriedWhenCreatingNodeInstance) {
+    prototype_store->last_query = std::make_pair(
+                                      MMI_STANDARD_NODE_TYPE_NONE, std::monostate{});
+
+    std::shared_ptr<NodeInstance> instance = manager.create_node_instance(
+                MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR );
+
+    ASSERT_EQ(prototype_store->last_query.first, MMI_STANDARD_NODE_TYPE_PROCESSOR);
+    ASSERT_EQ(prototype_store->last_query.second, mmi_standard_node_sub_type_e{MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp b/tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp
new file mode 100644 (file)
index 0000000..80a930e
--- /dev/null
@@ -0,0 +1,85 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-node-prototype-manager.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class NodePrototypeManagerTest : public testing::Test {
+public:
+    NodePrototypeManagerTest() {
+    }
+    virtual ~NodePrototypeManagerTest() {
+    }
+    void SetUp() override {
+        valid_prototype = std::make_shared<NodePrototype>();
+        ASSERT_TRUE(valid_prototype != nullptr);
+
+        valid_prototype->set_plugin_module_info(
+            PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+        valid_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+        valid_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT);
+
+        ASSERT_TRUE(valid_prototype->is_valid());
+    }
+    void TearDown() override {
+    }
+
+    NodePrototypeManager manager;
+    std::shared_ptr<NodePrototype> valid_prototype;
+};
+
+TEST_F(NodePrototypeManagerTest, NodePrototypeManagerProperlyAddedAndRetrieved_p) {
+    manager.add_node_prototype(valid_prototype);
+
+    auto prototype = manager.get_node_prototype(
+                         valid_prototype->get_type(),
+                         valid_prototype->get_sub_type());
+
+    ASSERT_TRUE(prototype != nullptr);
+}
+
+TEST_F(NodePrototypeManagerTest, NodePrototypeManagerRefusesInvalidPrototype_n) {
+    auto invalid_prototype = std::make_shared<NodePrototype>();
+
+    invalid_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+    invalid_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+
+    manager.add_node_prototype(invalid_prototype);
+
+    auto prototype = manager.get_node_prototype(
+                         invalid_prototype->get_type(),
+                         invalid_prototype->get_sub_type());
+
+    ASSERT_TRUE(prototype == nullptr);
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp b/tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp
new file mode 100644 (file)
index 0000000..bc90e8f
--- /dev/null
@@ -0,0 +1,147 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-node-prototype.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class NodePrototypeTest : public testing::Test {
+public:
+    NodePrototypeTest() {
+    }
+    virtual ~NodePrototypeTest() {
+    }
+    void SetUp() override {
+        prototype = std::make_shared<NodePrototype>();
+        ASSERT_TRUE(prototype != nullptr);
+    }
+    void TearDown() override {
+    }
+    std::shared_ptr<NodePrototype> prototype;
+};
+
+TEST_F(NodePrototypeTest, NodePrototypeValidationSucceedsForDifferentNodeTypes_p) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+    prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT);
+    ASSERT_TRUE(prototype->is_valid());
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR);
+    prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+    ASSERT_TRUE(prototype->is_valid());
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_LOGIC);
+    prototype->set_sub_type(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH);
+    ASSERT_TRUE(prototype->is_valid());
+}
+
+TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithNoSubtype_n) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+    /* No sub type provided */
+
+    ASSERT_FALSE(prototype->is_valid());
+}
+
+TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithIncorrectSubtype_n) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+    /* PROCESSOR sub type for SOURCE type */
+    prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+
+    ASSERT_FALSE(prototype->is_valid());
+}
+
+TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithNoPluginModuleInfo_n) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, ""});
+    /* No plugin module identifier provided */
+
+    prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+    prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT);
+
+    ASSERT_FALSE(prototype->is_valid());
+}
+
+TEST_F(NodePrototypeTest, PortInfoProperlyAdded_p) {
+    ASSERT_TRUE(prototype->get_port_infos().size() == 0);
+
+    PortInfo port_info_1 = {"test_port_1", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_TEXT, "test_description"};
+    prototype->add_port_info(port_info_1);
+
+    PortInfo port_info_2 = {"test_port_2", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_BOOLEAN, "test_description"};
+    prototype->add_port_info(port_info_2);
+
+    ASSERT_TRUE(prototype->get_port_infos().size() == 2);
+}
+
+TEST_F(NodePrototypeTest, PortInfoWithSameNameNotAdded_n) {
+    ASSERT_TRUE(prototype->get_port_infos().size() == 0);
+
+    PortInfo port_info_1 = {"test_port_1", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_TEXT, "test_description"};
+    prototype->add_port_info(port_info_1);
+
+    PortInfo port_info_2 = {"test_port_1", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_BOOLEAN, "test_description"};
+    prototype->add_port_info(port_info_2);
+
+    ASSERT_TRUE(prototype->get_port_infos().size() == 1);
+}
+
+TEST_F(NodePrototypeTest, AttributeProperlyAdded_p) {
+    ASSERT_TRUE(prototype->get_attribute_infos().size() == 0);
+
+    AttributeInfo attribute_info_1 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_INT};
+    prototype->add_attribute_info(attribute_info_1);
+
+    AttributeInfo attribute_info_2 = {"test_attribute_2", MMI_PRIMITIVE_VALUE_TYPE_STRING};
+    prototype->add_attribute_info(attribute_info_2);
+
+    ASSERT_TRUE(prototype->get_attribute_infos().size() == 2);
+}
+
+TEST_F(NodePrototypeTest, AttributeWithSameNameNotAdded_n) {
+    ASSERT_TRUE(prototype->get_attribute_infos().size() == 0);
+
+    AttributeInfo attribute_info_1 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_INT};
+    prototype->add_attribute_info(attribute_info_1);
+
+    AttributeInfo attribute_info_2 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_STRING};
+    prototype->add_attribute_info(attribute_info_2);
+
+    ASSERT_TRUE(prototype->get_attribute_infos().size() == 1);
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp b/tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp
new file mode 100644 (file)
index 0000000..d60dcde
--- /dev/null
@@ -0,0 +1,279 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-plugin-module-registry.h"
+#include "mmi-self-container.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class NodePrototypeStoreDummy : public INodePrototypeStore {
+public:
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) override {
+        _D("Adding prototype : %d", prototype->is_valid());
+        m_prototypes.push_back(prototype);
+
+        _D("Node prototype added: type=%d, sub_type=%d, plugin_module_type=%d, plugin_module_identifier=%s",
+           prototype->get_type(), to_int(prototype->get_sub_type()),
+           static_cast<int>(prototype->get_plugin_module_info().plugin_module_type),
+           prototype->get_plugin_module_info().plugin_module_identifier.c_str());
+
+        for (auto& port : prototype->get_port_infos()) {
+            _D("Port info: name=%s, port_type=%d, data_type=%d, data_description=%s",
+               port.name.c_str(), port.port_type, port.data_type, port.data_description.c_str());
+        }
+
+        for (auto& attribute : prototype->get_attribute_infos()) {
+            _D("Attribute info: name=%s, value_type=%d",
+               attribute.name.c_str(), attribute.value_type);
+        }
+
+        return true;
+    }
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override {
+        for (auto &prototype : m_prototypes) {
+            if (prototype->get_type() == type && prototype->get_sub_type() == sub_type) {
+                return prototype;
+            }
+        }
+        return nullptr;
+    }
+    size_t get_prototype_count() {
+        return m_prototypes.size();
+    }
+    std::vector<std::shared_ptr<NodePrototype>> m_prototypes;
+};
+
+class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore {
+public:
+    virtual bool add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) override {
+        _D("Adding prototype : %d", prototype->is_valid());
+        m_prototypes.push_back(prototype);
+
+        _D("Workflow prototype added: type=%d, plugin_module_type=%d, plugin_module_identifier=%s",
+           prototype->get_type(),
+           static_cast<int>(prototype->get_plugin_module_info().plugin_module_type),
+           prototype->get_plugin_module_info().plugin_module_identifier.c_str());
+        _D("  node_infos:");
+        for (auto& elem : prototype->get_node_infos()) {
+            _D("    name=%s, node_type=%d, node_sub_type=%d",
+               elem.name.c_str(), elem.node_type, to_int(elem.node_sub_type));
+        }
+        _D("  link_infos:");
+        for (auto& elem : prototype->get_link_infos()) {
+            _D("    from_node_name=%s, from_port_name=%s, to_node_name=%s, to_port_name=%s",
+               elem.from_node_name.c_str(), elem.from_port_name.c_str(),
+               elem.to_node_name.c_str(), elem.to_port_name.c_str());
+        }
+        _D("  output_assignment_infos:");
+        for (auto& elem : prototype->get_output_assignment_infos()) {
+            _D("    output_name=%s, from_node_name=%s, from_port_name=%s",
+               elem.output_name.c_str(), elem.from_node_name.c_str(), elem.from_port_name.c_str());
+        }
+
+        return true;
+    }
+    virtual std::shared_ptr<WorkflowPrototype> get_workflow_prototype(
+        mmi_standard_workflow_type_e type) override {
+        for (auto &prototype : m_prototypes) {
+            if (prototype->get_type() == type) {
+                return prototype;
+            }
+        }
+        return nullptr;
+    }
+    size_t get_prototype_count() {
+        return m_prototypes.size();
+    }
+    std::vector<std::shared_ptr<WorkflowPrototype>> m_prototypes;
+};
+
+/* Derived class to access protected members */
+class PluginModuleRegistry_Derived : public PluginModuleRegistry {
+public:
+    typedef std::pair<mmi_plugin_module_type_e, std::string> plugin_module_map_key;
+    std::map<plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>>& get_plugin_modules() {
+        return m_plugin_modules;
+    }
+};
+
+class PluginModuleRegistryTest : public testing::Test {
+public:
+    PluginModuleRegistryTest() {
+    }
+    virtual ~PluginModuleRegistryTest() {
+    }
+    void SetUp() override {
+        factory = std::make_shared<PluginModuleProxyFactorySelfContainerTest>();
+        node_prototype_store = std::make_shared<NodePrototypeStoreDummy>();
+        workflow_prototype_store = std::make_shared<WorkflowPrototypeStoreDummy>();
+        registry.set_plugin_module_proxy_factory(factory);
+        registry.set_node_prototype_store(node_prototype_store);
+        registry.set_workflow_prototype_store(workflow_prototype_store);
+    }
+    void TearDown() override {
+    }
+
+    std::shared_ptr<PluginModuleProxyFactorySelfContainerTest> factory;
+    std::shared_ptr<INodePrototypeStore> node_prototype_store;
+    std::shared_ptr<IWorkflowPrototypeStore> workflow_prototype_store;
+    PluginModuleRegistry_Derived registry;
+};
+
+TEST_F(PluginModuleRegistryTest, PluginModuleProxyProperlyCreated_p) {
+    registry.initialize();
+
+    std::map<PluginModuleRegistry_Derived::plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>>& plugin_modules = registry.get_plugin_modules();
+
+    auto plugin_module = registry.get_plugin_module_proxy(
+                             PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"});
+    ASSERT_NE(nullptr, plugin_module);
+
+    auto it = plugin_modules.find(std::make_pair(
+                                      mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"));
+
+    ASSERT_NE(plugin_modules.end(), it);
+
+    /* Reference count should be 1 */
+    ASSERT_EQ(1, it->second.use_count());
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, PluginModuleProxyProperlyDestroyedWhenGoesOutOfScope_p) {
+    registry.initialize();
+
+    std::map<PluginModuleRegistry_Derived::plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>>& plugin_modules = registry.get_plugin_modules();
+
+    {
+        auto plugin_module = registry.get_plugin_module_proxy(
+                                 PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"});
+        ASSERT_NE(nullptr, plugin_module);
+
+        std::map<PluginModuleRegistry_Derived::plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>>& plugin_modules = registry.get_plugin_modules();
+        auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"));
+
+        ASSERT_NE(plugin_modules.end(), it);
+        ASSERT_EQ(1, it->second.use_count());
+    }
+
+    auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"));
+
+    ASSERT_NE(plugin_modules.end(), it);
+
+    /* Reference count should be 0, since the 'plugin_module' variable went out of scope */
+    ASSERT_EQ(0, it->second.use_count());
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, PluginModuleProxyNotCreatedAgainWhenAlreadyExists_n) {
+    registry.initialize();
+
+    std::map<PluginModuleRegistry_Derived::plugin_module_map_key, std::weak_ptr<IPluginModuleProxy>>& plugin_modules = registry.get_plugin_modules();
+
+    auto plugin_module_1 = registry.get_plugin_module_proxy(
+                               PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"});
+    ASSERT_NE(nullptr, plugin_module_1);
+
+    /* There should be 2 plugin modules by now :
+     * 1. "default.so", which is created in the 'initialize' function
+     * 2. "dummy.so", which is created in the previous line
+     * */
+    ASSERT_EQ(plugin_modules.size(), 2);
+
+    auto plugin_module_2 = registry.get_plugin_module_proxy(
+                               PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"});
+    ASSERT_NE(nullptr, plugin_module_2);
+
+    /* Size should not be incremented */
+    ASSERT_EQ(plugin_modules.size(), 2);
+
+    auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"));
+    ASSERT_NE(plugin_modules.end(), it);
+
+    /* Reference count should be 2, since both 'plugin_module_1' and 'plugin_module_2' variables are pointing to the same object */
+    ASSERT_EQ(2, it->second.use_count());
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, DummyNodeInfoProperlyCreatedAndRetrieved_p) {
+    registry.initialize();
+
+    bool ret = registry.load_node_prototypes();
+    ASSERT_TRUE(ret);
+
+    auto asr_node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+    ASSERT_NE(nullptr, asr_node);
+
+    auto match_node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_LOGIC, MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH);
+    ASSERT_NE(nullptr, match_node);
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, NonExistingNodeNotRetrieved_n) {
+    registry.initialize();
+
+    bool ret = registry.load_node_prototypes();
+    ASSERT_TRUE(ret);
+
+    auto node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_NONE);
+    ASSERT_EQ(nullptr, node);
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, DummyWorkflowInfoProperlyCreatedAndRetrieved_p) {
+    registry.initialize();
+
+    bool ret = registry.load_workflow_prototypes();
+    ASSERT_TRUE(ret);
+
+    auto workflow = workflow_prototype_store->get_workflow_prototype(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+    ASSERT_NE(nullptr, workflow);
+
+    registry.deinitialize();
+}
+
+TEST_F(PluginModuleRegistryTest, NonExistingWorkflowNotRetrieved_n) {
+    registry.initialize();
+
+    bool ret = registry.load_workflow_prototypes();
+    ASSERT_TRUE(ret);
+
+    auto workflow = workflow_prototype_store->get_workflow_prototype(MMI_STANDARD_WORKFLOW_VOICE_TOUCH);
+    ASSERT_EQ(nullptr, workflow);
+
+    registry.deinitialize();
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp b/tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp
new file mode 100644 (file)
index 0000000..3cc5d6c
--- /dev/null
@@ -0,0 +1,53 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-port-instance-manager.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class PortInstanceManagerTest : public testing::Test {
+public:
+    PortInstanceManagerTest() {
+    }
+    virtual ~PortInstanceManagerTest() {
+    }
+    void SetUp() override {
+    }
+    void TearDown() override {
+    }
+
+    PortInstanceManager manager;
+};
+
+TEST_F(PortInstanceManagerTest, func1) {
+    ASSERT_TRUE(true);
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp b/tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp
new file mode 100644 (file)
index 0000000..957c537
--- /dev/null
@@ -0,0 +1,451 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-self-container.h"
+#include "mmi-workflow-instance-manager.h"
+
+using namespace mmi;
+using namespace mmi::communication;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class CommunicationChannelDummy : public CommunicationChannel {
+public:
+    int connect() override {
+        return MMI_ERROR_NONE;
+    }
+    int disconnect() override {
+        return MMI_ERROR_NONE;
+    }
+    int send(communication::Message *message) override {
+        return MMI_ERROR_NONE;
+    }
+
+};
+
+class NodePrototypeStoreDummy : public INodePrototypeStore {
+public:
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) override {
+        node_prototypes.push_back(prototype);
+        return true;
+    }
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override {
+        for (auto& prototype : node_prototypes) {
+            if (prototype->get_type() == type &&
+                    prototype->get_sub_type() == sub_type) {
+                return prototype;
+            }
+        }
+        return nullptr;
+    }
+    std::vector<std::shared_ptr<NodePrototype>> node_prototypes;
+};
+
+class NodeInstanceManagerDummy : public INodeInstanceManager {
+public:
+    NodeInstanceManagerDummy() {
+        node_prototype_store = std::make_shared<NodePrototypeStoreDummy>();
+
+        auto mic_prototype = std::make_shared<NodePrototype>();
+        mic_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+        mic_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT);
+        PortInfo mic_audio{"AUDIO", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_AUDIO, "audio data"};
+        mic_prototype->add_port_info(mic_audio);
+        node_prototype_store->add_node_prototype(mic_prototype);
+
+        auto asr_prototype = std::make_shared<NodePrototype>();
+        asr_prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR);
+        asr_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+        PortInfo asr_audio{"AUDIO", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_AUDIO, "audio data"};
+        asr_prototype->add_port_info(asr_audio);
+        node_prototype_store->add_node_prototype(asr_prototype);
+    }
+
+    virtual std::shared_ptr<NodeInstance> create_node_instance(
+        mmi_standard_node_type_e node_type,
+        mmi_standard_node_sub_type_e node_sub_type) override {
+        create_requests.push_back(std::make_pair(node_type, node_sub_type));
+        auto node_instance = std::make_shared<NodeInstance>(node_type, node_sub_type);
+        if (node_instance) {
+            node_instance->set_node_prototype_store(node_prototype_store);
+            node_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+            node_instance->initialize();
+        }
+        return node_instance;
+    }
+    virtual bool destroy_node_instance(
+        std::shared_ptr<NodeInstance> node_instance) override {
+        destroy_requests++;
+        return true;
+    }
+    virtual bool link_node_instances(
+        std::shared_ptr<NodeInstance> from_node_instance,
+        std::shared_ptr<NodeInstance> to_node_instance,
+        std::optional<std::string> from_port_name,
+        std::optional<std::string> to_port_name) override {
+        if (from_node_instance && to_node_instance) {
+            std::shared_ptr<NodePrototype> from_prototype =
+                node_prototype_store->get_node_prototype(
+                    from_node_instance->get_node_type(), from_node_instance->get_node_sub_type());
+            std::shared_ptr<NodePrototype> to_prototype =
+                node_prototype_store->get_node_prototype(
+                    to_node_instance->get_node_type(), to_node_instance->get_node_sub_type());
+            if (from_prototype->has_port_with_name(from_port_name.value()) &&
+                    to_prototype->has_port_with_name(to_port_name.value())) {
+                valid_link_requests++;
+            }
+        }
+        return true;
+    }
+    void set_plugin_module_proxy_provider(std::shared_ptr<IPluginModuleProxyProvider> provider) {
+        plugin_module_proxy_provider = std::move(provider);
+    }
+    std::shared_ptr<NodePrototypeStoreDummy> node_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider;
+    std::vector<std::pair<mmi_standard_node_type_e, mmi_standard_node_sub_type_e>> create_requests;
+    size_t destroy_requests{0};
+    size_t valid_link_requests{0};
+};
+
+class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore {
+public:
+    bool add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) override {
+        last_added_workflow_prototype = prototype;
+        return true;
+    }
+    std::shared_ptr<WorkflowPrototype> get_workflow_prototype(mmi_standard_workflow_type_e type) override {
+        last_requested_workflow_type = type;
+        return last_added_workflow_prototype;
+    }
+    mmi_standard_workflow_type_e last_requested_workflow_type;
+    std::shared_ptr<WorkflowPrototype> last_added_workflow_prototype;
+};
+class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider {
+public:
+    PluginModuleProxyProviderDummy() {
+        proxy = factory.create(
+                    PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"});
+    }
+    virtual std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy(
+        const PluginModuleInfo plugin_module_info) override {
+        return proxy;
+    }
+    PluginModuleProxyFactorySelfContainerTest factory;
+    std::shared_ptr<IPluginModuleProxy> proxy;
+};
+
+/* Derived class to access protected members */
+class WorkflowInstanceManager_Derived : public WorkflowInstanceManager {
+public:
+    std::vector<std::shared_ptr<WorkflowInstance>> get_workflow_instances() {
+        return m_workflow_instances;
+    }
+};
+
+class WorkflowInstanceManagerTest : public testing::Test {
+public:
+    WorkflowInstanceManagerTest() {
+        plugin_module_proxy_provider = std::make_shared<PluginModuleProxyProviderDummy>();
+
+        node_instance_manager = std::make_shared<NodeInstanceManagerDummy>();
+        node_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+
+        workflow_prototype_store = std::make_shared<WorkflowPrototypeStoreDummy>();
+
+        workflow_instance_manager = std::make_shared<WorkflowInstanceManager_Derived>();
+        if (workflow_instance_manager) {
+            workflow_instance_manager->set_workflow_prototype_store(workflow_prototype_store);
+            workflow_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+            workflow_instance_manager->set_node_instance_manager(node_instance_manager);
+        }
+    }
+    virtual ~WorkflowInstanceManagerTest() {
+    }
+    void SetUp() override {
+        workflow_instance_manager->initialize();
+        channel.add_observer(workflow_instance_manager.get());
+    }
+    void TearDown() override {
+        workflow_instance_manager->deinitialize();
+        channel.remove_observer(workflow_instance_manager.get());
+    }
+
+    CommunicationChannelDummy channel;
+
+    std::shared_ptr<NodeInstanceManagerDummy> node_instance_manager;
+
+    std::shared_ptr<WorkflowPrototypeStoreDummy> workflow_prototype_store;
+    std::shared_ptr<PluginModuleProxyProviderDummy> plugin_module_proxy_provider;
+
+    std::shared_ptr<WorkflowInstanceManager_Derived> workflow_instance_manager;
+};
+
+TEST_F(WorkflowInstanceManagerTest, WorkflowInstanceCreatedProperly_p) {
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = std::string{"client-1"};
+    msg.local_workflow_instance_id = 0;
+    msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    std::shared_ptr<WorkflowInstance> instance = workflow_instance_manager->get_workflow_instance(
+                msg.sender, msg.local_workflow_instance_id);
+    ASSERT_NE(instance, nullptr);
+}
+
+TEST_F(WorkflowInstanceManagerTest, WorkflowInstanceNotCreatedProperlyWhenDuplicated_n) {
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = std::string{"client-1"};
+    msg.local_workflow_instance_id = 0;
+    msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    std::shared_ptr<WorkflowInstance> instance = workflow_instance_manager->get_workflow_instance(
+                msg.sender, msg.local_workflow_instance_id);
+    ASSERT_NE(instance, nullptr);
+    ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1);
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1);
+}
+
+TEST_F(WorkflowInstanceManagerTest, ProperWorkflowTypeRequestedForInstanceCreation_p) {
+    workflow_prototype_store->last_requested_workflow_type = MMI_STANDARD_WORKFLOW_NONE;
+
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = std::string{"client-1"};
+    msg.local_workflow_instance_id = 0;
+    msg.workflow_type = MMI_STANDARD_WORKFLOW_VOICE_TOUCH;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    std::shared_ptr<WorkflowInstance> instance = workflow_instance_manager->get_workflow_instance(
+                msg.sender, msg.local_workflow_instance_id);
+    ASSERT_NE(instance, nullptr);
+
+    ASSERT_EQ(workflow_prototype_store->last_requested_workflow_type, MMI_STANDARD_WORKFLOW_VOICE_TOUCH);
+}
+
+TEST_F(WorkflowInstanceManagerTest, AllWorkflowInstanceDeletedOnDeinitialize_p) {
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = std::string{"client-1"};
+    msg.local_workflow_instance_id = 0;
+    msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1);
+
+    workflow_instance_manager->deinitialize();
+    ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 0);
+}
+
+TEST_F(WorkflowInstanceManagerTest, NodeInstanceCreationRequestedForWorkflowInstanceCreation_p) {
+    ASSERT_EQ(node_instance_manager->create_requests.size(), 0);
+
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate msg;
+    msg.sender = std::string{"client-1"};
+    msg.local_workflow_instance_id = 0;
+    msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&msg));
+
+    ASSERT_EQ(node_instance_manager->create_requests.size(), 2);
+}
+
+TEST_F(WorkflowInstanceManagerTest, NodeInstanceDestroyRequestedOnWorkflowDestruction_p) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_EQ(node_instance_manager->destroy_requests, 0);
+
+    ClientMessageWorkflowInstanceDestroy destroy_msg;
+    destroy_msg.sender = std::string{"client-1"};
+    destroy_msg.local_workflow_instance_id = 0;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&destroy_msg));
+    ASSERT_EQ(node_instance_manager->destroy_requests, 2);
+}
+
+TEST_F(WorkflowInstanceManagerTest, NodeInstanceDestroyRequestedOnClientDisconnection_p) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_EQ(node_instance_manager->destroy_requests, 0);
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED, std::string{"client-1"});
+
+    ASSERT_EQ(node_instance_manager->destroy_requests, 2);
+}
+
+TEST_F(WorkflowInstanceManagerTest, LinkCreatedIfLinkInfoIsProvided_p) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    prototype->add_link_info(mmi::LinkInfo{
+        "MIC", "AUDIO", "ASR", "AUDIO"});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_NE(node_instance_manager->valid_link_requests, 0);
+}
+
+TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfLinkInfoIsNotProvided_n) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_EQ(node_instance_manager->valid_link_requests, 0);
+}
+
+TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfPortNameIsInvalid_n) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    prototype->add_link_info(mmi::LinkInfo{
+        "MIC", "UNKNOWN_PORT", "ASR", "AUDIO"});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_EQ(node_instance_manager->valid_link_requests, 0);
+}
+
+TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfNodeNameIsInvalid_n) {
+    auto prototype = std::make_shared<WorkflowPrototype>();
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    /* Add 2 nodes to the workflow prototype */
+    prototype->add_node_info(mmi::NodeInfo{
+        "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+    prototype->add_node_info(mmi::NodeInfo{
+        "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+    prototype->add_link_info(mmi::LinkInfo{
+        "UNKNOWN_NODE", "AUDIO", "ASR", "AUDIO"});
+    workflow_prototype_store->add_workflow_prototype(prototype);
+
+    ClientMessageWorkflowInstanceCreate create_msg;
+    create_msg.sender = std::string{"client-1"};
+    create_msg.local_workflow_instance_id = 0;
+    create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND;
+
+    channel.notify_observers(
+        COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast<Message*>(&create_msg));
+
+    ASSERT_EQ(node_instance_manager->valid_link_requests, 0);
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp b/tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp
new file mode 100644 (file)
index 0000000..96d13b8
--- /dev/null
@@ -0,0 +1,184 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-self-container.h"
+#include "mmi-workflow-instance.h"
+#include "mmi-workflow-prototype.h"
+#include "mmi-workflow-prototype-manager.h"
+
+using namespace mmi;
+
+extern "C" {
+/* Add wrapper functions here with __wrap_ and __real prefix
+Example :
+
+int __real_vconf_get_int(const char *in_key, int *intval);
+int __wrap_vconf_get_int(const char *in_key, int *intval)
+{
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+}
+*/
+}
+
+class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore {
+public:
+    bool add_workflow_prototype(std::shared_ptr<WorkflowPrototype> prototype) override {
+        return true;
+    }
+    std::shared_ptr<WorkflowPrototype> get_workflow_prototype(mmi_standard_workflow_type_e type) override {
+        auto prototype = std::make_shared<WorkflowPrototype>();
+        prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+        /* Add 2 nodes to the workflow prototype */
+        prototype->add_node_info(mmi::NodeInfo{
+            "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT});
+        prototype->add_node_info(mmi::NodeInfo{
+            "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR});
+        AttributeAssignmentInfo attribute_assignment_info;
+        attribute_assignment_info.attribute_name = "CANDIDATE";
+        attribute_assignment_info.target_node_name = "ASR";
+        attribute_assignment_info.target_attribute_name = "CANDIDATE";
+        prototype->add_attribute_assignment_info(attribute_assignment_info);
+        return prototype;
+    }
+};
+
+class NodePrototypeStoreDummy : public INodePrototypeStore {
+public:
+    virtual bool add_node_prototype(std::shared_ptr<NodePrototype> prototype) override {
+        node_prototypes.push_back(prototype);
+        return true;
+    }
+    virtual std::shared_ptr<NodePrototype> get_node_prototype(
+        mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override {
+        for (auto& prototype : node_prototypes) {
+            if (prototype->get_type() == type &&
+                    prototype->get_sub_type() == sub_type) {
+                return prototype;
+            }
+        }
+        return nullptr;
+    }
+    std::vector<std::shared_ptr<NodePrototype>> node_prototypes;
+};
+
+class NodeInstanceManagerDummy : public INodeInstanceManager {
+public:
+    NodeInstanceManagerDummy() {
+        node_prototype_store = std::make_shared<NodePrototypeStoreDummy>();
+
+        auto mic_prototype = std::make_shared<NodePrototype>();
+        mic_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE);
+        mic_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT);
+        PortInfo mic_audio{"AUDIO", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_AUDIO, "audio data"};
+        mic_prototype->add_port_info(mic_audio);
+        node_prototype_store->add_node_prototype(mic_prototype);
+
+        auto asr_prototype = std::make_shared<NodePrototype>();
+        asr_prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR);
+        asr_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR);
+        PortInfo asr_audio{"AUDIO", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_AUDIO, "audio data"};
+        asr_prototype->add_port_info(asr_audio);
+        node_prototype_store->add_node_prototype(asr_prototype);
+    }
+
+    virtual std::shared_ptr<NodeInstance> create_node_instance(
+        mmi_standard_node_type_e node_type,
+        mmi_standard_node_sub_type_e node_sub_type) override {
+        auto node_instance = std::make_shared<NodeInstance>(node_type, node_sub_type);
+        if (node_instance) {
+            node_instance->set_node_prototype_store(node_prototype_store);
+            node_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+            node_instance->initialize();
+        }
+        return node_instance;
+    }
+    virtual bool destroy_node_instance(
+        std::shared_ptr<NodeInstance> node_instance) override {
+        return true;
+    }
+    virtual bool link_node_instances(
+        std::shared_ptr<NodeInstance> from_node_instance,
+        std::shared_ptr<NodeInstance> to_node_instance,
+        std::optional<std::string> from_port_name,
+        std::optional<std::string> to_port_name) override {
+        return true;
+    }
+    void set_plugin_module_proxy_provider(std::shared_ptr<IPluginModuleProxyProvider> provider) {
+        plugin_module_proxy_provider = provider;
+    }
+    std::shared_ptr<NodePrototypeStoreDummy> node_prototype_store;
+    std::shared_ptr<IPluginModuleProxyProvider> plugin_module_proxy_provider;
+};
+
+class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider {
+public:
+    PluginModuleProxyProviderDummy() {
+        proxy = factory.create(
+                    PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"});
+    }
+    virtual std::shared_ptr<IPluginModuleProxy> get_plugin_module_proxy(
+        const PluginModuleInfo plugin_module_info) override {
+        return proxy;
+    }
+    PluginModuleProxyFactorySelfContainerTest factory;
+    std::shared_ptr<IPluginModuleProxy> proxy;
+};
+
+class WorkflowInstanceTest : public testing::Test
+{
+public:
+    WorkflowInstanceTest() {
+        workflow_prototype_store = std::make_shared<WorkflowPrototypeStoreDummy>();
+        node_instance_manager = std::make_shared<NodeInstanceManagerDummy>();
+        plugin_module_proxy_provider = std::make_shared<PluginModuleProxyProviderDummy>();
+    }
+    virtual ~WorkflowInstanceTest() {
+    }
+    void SetUp() override {
+        workflow_instance = std::make_shared<WorkflowInstance>();
+        if (workflow_instance) {
+            workflow_instance->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+            workflow_instance->set_workflow_prototype_store(workflow_prototype_store);
+            workflow_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+            workflow_instance->set_node_instance_manager(node_instance_manager);
+        }
+        if (node_instance_manager) {
+            node_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider);
+        }
+    }
+    void TearDown() override {
+        workflow_instance.reset();
+    }
+    std::shared_ptr<WorkflowInstance> workflow_instance;
+    std::shared_ptr<PluginModuleProxyProviderDummy> plugin_module_proxy_provider;
+    std::shared_ptr<WorkflowPrototypeStoreDummy> workflow_prototype_store;
+    std::shared_ptr<NodeInstanceManagerDummy> node_instance_manager;
+};
+
+TEST_F(WorkflowInstanceTest, WorkflowAttributePassedToNodeAttribute_p) {
+    ASSERT_NE(workflow_instance, nullptr);
+    workflow_instance->initialize();
+    mmi_primitive_value_h value = nullptr;
+    mmi_primitive_value_create_int(5, &value);
+    mmi_attribute_h attribute = nullptr;
+    mmi_attribute_create(value, "CANDIDATE", &attribute);
+    ASSERT_TRUE(workflow_instance->set_attribute(attribute));
+    workflow_instance->deinitialize();
+    mmi_attribute_destroy(attribute);
+    mmi_primitive_value_destroy(value);
+}
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp b/tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp
new file mode 100644 (file)
index 0000000..89c1d2d
--- /dev/null
@@ -0,0 +1,68 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mmi-workflow-prototype.h"
+
+using namespace mmi;
+
+extern "C" {
+    /* Add wrapper functions here with __wrap_ and __real prefix
+    Example :
+
+    int __real_vconf_get_int(const char *in_key, int *intval);
+    int __wrap_vconf_get_int(const char *in_key, int *intval)
+    {
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+    }
+    */
+}
+
+namespace {
+
+class WorkflowPrototypeTest : public testing::Test {
+public:
+    WorkflowPrototypeTest() {
+    }
+    virtual ~WorkflowPrototypeTest() {
+    }
+    void SetUp() override {
+        prototype = std::make_shared<WorkflowPrototype>();
+        ASSERT_TRUE(prototype != nullptr);
+    }
+    void TearDown() override {
+    }
+
+    std::shared_ptr<WorkflowPrototype> prototype;
+};
+
+TEST_F(WorkflowPrototypeTest, WorkflowPrototypeValidationSucceeds_p) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+    prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+    ASSERT_TRUE(prototype->is_valid());
+}
+
+TEST_F(WorkflowPrototypeTest, WorkflowPrototypeValidationFailsWithNoType_n) {
+    prototype->set_plugin_module_info(
+        PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"});
+
+    /* No type provided */
+
+    ASSERT_FALSE(prototype->is_valid());
+}
+
+} // namespace
+
+#ifdef TEST_MAIN_REQUIRED
+int main(int argc, char** argv) {
+    std::cout << "Starting tests" << std::endl;
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    return ret;
+}
+#endif
diff --git a/tests/mmi/attribute/mmi-attribute-test.cpp b/tests/mmi/attribute/mmi-attribute-test.cpp
new file mode 100644 (file)
index 0000000..0f85233
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include <gmock/gmock.h>
+
+#include <mmi-error.h>
+#include <mmi-attribute.h>
+
+
+class MMIAttributeTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        mmiAttribute = nullptr;
+        cloned_attribute = nullptr;
+        primitiveValue = nullptr;
+    }
+
+    void TearDown(void) override {
+        if (nullptr != mmiAttribute) {
+            mmi_attribute_destroy(mmiAttribute);
+            mmiAttribute = nullptr;
+        }
+
+        if (nullptr != cloned_attribute) {
+            mmi_attribute_destroy(cloned_attribute);
+            cloned_attribute = nullptr;
+        }
+
+        if (nullptr != primitiveValue) {
+            mmi_primitive_value_destroy(primitiveValue);
+            primitiveValue = nullptr;
+        }
+    }
+
+    mmi_attribute_h mmiAttribute = nullptr;
+    mmi_attribute_h cloned_attribute = nullptr;
+    mmi_primitive_value_h primitiveValue = nullptr;
+};
+
+TEST_F(MMIAttributeTest, MMIAttributeCreate_p) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeCreate_n1) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    EXPECT_EQ(mmi_attribute_create(nullptr, name, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_attribute_create(primitiveValue, nullptr, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_attribute_create(primitiveValue, name, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeCreate_n2) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    EXPECT_EQ(mmi_attribute_create(primitiveValue, "", &mmiAttribute), MMI_ERROR_INVALID_PARAMETER);
+
+    constexpr size_t NAME_LENGTH = 300;
+    char name[NAME_LENGTH];
+    for (auto &c : name) {
+        c = 'a';
+    }
+    name[NAME_LENGTH - 1] = 0;
+    EXPECT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeGetName_p) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    char *temp_name = nullptr;
+    ASSERT_EQ(mmi_attribute_get_name(mmiAttribute, &temp_name), MMI_ERROR_NONE);
+    ASSERT_NE(temp_name, nullptr);
+    EXPECT_STREQ(temp_name, name);
+    free(temp_name);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeGetName_n) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    char *temp_name = nullptr;
+    EXPECT_EQ(mmi_attribute_get_name(nullptr, &temp_name), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_attribute_get_name(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeGetValue_p) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    mmi_primitive_value_h temp_value = nullptr;
+    ASSERT_EQ(mmi_attribute_get_value(mmiAttribute, &temp_value), MMI_ERROR_NONE);
+    ASSERT_NE(temp_value, nullptr);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+    EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+    int value = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE);
+    EXPECT_EQ(value, sourceInt);
+
+    EXPECT_EQ(mmi_primitive_value_destroy(temp_value), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeGetValue_n) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    mmi_primitive_value_h temp_value = nullptr;
+    EXPECT_EQ(mmi_attribute_get_value(nullptr, &temp_value), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_attribute_get_value(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeClone_p) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    ASSERT_EQ(mmi_attribute_clone(mmiAttribute, &cloned_attribute), MMI_ERROR_NONE);
+    ASSERT_NE(cloned_attribute, nullptr);
+
+    char *temp_name = nullptr;
+    ASSERT_EQ(mmi_attribute_get_name(cloned_attribute, &temp_name), MMI_ERROR_NONE);
+    ASSERT_NE(temp_name, nullptr);
+    EXPECT_STREQ(temp_name, name);
+    free(temp_name);
+
+    mmi_primitive_value_h temp_value = nullptr;
+    ASSERT_EQ(mmi_attribute_get_value(mmiAttribute, &temp_value), MMI_ERROR_NONE);
+    ASSERT_NE(temp_value, nullptr);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+    EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+    int value = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE);
+    EXPECT_EQ(value, sourceInt);
+
+    EXPECT_EQ(mmi_primitive_value_destroy(temp_value), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeClone_n) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    EXPECT_EQ(mmi_attribute_clone(nullptr, &cloned_attribute), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_attribute_clone(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIDataDestroy_p1) {
+    constexpr int sourceInt = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    ASSERT_EQ(mmi_attribute_destroy(mmiAttribute), MMI_ERROR_NONE);
+    mmiAttribute = nullptr;
+}
+
+TEST_F(MMIAttributeTest, MMIDataDestroy_n) {
+    ASSERT_EQ(mmi_attribute_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIAttributeTest, MMIAttributeBytesConversion_p) {
+    constexpr int sourceInt = 5;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE);
+    ASSERT_NE(primitiveValue, nullptr);
+
+    constexpr const char *name = "Attribute";
+    ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(mmiAttribute, nullptr);
+
+    unsigned char *bytes = nullptr;
+    size_t size = 0;
+    ASSERT_EQ(mmi_attribute_to_bytes(mmiAttribute, &bytes, &size), MMI_ERROR_NONE);
+    ASSERT_NE(bytes, nullptr);
+
+    ASSERT_EQ(mmi_attribute_destroy(mmiAttribute), MMI_ERROR_NONE);
+    mmiAttribute = nullptr;
+
+    mmi_attribute_h restoredAttribute = nullptr;
+    ASSERT_EQ(mmi_attribute_from_bytes(bytes, size, &restoredAttribute), MMI_ERROR_NONE);
+    ASSERT_NE(restoredAttribute, nullptr);
+
+    free(bytes);
+
+    char *temp_name = nullptr;
+    ASSERT_EQ(mmi_attribute_get_name(restoredAttribute, &temp_name), MMI_ERROR_NONE);
+    ASSERT_NE(temp_name, nullptr);
+    EXPECT_STREQ(temp_name, name);
+    free(temp_name);
+
+    mmi_primitive_value_h temp_value = nullptr;
+    ASSERT_EQ(mmi_attribute_get_value(restoredAttribute, &temp_value), MMI_ERROR_NONE);
+    ASSERT_NE(temp_value, nullptr);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+    int value = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE);
+    EXPECT_EQ(value, sourceInt);
+
+    mmi_primitive_value_destroy(temp_value);
+    temp_value = nullptr;
+
+    mmi_attribute_destroy(restoredAttribute);
+    restoredAttribute = nullptr;
+}
diff --git a/tests/mmi/meson.build b/tests/mmi/meson.build
new file mode 100644 (file)
index 0000000..4834a64
--- /dev/null
@@ -0,0 +1,22 @@
+mmi_tests_srcs = [
+       'mmi-tests.cpp',
+       'mmi-main-test.cpp',
+       'mmi-ipc-test.cpp',
+       'mmi-data-test.cpp',
+       'attribute/mmi-attribute-test.cpp',
+       'primitive-value/mmi-primitive-value-test.cpp',
+       'workflow/mmi-workflow-test.cpp',
+       ]
+
+gmock_dep = dependency('gmock', method : 'pkg-config')
+ecore_dep = dependency('ecore', method : 'pkg-config')
+
+tc = executable(
+       'mmi-tests',
+       mmi_tests_srcs,
+       dependencies : [mmi_declared_dep, gmock_dep, ecore_dep],
+       install_dir : mmi_prefix_bindir,
+       install : true
+       )
+
+test('mmi-tests', tc, args : ['--gtest_output=xml:./mmi-tests.xml'])
diff --git a/tests/mmi/mmi-data-test.cpp b/tests/mmi/mmi-data-test.cpp
new file mode 100644 (file)
index 0000000..393acb0
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include "mmi-tests.h"
+#include <mmi-error.h>
+#include <mmi-data.h>
+
+
+class MMIDataTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        mmiData = nullptr;
+        for (size_t i = 0; i < MAX_ARRAY_LENGTH; i++) {
+            arrayElement[i] = nullptr;
+        }
+    }
+
+    void TearDown(void) override {
+        if (nullptr != mmiData) {
+            mmi_data_destroy(mmiData);
+            mmiData = nullptr;
+        }
+
+        for (size_t i = 0; i < MAX_ARRAY_LENGTH; i++) {
+            if (nullptr != arrayElement[i]) {
+                mmi_data_destroy(arrayElement[i]);
+                arrayElement[i] = nullptr;
+            }
+        }
+    }
+
+    static constexpr size_t MAX_ARRAY_LENGTH = 5;
+
+    mmi_data_h mmiData = nullptr;
+    mmi_data_h arrayElement[MAX_ARRAY_LENGTH] = {nullptr, };
+};
+
+TEST_F(MMIDataTest, MMIDataCreateDataByInt_p) {
+    constexpr int sourceValue = 0;
+    ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByInt_n) {
+    constexpr int sourceValue = 0;
+    EXPECT_EQ(mmi_data_create_int(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByText_p) {
+    constexpr const char *sourceValue = "Hello";
+    ASSERT_EQ(mmi_data_create_text(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByText_n) {
+    constexpr const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_data_create_text(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_create_text(nullptr, &mmiData), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByAudio_p) {
+    constexpr size_t DATA_LENGTH = 10;
+    constexpr char sourceValue[DATA_LENGTH] = {1, };
+    ASSERT_EQ(mmi_data_create_audio(sourceValue, DATA_LENGTH, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByAudio_n) {
+    constexpr size_t DATA_LENGTH = 10;
+    constexpr char sourceValue[DATA_LENGTH] = {1, };
+    EXPECT_EQ(mmi_data_create_audio(nullptr, DATA_LENGTH, &mmiData), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_create_audio(sourceValue, 0, &mmiData), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_create_audio(sourceValue, DATA_LENGTH, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByArray_p) {
+    EXPECT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByArray_n) {
+    EXPECT_EQ(mmi_data_create_array(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataAddElementIntoArray_p) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr int source[COUNT_ELEMENTS] = {1, 2};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+}
+
+TEST_F(MMIDataTest, MMIDataAddElementIntoArray_n1) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE);
+
+    EXPECT_EQ(mmi_data_add_array_element(nullptr, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_add_array_element(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER);
+
+    ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[0]), MMI_ERROR_NONE);
+    arrayElement[0] = nullptr;
+    EXPECT_EQ(mmi_data_add_array_element(mmiData, arrayElement[1]), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataAddElementIntoArray_n2) {
+    constexpr int sourceInt = 1;
+    EXPECT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    ASSERT_NE(arrayElement, nullptr);
+
+    EXPECT_EQ(mmi_data_add_array_element(mmiData, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByStruct_p) {
+    EXPECT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataCreateDataByStruct_n) {
+    EXPECT_EQ(mmi_data_create_struct(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataAddElementIntoStruct_p) {
+    ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+}
+
+TEST_F(MMIDataTest, MMIDataAddElementIntoStruct_n1) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+
+    constexpr const char *name = "Element";
+    ASSERT_EQ(mmi_data_set_struct_element(nullptr, name, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER);
+    ASSERT_EQ(mmi_data_set_struct_element(mmiData, nullptr, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER);
+    ASSERT_EQ(mmi_data_set_struct_element(mmiData, name, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetElementFromStruct_p) {
+    ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    size_t count = 0;
+    ASSERT_EQ(mmi_data_get_struct_count(mmiData, &count), MMI_ERROR_NONE);
+    ASSERT_EQ(count, COUNT_ELEMENTS);
+
+    const char *element_name = nullptr;
+    mmi_data_h element_value = nullptr;
+
+    EXPECT_EQ(mmi_data_get_struct_element_name(mmiData, 0, &element_name), MMI_ERROR_NONE);
+    EXPECT_STREQ(element_name, "Element1");
+    EXPECT_EQ(mmi_data_get_struct_element_value(mmiData, 0, &element_value), MMI_ERROR_NONE);
+    int intValue = 0;
+    EXPECT_EQ(mmi_data_get_int(element_value, &intValue), MMI_ERROR_NONE);
+    EXPECT_EQ(intValue, sourceInt);
+
+    EXPECT_EQ(mmi_data_get_struct_element_name(mmiData, 1, &element_name), MMI_ERROR_NONE);
+    EXPECT_STREQ(element_name, "Element2");
+    EXPECT_EQ(mmi_data_get_struct_element_value(mmiData, 1, &element_value), MMI_ERROR_NONE);
+    const char *textValue = nullptr;
+    EXPECT_EQ(mmi_data_get_text(element_value, &textValue), MMI_ERROR_NONE);
+    EXPECT_STREQ(textValue, sourceText);
+}
+
+TEST_F(MMIDataTest, MMIDataGetType_p) {
+    mmi_data_h tempValue = nullptr;
+    mmi_data_type_e type = MMI_DATA_TYPE_ARRAY;
+
+    EXPECT_EQ(mmi_data_create_int(0, &tempValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_DATA_TYPE_INTEGER);
+    EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE);
+    tempValue = nullptr;
+
+    EXPECT_EQ(mmi_data_create_text("Hello", &tempValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_DATA_TYPE_TEXT);
+    EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE);
+    tempValue = nullptr;
+
+    constexpr size_t length = 10;
+    constexpr char sourceValue[length] = {1, };
+    EXPECT_EQ(mmi_data_create_audio(sourceValue, length, &tempValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_DATA_TYPE_AUDIO);
+    EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE);
+    tempValue = nullptr;
+
+    EXPECT_EQ(mmi_data_create_array(&tempValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_DATA_TYPE_ARRAY);
+    EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE);
+    tempValue = nullptr;
+
+    EXPECT_EQ(mmi_data_create_struct(&tempValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_DATA_TYPE_STRUCT);
+    EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE);
+    tempValue = nullptr;
+}
+
+TEST_F(MMIDataTest, MMIDataGetType_n) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    mmi_data_type_e type = MMI_DATA_TYPE_ARRAY;
+    EXPECT_EQ(mmi_data_get_type(nullptr, &type), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_type(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetIntValue_p) {
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_data_get_int(mmiData, &targetValue), MMI_ERROR_NONE);
+    EXPECT_EQ(targetValue, sourceInt);
+}
+
+TEST_F(MMIDataTest, MMIDataGetIntValue_n1) {
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_data_get_int(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_int(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetIntValue_n2) {
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_data_get_int(mmiData, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetTextValue_p) {
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    const char *textInData = nullptr;
+    EXPECT_EQ(mmi_data_get_text(mmiData, &textInData), MMI_ERROR_NONE);
+    EXPECT_STREQ(textInData, sourceText);
+}
+
+TEST_F(MMIDataTest, MMIDataGetTextValue_n1) {
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    const char *textInData = nullptr;
+    EXPECT_EQ(mmi_data_get_text(nullptr, &textInData), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_text(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetTextValue_n2) {
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    const char *textInData = nullptr;
+    EXPECT_EQ(mmi_data_get_text(mmiData, &textInData), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayCount_p) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr int source[COUNT_ELEMENTS] = {1, 2};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        size_t count = 10;
+        EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE);
+        EXPECT_EQ(count, i);
+
+        ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    size_t count = 10;
+    EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, COUNT_ELEMENTS);
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayCount_n1) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_data_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_array_count(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayCount_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayElement_p) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr int source[COUNT_ELEMENTS] = {1, 2};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, COUNT_ELEMENTS);
+
+    for (size_t i = 0; i < count; i++) {
+        mmi_data_h element = nullptr;
+        EXPECT_EQ(mmi_data_get_array_element(mmiData, i, &element), MMI_ERROR_NONE);
+
+        mmi_data_type_e type = MMI_DATA_TYPE_ANY;
+        EXPECT_EQ(mmi_data_get_type(element, &type), MMI_ERROR_NONE);
+        EXPECT_EQ(type, MMI_DATA_TYPE_INTEGER);
+
+        int data = 0;
+        EXPECT_EQ(mmi_data_get_int(element, &data), MMI_ERROR_NONE);
+        EXPECT_EQ(data, source[i]);
+    }
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayElement_n1) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    mmi_data_h element = nullptr;
+    EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, &element), MMI_ERROR_INVALID_PARAMETER);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr int source[COUNT_ELEMENTS] = {1, 2};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, COUNT_ELEMENTS);
+
+    EXPECT_EQ(mmi_data_get_array_element(nullptr, 0, &element), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_array_element(mmiData, count, &element), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetArrayElement_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    mmi_data_h element = nullptr;
+    EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, &element), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetStructElement_p) {
+    ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+
+    constexpr const char *name = "Element";
+    EXPECT_EQ(mmi_data_set_struct_element(mmiData, name, arrayElement[0]), MMI_ERROR_NONE);
+    arrayElement[0] = nullptr;
+
+    mmi_data_h element = nullptr;
+    EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_NONE);
+    EXPECT_NE(element, nullptr);
+}
+
+TEST_F(MMIDataTest, MMIDataGetStructElement_n1) {
+    ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr const char *name = "Element";
+    mmi_data_h element = nullptr;
+    EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_INVALID_PARAMETER);
+
+    constexpr int sourceInt = 1;
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_data_set_struct_element(mmiData, name, arrayElement[0]), MMI_ERROR_NONE);
+    arrayElement[0] = nullptr;
+
+    EXPECT_EQ(mmi_data_get_struct_element(nullptr, name, &element), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_struct_element(mmiData, nullptr, &element), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataGetStructElement_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr const char *name = "Element";
+    mmi_data_h element = nullptr;
+    EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIDataTest, MMIDataDestroy_p1) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE);
+    mmiData = nullptr;
+}
+
+TEST_F(MMIDataTest, MMIDataDestroy_p2) {
+    const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_data_create_text(sourceValue, &mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE);
+    mmiData = nullptr;
+}
+
+TEST_F(MMIDataTest, MMIDataDestroy_p3) {
+    ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr int source[COUNT_ELEMENTS] = {1, 2};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE);
+    mmiData = nullptr;
+}
+
+TEST_F(MMIDataTest, MMIDataDestroy_p4) {
+    ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE);
+    ASSERT_NE(mmiData, nullptr);
+
+    constexpr int sourceInt = 1;
+    constexpr const char *sourceText = "Hello";
+    ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE);
+
+    constexpr int COUNT_ELEMENTS = 2;
+    constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"};
+    for (size_t i = 0; i < COUNT_ELEMENTS; i++) {
+        ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE);
+        arrayElement[i] = nullptr;
+    }
+
+    ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE);
+    mmiData = nullptr;
+}
+
+TEST_F(MMIDataTest, MMIDataDestroy_n) {
+    ASSERT_EQ(mmi_data_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
diff --git a/tests/mmi/mmi-ipc-test.cpp b/tests/mmi/mmi-ipc-test.cpp
new file mode 100644 (file)
index 0000000..2574314
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include "mmi.h"
+#include "mmi-tests.h"
+#include "mmi-ipc-tidl.h"
+
+#include <Ecore.h>
+#include <rpc-port-internal.h>
+
+static mmi_state_e g_state = MMI_STATE_NONE;
+static int state_changed_cb(mmi_state_e state, void *user_data) {
+    g_state = state;
+    return MMI_ERROR_NONE;
+}
+
+static void wait_until_connected(int msec) {
+    int i = 0;
+    while (g_state != MMI_STATE_READY && i < msec) {
+        ecore_main_loop_iterate();
+        usleep(1000);
+        i++;
+    }
+}
+
+class MMIIpcTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        ecore_init();
+    }
+
+    void TearDown(void) override {
+        ecore_shutdown();
+    }
+};
+
+TEST_F(MMIIpcTest, MMIFWIpcInitSuccess_p) {
+    int res = mmi_initialize();
+
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    mmi_deinitialize();
+}
+
+TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceCreationSuccess_p) {
+    int res = mmi_initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_set_state_changed_cb(state_changed_cb, nullptr);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    wait_until_connected(1000);
+
+    mmi_workflow_instance_h instance = nullptr;
+    res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_unset_state_changed_cb(state_changed_cb);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceSetAttributeSuccess_p) {
+    int res = mmi_initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_set_state_changed_cb(state_changed_cb, nullptr);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    wait_until_connected(1000);
+
+    mmi_workflow_instance_h instance = nullptr;
+    res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    mmi_attribute_h attribute = nullptr;
+    constexpr size_t COMMAND_NUM = 2;
+    const char *commands[] = {"Open", "Close"};
+    res =  mmi_attribute_create_string_array("COMMANDS", commands, COMMAND_NUM, &attribute);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_workflow_instance_set_attribute(instance, attribute);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_unset_state_changed_cb(state_changed_cb);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
+
+TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceActivateSuccess_p) {
+    int res = mmi_initialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_set_state_changed_cb(state_changed_cb, nullptr);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    wait_until_connected(1000);
+
+    mmi_workflow_instance_h instance = nullptr;
+    res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_workflow_instance_activate(instance);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_unset_state_changed_cb(state_changed_cb);
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    res = mmi_deinitialize();
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+}
diff --git a/tests/mmi/mmi-main-test.cpp b/tests/mmi/mmi-main-test.cpp
new file mode 100644 (file)
index 0000000..1abb86a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include "mmi.h"
+#include "mmi-tests.h"
+#include "mmi-ipc-tidl.h"
+
+#include <Ecore.h>
+#include <rpc-port-internal.h>
+
+static int state_changed_cb(mmi_state_e state, void *user_data) {
+    return MMI_ERROR_NONE;
+}
+
+class MMIMainTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        ecore_init();
+    }
+
+    void TearDown(void) override {
+        ecore_shutdown();
+    }
+};
+
+TEST_F(MMIMainTest, MMIMainInit) {
+    int res = mmi_initialize();
+
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    mmi_deinitialize();
+}
+
+TEST_F(MMIMainTest, MmiClientCreateSuccess) {
+    int res = mmi_initialize();
+
+    EXPECT_EQ(res, MMI_ERROR_NONE);
+
+    mmi_deinitialize();
+}
+
+
similarity index 53%
rename from tests/mmi-tests.cpp
rename to tests/mmi/mmi-tests.cpp
index 1db650c..3ad4905 100644 (file)
 
 #include "mmi-tests.h"
 
-int main(int argc, char **argv)
-{
-       auto testResults = false;
+int main(int argc, char **argv) {
+    auto testResults = false;
 
 #ifdef TIZEN_TEST_GCOV
-       setenv("GCOV_PREFIX", "/tmp", 1);
+    setenv("GCOV_PREFIX", "/tmp", 1);
 #endif
-       try
-       {
-               ::testing::InitGoogleMock(&argc, argv);
-               ::testing::FLAGS_gtest_death_test_style = "fast";
-       }
-       catch ( ... )
-       {
-               PRINT("Error occurred while trying to initialize GoogleTest.\n");
-               exit(EXIT_FAILURE);
-       }
+    try {
+        ::testing::InitGoogleMock(&argc, argv);
+        ::testing::FLAGS_gtest_death_test_style = "fast";
+    } catch ( ... ) {
+        PRINT("Error occurred while trying to initialize GoogleTest.\n");
+        exit(EXIT_FAILURE);
+    }
 
-       try
-       {
-               testResults = (RUN_ALL_TESTS() == 0) ? true : false;
-       }
-       catch (const ::testing::internal::GoogleTestFailureException &e)
-       {
-               testResults = false;
-               PRINT("GoogleTestFailureException has been thrown: %s\n", e.what());
-       }
+    try {
+        testResults = (RUN_ALL_TESTS() == 0) ? true : false;
+    } catch (const ::testing::internal::GoogleTestFailureException &e) {
+        testResults = false;
+        PRINT("GoogleTestFailureException has been thrown: %s\n", e.what());
+    }
 
 #ifdef TIZEN_TEST_GCOV
-       __gcov_flush();
+    __gcov_flush();
 #endif
-       return testResults;
+    return (testResults ? 0 : 1);
 }
 
similarity index 96%
rename from tests/mmi-tests.h
rename to tests/mmi/mmi-tests.h
index 862d1f3..d775edf 100644 (file)
@@ -29,7 +29,6 @@
 #ifdef TIZEN_TEST_GCOV
 extern "C" void __gcov_flush(void);
 #endif
-extern void wait_for_connect();
 
 using ::testing::TestWithParam;
 using ::testing::Bool;
diff --git a/tests/mmi/primitive-value/mmi-primitive-value-test.cpp b/tests/mmi/primitive-value/mmi-primitive-value-test.cpp
new file mode 100644 (file)
index 0000000..bb9725c
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+
+#include <gmock/gmock.h>
+
+#include <mmi-error.h>
+#include <mmi-primitive-value.h>
+
+
+class MMIPrimitiveValueTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        primitiveValue = nullptr;
+        arrayElement1 = nullptr;
+        arrayElement2 = nullptr;
+    }
+
+    void TearDown(void) override {
+        if (nullptr != primitiveValue) {
+            mmi_primitive_value_destroy(primitiveValue);
+        }
+
+        if (nullptr != arrayElement1) {
+            mmi_primitive_value_destroy(arrayElement1);
+        }
+
+        if (nullptr != arrayElement2) {
+            mmi_primitive_value_destroy(arrayElement2);
+        }
+
+        primitiveValue = nullptr;
+        arrayElement1 = nullptr;
+        arrayElement2 = nullptr;
+    }
+
+    mmi_primitive_value_h primitiveValue = nullptr;
+    mmi_primitive_value_h arrayElement1 = nullptr;
+    mmi_primitive_value_h arrayElement2 = nullptr;
+};
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByInt_p) {
+    int sourceValue = 0;
+    EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByInt_n) {
+    int sourceValue = 0;
+    EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByFloat_p) {
+    float sourceValue = 0.0;
+    EXPECT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByFloat_n) {
+    float sourceValue = 0.0;
+    EXPECT_EQ(mmi_primitive_value_create_float(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByString_p) {
+    const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByString_n) {
+    const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_create_string(nullptr, &primitiveValue), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByBool_p) {
+    bool sourceValue = true;
+    EXPECT_EQ(mmi_primitive_value_create_bool(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByBool_n) {
+    bool sourceValue = true;
+    EXPECT_EQ(mmi_primitive_value_create_bool(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByArray_p) {
+    EXPECT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByArray_n) {
+    EXPECT_EQ(mmi_primitive_value_create_array(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_p) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int source1 = 1, source2 = 2;
+    ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source2, &arrayElement2), MMI_ERROR_NONE);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_n1) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int source1 = 1;
+    float source2 = 2.0;
+    ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_float(source2, &arrayElement2), MMI_ERROR_NONE);
+
+    EXPECT_EQ(mmi_primitive_value_add_array_element(nullptr, arrayElement1), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_n2) {
+    int sourceValue = 0;
+    EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int source1 = 1;
+    ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE);
+    EXPECT_NE(arrayElement1, nullptr);
+
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetType_p) {
+    mmi_primitive_value_h tempIntValue = nullptr;
+    mmi_primitive_value_type_e tempIntType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+
+    EXPECT_EQ(mmi_primitive_value_create_int(0, &tempIntValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_primitive_value_get_type(tempIntValue, &tempIntType), MMI_ERROR_NONE);
+    EXPECT_EQ(tempIntType, MMI_PRIMITIVE_VALUE_TYPE_INT);
+    EXPECT_EQ(mmi_primitive_value_destroy(tempIntValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_h tempFloatValue = nullptr;
+    mmi_primitive_value_type_e tempFloatType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+
+    EXPECT_EQ(mmi_primitive_value_create_float(0.0, &tempFloatValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_primitive_value_get_type(tempFloatValue, &tempFloatType), MMI_ERROR_NONE);
+    EXPECT_EQ(tempFloatType, MMI_PRIMITIVE_VALUE_TYPE_FLOAT);
+    EXPECT_EQ(mmi_primitive_value_destroy(tempFloatValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_h tempStringValue = nullptr;
+    mmi_primitive_value_type_e tempStringType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+
+    EXPECT_EQ(mmi_primitive_value_create_string("Hello", &tempStringValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_primitive_value_get_type(tempStringValue, &tempStringType), MMI_ERROR_NONE);
+    EXPECT_EQ(tempStringType, MMI_PRIMITIVE_VALUE_TYPE_STRING);
+    EXPECT_EQ(mmi_primitive_value_destroy(tempStringValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_h tempBoolValue = nullptr;
+    mmi_primitive_value_type_e tempBoolType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+
+    EXPECT_EQ(mmi_primitive_value_create_bool(true, &tempBoolValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_primitive_value_get_type(tempBoolValue, &tempBoolType), MMI_ERROR_NONE);
+    EXPECT_EQ(tempBoolType, MMI_PRIMITIVE_VALUE_TYPE_BOOL);
+    EXPECT_EQ(mmi_primitive_value_destroy(tempBoolValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_h tempArrayValue = nullptr;
+    mmi_primitive_value_type_e tempArrayType = MMI_PRIMITIVE_VALUE_TYPE_INT;
+
+    EXPECT_EQ(mmi_primitive_value_create_array(&tempArrayValue), MMI_ERROR_NONE);
+    EXPECT_EQ(mmi_primitive_value_get_type(tempArrayValue, &tempArrayType), MMI_ERROR_NONE);
+    EXPECT_EQ(tempArrayType, MMI_PRIMITIVE_VALUE_TYPE_ARRAY);
+    EXPECT_EQ(mmi_primitive_value_destroy(tempArrayValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetType_n) {
+    float sourceValue = 0.0;
+    ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY;
+    EXPECT_EQ(mmi_primitive_value_get_type(nullptr, &type), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_type(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_p) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, &targetValue), MMI_ERROR_NONE);
+    EXPECT_EQ(targetValue, sourceValue);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_n1) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_n2) {
+    float sourceValue = 0.0;
+    ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    int targetValue = -1;
+    EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_p) {
+    float sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    float targetValue = -1.0;
+    EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, &targetValue), MMI_ERROR_NONE);
+    EXPECT_EQ(targetValue, sourceValue);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_n1) {
+    float sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    float targetValue = -1.0;
+    EXPECT_EQ(mmi_primitive_value_get_float(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    float targetValue = -1.0;
+    EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_p) {
+    const char *sourceValue = "Hello";
+    ASSERT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const char *targetValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, &targetValue), MMI_ERROR_NONE);
+    EXPECT_STREQ(targetValue, sourceValue);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_n1) {
+    const char *sourceValue = "Hello";
+    ASSERT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const char *targetValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_get_string(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const char *targetValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_p) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    size_t count = 10;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, 0);
+
+    int source1 = 1, source2 = 2;
+    ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source2, &arrayElement2), MMI_ERROR_NONE);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, 2);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_n1) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_p) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const int source[] = {1, 2};
+    ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE);
+
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, 2);
+
+    for (size_t i = 0; i < count; i++) {
+        mmi_primitive_value_h element = nullptr;
+        EXPECT_EQ(mmi_primitive_value_get_array_element(primitiveValue, i, &element), MMI_ERROR_NONE);
+
+        mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+        EXPECT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE);
+        EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+        int data = 0;
+        EXPECT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE);
+        EXPECT_EQ(data, source[i]);
+    }
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_n1) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_n2) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p1) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    mmi_primitive_value_h clonedValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    EXPECT_EQ(mmi_primitive_value_get_type(clonedValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+    int data = 0;
+    EXPECT_EQ(mmi_primitive_value_get_int(clonedValue, &data), MMI_ERROR_NONE);
+    EXPECT_EQ(data, sourceValue);
+
+    EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p2) {
+    const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    mmi_primitive_value_h clonedValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    EXPECT_EQ(mmi_primitive_value_get_type(clonedValue, &type), MMI_ERROR_NONE);
+    EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_STRING);
+
+    const char *data = nullptr;
+    EXPECT_EQ(mmi_primitive_value_get_string(clonedValue, &data), MMI_ERROR_NONE);
+    EXPECT_STREQ(data, sourceValue);
+
+    EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p3) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const int source[] = {1, 2};
+    ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    mmi_primitive_value_h clonedValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE);
+
+    size_t count = 0;
+    EXPECT_EQ(mmi_primitive_value_get_array_count(clonedValue, &count), MMI_ERROR_NONE);
+    EXPECT_EQ(count, 2);
+
+    for (size_t i = 0; i < count; i++) {
+        mmi_primitive_value_h element = nullptr;
+        EXPECT_EQ(mmi_primitive_value_get_array_element(clonedValue, i, &element), MMI_ERROR_NONE);
+
+        mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+        EXPECT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE);
+        EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+        int data = 0;
+        EXPECT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE);
+        EXPECT_EQ(data, source[i]);
+    }
+
+    EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_n) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    mmi_primitive_value_h clonedValue = nullptr;
+    EXPECT_EQ(mmi_primitive_value_clone(nullptr, &clonedValue), MMI_ERROR_INVALID_PARAMETER);
+    EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p1) {
+    int sourceValue = 0;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE);
+    primitiveValue = nullptr;
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p2) {
+    const char *sourceValue = "Hello";
+    EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE);
+    primitiveValue = nullptr;
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p3) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    const int source[] = {1, 2};
+    ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE);
+
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE);
+    primitiveValue = nullptr;
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_n) {
+    ASSERT_EQ(mmi_primitive_value_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionIntValue_p) {
+    int sourceValue = 3;
+    ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    unsigned char *bytes = nullptr;
+    size_t length = 0;
+    ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE);
+
+    mmi_primitive_value_destroy(primitiveValue);
+    primitiveValue = nullptr;
+
+    mmi_primitive_value_h restoredValue = nullptr;
+    ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE);
+
+    free(bytes);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE);
+    ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+
+    int data = 0;
+    ASSERT_EQ(mmi_primitive_value_get_int(restoredValue, &data), MMI_ERROR_NONE);
+    ASSERT_EQ(data, sourceValue);
+
+    ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionIntArray_p) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    constexpr size_t SOURCE_COUNT = 2;
+    const int source[SOURCE_COUNT] = {1, 2};
+    ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    unsigned char *bytes = nullptr;
+    size_t length = 0;
+    ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE);
+
+    mmi_primitive_value_destroy(primitiveValue);
+    primitiveValue = nullptr;
+    mmi_primitive_value_destroy(arrayElement1);
+    arrayElement1 = nullptr;
+    mmi_primitive_value_destroy(arrayElement2);
+    arrayElement2 = nullptr;
+
+    mmi_primitive_value_h restoredValue = nullptr;
+    ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE);
+
+    free(bytes);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE);
+    ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_ARRAY);
+
+    size_t count = 0;
+    ASSERT_EQ(mmi_primitive_value_get_array_count(restoredValue, &count), MMI_ERROR_NONE);
+    ASSERT_EQ(count, SOURCE_COUNT);
+
+    for (size_t i = 0; i < count; ++i) {
+        int data = 0;
+        mmi_primitive_value_h element = nullptr;
+        ASSERT_EQ(mmi_primitive_value_get_array_element(restoredValue, i, &element), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE);
+        ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT);
+        ASSERT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE);
+        ASSERT_EQ(data, source[i]);
+    }
+    ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE);
+}
+
+TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionStringArray_p) {
+    ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE);
+    EXPECT_NE(primitiveValue, nullptr);
+
+    constexpr size_t SOURCE_COUNT = 2;
+    const char *source[SOURCE_COUNT] = {"1", "Number 2"};
+    ASSERT_EQ(mmi_primitive_value_create_string(source[0], &arrayElement1), MMI_ERROR_NONE);
+    ASSERT_EQ(mmi_primitive_value_create_string(source[1], &arrayElement2), MMI_ERROR_NONE);
+
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE);
+    arrayElement1 = nullptr;
+    ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE);
+    arrayElement2 = nullptr;
+
+    unsigned char *bytes = nullptr;
+    size_t length = 0;
+    ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE);
+
+    mmi_primitive_value_destroy(primitiveValue);
+    primitiveValue = nullptr;
+    mmi_primitive_value_destroy(arrayElement1);
+    arrayElement1 = nullptr;
+    mmi_primitive_value_destroy(arrayElement2);
+    arrayElement2 = nullptr;
+
+    mmi_primitive_value_h restoredValue = nullptr;
+    ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE);
+
+    free(bytes);
+
+    mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL;
+    ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE);
+    ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_ARRAY);
+
+    size_t count = 0;
+    ASSERT_EQ(mmi_primitive_value_get_array_count(restoredValue, &count), MMI_ERROR_NONE);
+    ASSERT_EQ(count, SOURCE_COUNT);
+
+    for (size_t i = 0; i < count; ++i) {
+        mmi_primitive_value_h element = nullptr;
+        ASSERT_EQ(mmi_primitive_value_get_array_element(restoredValue, i, &element), MMI_ERROR_NONE);
+        ASSERT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE);
+        ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_STRING);
+        const char *data = nullptr;
+        EXPECT_EQ(mmi_primitive_value_get_string(element, &data), MMI_ERROR_NONE);
+        ASSERT_STREQ(data, source[i]);
+    }
+    ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE);
+}
diff --git a/tests/mmi/workflow/mmi-workflow-test.cpp b/tests/mmi/workflow/mmi-workflow-test.cpp
new file mode 100644 (file)
index 0000000..04ecc94
--- /dev/null
@@ -0,0 +1,299 @@
+#include <iostream>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <mmi-workflow.h>
+#include <mmi-node-source.h>
+#include <mmi-node-processor.h>
+#include <mmi-node-logic.h>
+
+#include "mmi-workflow-script-parser.h"
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+extern "C" {
+/* Add wrapper functions here with __wrap_ and __real prefix
+Example :
+
+int __real_vconf_get_int(const char *in_key, int *intval);
+int __wrap_vconf_get_int(const char *in_key, int *intval)
+{
+    if (strcmp(in_key, "TestKey") == 0)
+    return 0;
+
+    return __real_vconf_get_int(in_key, intval);
+}
+*/
+}
+
+class MMIWorkflowTest : public ::testing::Test {
+public:
+    void SetUp(void) override {
+        mmi_workflow_create(&m_workflow);
+
+        mmi_workflow_set_type(m_workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+        m_node_mic = nullptr;
+        /* FIXME: node should be 'found' instead of 'created' */
+        mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &m_node_mic);
+        mmi_workflow_node_add(m_workflow, "MIC", m_node_mic);
+
+        m_node_asr = nullptr;
+        mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &m_node_asr);
+        mmi_workflow_node_add(m_workflow, "ASR", m_node_asr);
+
+        m_node_match = nullptr;
+        mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &m_node_match);
+        mmi_workflow_node_add(m_workflow, "MATCH", m_node_match);
+
+        mmi_workflow_link_nodes_by_names(m_workflow, "MIC", "AUDIO", "ASR", "AUDIO");
+        mmi_workflow_link_nodes_by_names(m_workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT");
+
+        mmi_workflow_attribute_assign(m_workflow, "COMMANDS", "MATCH", "CANDIDATES");
+
+        mmi_workflow_output_assign(m_workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE");
+    }
+
+    void TearDown(void) override {
+        mmi_node_destroy(m_node_match);
+        mmi_node_destroy(m_node_asr);
+        mmi_node_destroy(m_node_mic);
+
+        mmi_workflow_destroy(m_workflow);
+    }
+
+    mmi_workflow_h m_workflow{nullptr};
+    mmi_node_h m_node_mic{nullptr};
+    mmi_node_h m_node_asr{nullptr};
+    mmi_node_h m_node_match{nullptr};
+};
+
+static bool compare_workflow_prototype(mmi_workflow_h first, mmi_workflow_h second) {
+    if (first == nullptr || second == nullptr) {
+        return false;
+    }
+    mmi_workflow_s *first_workflow = (mmi_workflow_s *)first;
+    mmi_workflow_s *second_workflow = (mmi_workflow_s *)second;
+
+    if (first_workflow->type != second_workflow->type) {
+        return false;
+    }
+
+    if (first_workflow->node_info_count != second_workflow->node_info_count) {
+        return false;
+    }
+
+    for (size_t i = 0; i < first_workflow->node_info_count; i++) {
+        if (strcmp(first_workflow->node_infos[i].name, second_workflow->node_infos[i].name) != 0) {
+            return false;
+        }
+
+        mmi_standard_node_type_e first_type;
+        mmi_standard_node_type_e second_type;
+        mmi_node_get_type(first_workflow->node_infos[i].node, &first_type);
+        mmi_node_get_type(second_workflow->node_infos[i].node, &second_type);
+
+        if (first_type != second_type) {
+            return false;
+        }
+
+        switch(first_type) {
+            case MMI_STANDARD_NODE_TYPE_SOURCE:
+                {
+                    mmi_standard_node_source_type_e first_source_type;
+                    mmi_standard_node_source_type_e second_source_type;
+                    mmi_standard_node_get_source_type(first_workflow->node_infos[i].node, &first_source_type);
+                    mmi_standard_node_get_source_type(second_workflow->node_infos[i].node, &second_source_type);
+
+                    if (first_source_type != second_source_type) {
+                        return false;
+                    }
+                }
+                break;
+            case MMI_STANDARD_NODE_TYPE_PROCESSOR:
+                {
+                    mmi_standard_node_processor_type_e first_processor_type;
+                    mmi_standard_node_processor_type_e second_processor_type;
+                    mmi_standard_node_get_processor_type(first_workflow->node_infos[i].node, &first_processor_type);
+                    mmi_standard_node_get_processor_type(second_workflow->node_infos[i].node, &second_processor_type);
+
+                    if (first_processor_type != second_processor_type) {
+                        return false;
+                    }
+                }
+                break;
+            case MMI_STANDARD_NODE_TYPE_LOGIC:
+                {
+                    mmi_standard_node_logic_type_e first_logic_type;
+                    mmi_standard_node_logic_type_e second_logic_type;
+                    mmi_standard_node_get_logic_type(first_workflow->node_infos[i].node, &first_logic_type);
+                    mmi_standard_node_get_logic_type(second_workflow->node_infos[i].node, &second_logic_type);
+
+                    if (first_logic_type != second_logic_type) {
+                        return false;
+                    }
+                }
+                break;
+            default:
+                return false;
+        }
+    }
+
+    if (first_workflow->link_info_count != second_workflow->link_info_count) {
+        return false;
+    }
+
+    for (size_t i = 0; i < first_workflow->link_info_count; i++) {
+        if (strcmp(first_workflow->link_infos[i].from_node_name, second_workflow->link_infos[i].from_node_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->link_infos[i].from_port_name, second_workflow->link_infos[i].from_port_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->link_infos[i].to_node_name, second_workflow->link_infos[i].to_node_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->link_infos[i].to_port_name, second_workflow->link_infos[i].to_port_name) != 0) {
+            return false;
+        }
+    }
+
+    if (first_workflow->attribute_assignment_info_count != second_workflow->attribute_assignment_info_count) {
+        return false;
+    }
+
+    for (size_t i = 0; i < first_workflow->attribute_assignment_info_count; i++) {
+        if (strcmp(first_workflow->attribute_assignment_infos[i].attribute_name, second_workflow->attribute_assignment_infos[i].attribute_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->attribute_assignment_infos[i].target_node_name, second_workflow->attribute_assignment_infos[i].target_node_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->attribute_assignment_infos[i].target_attribute_name, second_workflow->attribute_assignment_infos[i].target_attribute_name) != 0) {
+            return false;
+        }
+    }
+
+    if (first_workflow->output_assignment_info_count != second_workflow->output_assignment_info_count) {
+        return false;
+    }
+
+    for (size_t i = 0; i < first_workflow->output_assignment_info_count; i++) {
+        if (strcmp(first_workflow->output_assignment_infos[i].output_name, second_workflow->output_assignment_infos[i].output_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->output_assignment_infos[i].from_node_name, second_workflow->output_assignment_infos[i].from_node_name) != 0) {
+            return false;
+        }
+        if (strcmp(first_workflow->output_assignment_infos[i].from_port_name, second_workflow->output_assignment_infos[i].from_port_name) != 0) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+TEST_F(MMIWorkflowTest, TestCompareFunction_p1) {
+    mmi_workflow_h workflow = nullptr;
+
+    mmi_workflow_create(&workflow);
+
+    mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND);
+
+    mmi_node_h node_mic = nullptr;
+    /* FIXME: node should be 'found' instead of 'created' */
+    mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic);
+    mmi_workflow_node_add(workflow, "MIC", node_mic);
+
+    mmi_node_h node_asr = nullptr;
+    mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr);
+    mmi_workflow_node_add(workflow, "ASR", node_asr);
+
+    mmi_node_h node_match = nullptr;
+    mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match);
+    mmi_workflow_node_add(workflow, "MATCH", node_match);
+
+    mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO");
+    mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT");
+
+    mmi_workflow_attribute_assign(workflow, "COMMANDS", "MATCH", "CANDIDATES");
+
+    mmi_workflow_output_assign(workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE");
+
+    EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow));
+
+    mmi_node_destroy(node_match);
+    mmi_node_destroy(node_asr);
+    mmi_node_destroy(node_mic);
+
+    mmi_workflow_destroy(workflow);
+}
+
+TEST_F(MMIWorkflowTest, TestCompareFunction_p2) {
+    mmi_workflow_h workflow = nullptr;
+
+    mmi_workflow_clone(m_workflow, &workflow);
+
+    EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow));
+
+    mmi_workflow_destroy(workflow);
+}
+
+TEST_F(MMIWorkflowTest, TestCompareFunction_n1) {
+    mmi_workflow_h workflow = nullptr;
+
+    mmi_workflow_clone(m_workflow, &workflow);
+
+    mmi_node_h node_regex_match = nullptr;
+    mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH, &node_regex_match);
+    mmi_workflow_node_add(workflow, "REGEX_MATCH", node_regex_match);
+
+    EXPECT_FALSE(compare_workflow_prototype(m_workflow, workflow));
+
+    mmi_node_destroy(node_regex_match);
+
+    mmi_workflow_destroy(workflow);
+}
+
+TEST_F(MMIWorkflowTest, MMIWorkflowCreateFromScript_p1) {
+    mmi_workflow_h workflow = NULL;
+    const char *script = NULL;
+    int ret = MMI_ERROR_NONE;
+
+    std::string script_data = R"(
+@workflow
+name : WAKEUPLESS_COMMAND
+
+@node-list
+[Source] MIC_AMBIENT as MIC
+[Processor] ASR as ASR
+[Logic] FIXED_STRING_MATCH as MATCH
+
+@link-list
+MIC.AUDIO -> ASR.AUDIO
+ASR.FINAL_RESULT -> MATCH.TEXT
+
+@attribute-list
+MATCH.CANDIDATES as COMMANDS
+
+@output-list
+MATCH.MATCHED_CANDIDATE as COMMAND
+    )";
+
+    std::vector<std::string> lines;
+    std::stringstream ss(script_data);
+    std::string line;
+    while (std::getline(ss, line)) {
+        lines.push_back(line);
+    }
+
+    WorkflowScriptParser parser;
+    parser.parse(lines, &workflow);
+
+    EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow));
+
+    mmi_workflow_destroy(workflow);
+}
+
diff --git a/tests/wait-helper.cpp b/tests/wait-helper.cpp
deleted file mode 100644 (file)
index d5a387b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "mmi.h"
-#include "mmi-tests.h"
-#include "mmi-ipc.h"
-
-#include <Ecore.h>
-
-void wait_for_connect()
-{
-       int timer = 0;
-       while (!mmi_ipc_is_connected() && timer < MAX_WAIT_TIME)
-       {
-               ecore_main_loop_iterate();
-               timer++;
-       }
-}
\ No newline at end of file
index ed51938..3d5f6f0 100644 (file)
@@ -1,8 +1,10 @@
 interface mmi {
-       void result_cb(int input_event_type, string result_out) delegate;
+       void result_cb(string source_name, bundle data) delegate;
 
-       int register_input_event(int input_event_type, result_cb callback);
-
-       int activate_input_event(int input_event_type);
-       int deactivate_input_event(int input_event_type);
+       int workflow_instance_create(in int local_workflow_instance_id, in int workflow_type);
+       int workflow_instance_destroy(in int local_workflow_instance_id);
+       int workflow_instance_set_attribute(in int local_workflow_instance_id, in bundle attribute);
+       int workflow_instance_activate(in int local_workflow_instance_id);
+       int workflow_instance_deactivate(in int local_workflow_instance_id);
+       int workflow_instance_register_result_callback(in int local_workflow_instance_id, result_cb callback);
 }