aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt44
-rw-r--r--src/plugins/CMakeLists.txt182
-rw-r--r--src/plugins/TestMain.cpp20
-rw-r--r--src/plugins/VshlApi.cpp607
-rw-r--r--src/plugins/VshlApi.h46
-rw-r--r--src/plugins/afb/AFBApiImpl.cpp87
-rw-r--r--src/plugins/afb/AFBApiImpl.h61
-rw-r--r--src/plugins/afb/AFBRequestImpl.cpp37
-rw-r--r--src/plugins/afb/AFBRequestImpl.h49
-rw-r--r--src/plugins/afb/include/AFBEventImpl.h77
-rw-r--r--src/plugins/afb/src/AFBEventImpl.cpp89
-rwxr-xr-xsrc/plugins/agreement.sh43
-rw-r--r--src/plugins/capabilities/CapabilitiesFactory.cpp57
-rw-r--r--src/plugins/capabilities/CapabilitiesFactory.h63
-rw-r--r--src/plugins/capabilities/CapabilityMessagingService.cpp117
-rw-r--r--src/plugins/capabilities/CapabilityMessagingService.h82
-rw-r--r--src/plugins/capabilities/communication/include/PhoneControlCapability.h52
-rw-r--r--src/plugins/capabilities/communication/include/PhoneControlMessages.h128
-rw-r--r--src/plugins/capabilities/communication/src/PhoneControlCapability.cpp42
-rw-r--r--src/plugins/capabilities/core/include/MessageChannel.h68
-rw-r--r--src/plugins/capabilities/core/include/PublisherForwarder.h73
-rw-r--r--src/plugins/capabilities/core/include/SubscriberForwarder.h84
-rw-r--r--src/plugins/capabilities/core/src/MessageChannel.cpp51
-rw-r--r--src/plugins/capabilities/core/src/PublisherForwarder.cpp69
-rw-r--r--src/plugins/capabilities/core/src/SubscriberForwarder.cpp139
-rw-r--r--src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h52
-rw-r--r--src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h50
-rw-r--r--src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp42
-rw-r--r--src/plugins/capabilities/navigation/include/NavigationCapability.h52
-rw-r--r--src/plugins/capabilities/navigation/include/NavigationMessages.h48
-rw-r--r--src/plugins/capabilities/navigation/src/NavigationCapability.cpp42
-rw-r--r--src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp96
-rw-r--r--src/plugins/capabilities/test/PublisherForwarderTest.cpp116
-rw-r--r--src/plugins/capabilities/test/SubscriberForwarderTest.cpp262
-rw-r--r--src/plugins/cmake/gtest.cmake44
-rw-r--r--src/plugins/core/VRRequestProcessor.h75
-rw-r--r--src/plugins/core/VRRequestProcessorImpl.cpp72
-rw-r--r--src/plugins/core/include/VRAgentsObserver.h67
-rw-r--r--src/plugins/core/include/VRRequest.h80
-rw-r--r--src/plugins/core/include/VRRequestProcessorDelegate.h88
-rw-r--r--src/plugins/core/src/VRAgentsObserverImpl.cpp54
-rw-r--r--src/plugins/core/src/VRRequestImpl.cpp92
-rw-r--r--src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp93
-rw-r--r--src/plugins/core/test/VRRequestProcessorTest.cpp213
-rw-r--r--src/plugins/core/test/VRRequestTest.cpp101
-rw-r--r--src/plugins/interfaces/afb/IAFBApi.h98
-rw-r--r--src/plugins/interfaces/capabilities/ICapability.h58
-rw-r--r--src/plugins/interfaces/utilities/events/IEventFilter.h46
-rw-r--r--src/plugins/interfaces/utilities/logging/ILogger.h42
-rw-r--r--src/plugins/interfaces/voiceagents/IVoiceAgent.h95
-rw-r--r--src/plugins/interfaces/voiceagents/IVoiceAgentsChangeObserver.h80
-rw-r--r--src/plugins/test/common/ConsoleLogger.cpp31
-rw-r--r--src/plugins/test/common/ConsoleLogger.h37
-rw-r--r--src/plugins/test/mocks/AFBApiMock.h41
-rw-r--r--src/plugins/test/mocks/AFBEventMock.h47
-rw-r--r--src/plugins/test/mocks/AFBRequestMock.h33
-rw-r--r--src/plugins/test/mocks/CapabilityMock.h35
-rw-r--r--src/plugins/test/mocks/VoiceAgentsChangeObserverMock.h38
-rw-r--r--src/plugins/utilities/events/EventRouter.cpp68
-rw-r--r--src/plugins/utilities/events/EventRouter.h66
-rw-r--r--src/plugins/utilities/logging/Logger.cpp56
-rw-r--r--src/plugins/utilities/logging/Logger.h53
-rw-r--r--src/plugins/utilities/uuid/UUIDGeneration.cpp137
-rw-r--r--src/plugins/utilities/uuid/UUIDGeneration.h42
-rw-r--r--src/plugins/voiceagents/VoiceAgentEventNames.h38
-rw-r--r--src/plugins/voiceagents/VoiceAgentsDataManager.h135
-rw-r--r--src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp272
-rw-r--r--src/plugins/voiceagents/include/VoiceAgent.h95
-rw-r--r--src/plugins/voiceagents/include/VoiceAgentEventsHandler.h95
-rw-r--r--src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp139
-rw-r--r--src/plugins/voiceagents/src/VoiceAgentImpl.cpp126
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentTest.cpp94
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp294
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentsTestData.h67
-rw-r--r--src/vshl-apidef.h43
-rw-r--r--src/vshl-apidef.json109
-rw-r--r--src/vshl-binding.c116
-rw-r--r--src/vshl-binding.h25
78 files changed, 6857 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..729dcb6
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+###########################################################################
+# Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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.
+###########################################################################
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(vshl)
+ set(CMAKE_CXX_STANDARD 11)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+ set(CMAKE_CXX_EXTENSIONS OFF)
+
+ # Define project Targets
+ add_library(${TARGET_NAME} MODULE
+ ${TARGET_NAME}-binding.c
+ )
+
+ set(OPENAPI_DEF "${TARGET_NAME}-apidef" CACHE STRING "name and path to the JSON API definition without extension")
+
+ # Binder exposes a unique public entry point
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ PREFIX "afb-"
+ LABELS "BINDINGV3"
+ LINK_FLAGS ${BINDINGS_LINK_FLAG}
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ afb-helpers
+ ctl-utilities
+ ${link_libraries})
+
+add_subdirectory("plugins") \ No newline at end of file
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 0000000..06ef7a4
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1,182 @@
+###########################################################################
+# Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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.
+###########################################################################
+
+PROJECT_TARGET_ADD(vshl-api)
+
+ set(CMAKE_CXX_STANDARD 11)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+ set(CMAKE_CXX_EXTENSIONS OFF)
+
+ set(VSHL_LIB_SRC
+ ${CMAKE_CURRENT_SOURCE_DIR}/VshlApi.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/VshlApi.h
+
+ # Interfaces
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/afb/IAFBApi.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/capabilities/ICapability.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/utilities/events/IEventFilter.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/utilities/logging/ILogger.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/voiceagents/IVoiceAgent.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/voiceagents/IVoiceAgentsChangeObserver.h
+
+ # AFB
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBApiImpl.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBApiImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBRequestImpl.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBRequestImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/include/AFBEventImpl.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/afb/src/AFBEventImpl.cpp
+
+ # VoiceAgents
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/VoiceAgentsDataManager.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/VoiceAgentsDataManagerImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/VoiceAgentEventNames.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/include/VoiceAgent.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/src/VoiceAgentImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/include/VoiceAgentEventsHandler.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/src/VoiceAgentEventsHandler.cpp
+
+ # Core
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/VRRequestProcessor.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/VRRequestProcessorImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/include/VRAgentsObserver.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/src/VRAgentsObserverImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/include/VRRequest.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/src/VRRequestImpl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/include/VRRequestProcessorDelegate.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/src/VRRequestProcessorDelegateImpl.cpp
+
+ #Capabilities
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilitiesFactory.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilitiesFactory.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilityMessagingService.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilityMessagingService.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/MessageChannel.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/MessageChannel.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/PublisherForwarder.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/PublisherForwarder.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/SubscriberForwarder.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/SubscriberForwarder.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/include/PhoneControlMessages.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/include/PhoneControlCapability.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/src/PhoneControlCapability.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/include/NavigationMessages.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/include/NavigationCapability.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/src/NavigationCapability.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/include/GuiMetadataMessages.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/include/GuiMetadataCapability.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/src/GuiMetadataCapability.cpp
+
+ #Utilities
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/events/EventRouter.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/events/EventRouter.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logging/Logger.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logging/Logger.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/uuid/UUIDGeneration.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/utilities/uuid/UUIDGeneration.cpp
+ )
+
+ # Define targets
+ ADD_LIBRARY(${TARGET_NAME} MODULE
+ ${VSHL_LIB_SRC}
+ )
+
+ # VSHL plugin properties
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ LABELS "PLUGIN"
+ PREFIX ""
+ SUFFIX ".ctlso"
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ # Define target includes
+ TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME}
+ PUBLIC ${GLIB_PKG_INCLUDE_DIRS}
+ PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}"
+ PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib"
+ )
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ afb-helpers
+ ${GLIB_PKG_LIBRARIES}
+ ${link_libraries}
+ )
+
+ option(ENABLE_UNIT_TESTS "Build unit tests or not" OFF)
+ if (ENABLE_UNIT_TESTS)
+ execute_process(
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/agreement.sh"
+ RESULT_VARIABLE AGREEMENT_RESULT
+ )
+ message(STATUS "Agreement Result: ${AGREEMENT_RESULT}")
+ if (${AGREEMENT_RESULT} MATCHES "1")
+ message(FATAL_ERROR "User agreement not accepted. Quitting")
+ endif()
+
+ include(cmake/gtest.cmake)
+
+ set(VSHL_TEST_SRC ${VSHL_LIB_SRC})
+ list(APPEND VSHL_TEST_SRC
+ # Main
+ ${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp
+
+ # Test common
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/common/ConsoleLogger.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/common/ConsoleLogger.cpp
+
+ # Test Mocks
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBApiMock.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBEventMock.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBRequestMock.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/CapabilityMock.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/VoiceAgentsChangeObserverMock.h
+
+ # Capabilities
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/CapabilityMessagingServiceTest.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/PublisherForwarderTest.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/SubscriberForwarderTest.cpp
+
+ # Core
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/test/VRRequestTest.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/core/test/VRRequestProcessorTest.cpp
+
+ # VoiceAgents
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/test/VoiceAgentTest.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/voiceagents/test/VoiceAgentsDataManagerTest.cpp
+ )
+
+ ADD_EXECUTABLE(${TARGET_NAME}_Test
+ ${VSHL_TEST_SRC}
+ )
+
+ TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME}_Test
+ PUBLIC ${GLIB_PKG_INCLUDE_DIRS}
+ PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}"
+ PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib"
+ )
+
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}_Test
+ afb-helpers
+ libgtest
+ libgmock
+ ${GLIB_PKG_LIBRARIES}
+ ${link_libraries}
+ )
+
+ ENABLE_TESTING()
+ ADD_TEST(VshlTest ${TARGET_NAME}_Test)
+ endif() \ No newline at end of file
diff --git a/src/plugins/TestMain.cpp b/src/plugins/TestMain.cpp
new file mode 100644
index 0000000..d4fcbec
--- /dev/null
+++ b/src/plugins/TestMain.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "gtest/gtest.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+} \ No newline at end of file
diff --git a/src/plugins/VshlApi.cpp b/src/plugins/VshlApi.cpp
new file mode 100644
index 0000000..f2c7b7c
--- /dev/null
+++ b/src/plugins/VshlApi.cpp
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "VshlApi.h"
+
+#include <list>
+
+#include "afb/AFBApiImpl.h"
+#include "afb/AFBRequestImpl.h"
+#include "capabilities/CapabilitiesFactory.h"
+#include "capabilities/CapabilityMessagingService.h"
+#include "core/VRRequestProcessor.h"
+#include "utilities/events/EventRouter.h"
+#include "utilities/logging/Logger.h"
+#include "voiceagents/VoiceAgentEventNames.h"
+#include "voiceagents/VoiceAgentsDataManager.h"
+
+#include "json.hpp"
+
+using namespace std;
+
+CTLP_CAPI_REGISTER("vshl-api");
+
+static std::string TAG = "vshl::plugins::VshlAPI";
+
+static std::string VA_JSON_ATTR_DEFAULT = "default";
+static std::string VA_JSON_ATTR_AGENTS = "agents";
+static std::string VA_JSON_ATTR_ID = "id";
+static std::string VA_JSON_ATTR_NAME = "name";
+static std::string VA_JSON_ATTR_API = "api";
+static std::string VA_JSON_ATTR_ACTIVE = "active";
+static std::string VA_JSON_ATTR_WWS = "wakewords";
+static std::string VA_JSON_ATTR_ACTIVE_WW = "activewakeword";
+static std::string VA_JSON_ATTR_DESCRIPTION = "description";
+static std::string VA_JSON_ATTR_VENDOR = "vendor";
+
+static std::string STARTLISTENING_JSON_ATTR_REQUEST = "request_id";
+
+static std::string EVENTS_JSON_ATTR_VA_ID = "va_id";
+static std::string EVENTS_JSON_ATTR_EVENTS = "events";
+
+static std::string CAPABILITIES_JSON_ATTR_ACTION = "action";
+static std::string CAPABILITIES_JSON_ATTR_ACTIONS = "actions";
+static std::string CAPABILITIES_JSON_ATTR_PAYLOAD = "payload";
+
+static std::shared_ptr<vshl::utilities::logging::Logger> sLogger;
+static std::shared_ptr<vshl::common::interfaces::IAFBApi> sAfbApi;
+static std::unique_ptr<vshl::capabilities::CapabilitiesFactory> sCapabilitiesFactory;
+static std::unique_ptr<vshl::capabilities::CapabilityMessagingService> sCapabilityMessagingService;
+static std::unique_ptr<vshl::core::VRRequestProcessor> sVRRequestProcessor;
+static std::unique_ptr<vshl::voiceagents::VoiceAgentsDataManager> sVoiceAgentsDataManager;
+static std::unique_ptr<vshl::utilities::events::EventRouter> sEventRouter;
+
+using json = nlohmann::json;
+using Level = vshl::utilities::logging::Logger::Level;
+
+CTLP_ONLOAD(plugin, ret) {
+ if (plugin->api == nullptr) {
+ return -1;
+ }
+
+ // Logger
+ sLogger = vshl::utilities::logging::Logger::create(plugin->api);
+ // sLogger->log(Level::INFO, TAG, "Vshl plugin loaded & initialized.");
+
+ // AFB Wrapper
+ sAfbApi = vshl::afb::AFBApiImpl::create(plugin->api);
+
+ // VRRequestProcessor
+ auto vrRequestProcessorDelegate = vshl::core::VRRequestProcessorDelegate::create(sLogger, sAfbApi);
+ sVRRequestProcessor = vshl::core::VRRequestProcessor::create(sLogger, vrRequestProcessorDelegate);
+ if (!sVRRequestProcessor) {
+ sLogger->log(Level::ERROR, TAG, "Failed to create VRRequestProcessor");
+ return -1;
+ }
+
+ // VoiceAgentDataManager
+ sVoiceAgentsDataManager = vshl::voiceagents::VoiceAgentsDataManager::create(sLogger, sAfbApi);
+ if (!sVoiceAgentsDataManager) {
+ sLogger->log(Level::ERROR, TAG, "Failed to create VoiceAgentsDataManager");
+ return -1;
+ }
+ sVoiceAgentsDataManager->addVoiceAgentsChangeObserver(sVRRequestProcessor->getVoiceAgentsChangeObserver());
+
+ // EventRouter
+ sEventRouter = vshl::utilities::events::EventRouter::create(sLogger);
+ if (!sEventRouter) {
+ sLogger->log(Level::ERROR, TAG, "Failed to create EventRouter");
+ return -1;
+ }
+ sEventRouter->addEventFilter(sVoiceAgentsDataManager->getEventFilter());
+
+ sCapabilitiesFactory = vshl::capabilities::CapabilitiesFactory::create();
+ if (!sCapabilitiesFactory) {
+ sLogger->log(Level::ERROR, TAG, "Failed to create CapabilitiesFactory");
+ return -1;
+ }
+
+ sCapabilityMessagingService = vshl::capabilities::CapabilityMessagingService::create(sLogger, sAfbApi);
+ if (!sCapabilityMessagingService) {
+ sLogger->log(Level::ERROR, TAG, "Failed to create CapabilityMessagingService");
+ return -1;
+ }
+
+ return 0;
+}
+
+CTLP_CAPI(onAuthStateEvent, source, argsJ, eventJ) {
+ if (sEventRouter == nullptr) {
+ return -1;
+ }
+
+ string eventName = vshl::voiceagents::VSHL_EVENT_AUTH_STATE_EVENT;
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find(EVENTS_JSON_ATTR_VA_ID) == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onAuthStateEvent: No voiceagent id found.");
+ return -1;
+ }
+ std::string voiceAgentId(eventJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ if (!sEventRouter->handleIncomingEvent(eventName, voiceAgentId, json_object_to_json_string(eventJ))) {
+ sLogger->log(Level::ERROR, TAG, "onAuthStateEvent: Failed to handle.");
+ return -1;
+ }
+
+ return 0;
+}
+
+CTLP_CAPI(onConnectionStateEvent, source, argsJ, eventJ) {
+ if (sEventRouter == nullptr) {
+ return -1;
+ }
+
+ string eventName = vshl::voiceagents::VSHL_EVENT_CONNECTION_STATE_EVENT;
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find(EVENTS_JSON_ATTR_VA_ID) == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onConnectionStateEvent: No voiceagent id found.");
+ return -1;
+ }
+ std::string voiceAgentId(eventJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ if (!sEventRouter->handleIncomingEvent(eventName, voiceAgentId, json_object_to_json_string(eventJ))) {
+ sLogger->log(Level::ERROR, TAG, "onConnectionStateEvent: Failed to handle.");
+ return -1;
+ }
+
+ return 0;
+}
+
+CTLP_CAPI(onDialogStateEvent, source, argsJ, eventJ) {
+ if (sEventRouter == nullptr) {
+ return -1;
+ }
+
+ string eventName = vshl::voiceagents::VSHL_EVENT_DIALOG_STATE_EVENT;
+ json eventJson = json::parse(json_object_to_json_string(eventJ));
+ if (eventJson.find(EVENTS_JSON_ATTR_VA_ID) == eventJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "onDialogStateEvent: No voiceagent id found.");
+ return -1;
+ }
+ std::string voiceAgentId(eventJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ if (!sEventRouter->handleIncomingEvent(eventName, voiceAgentId, json_object_to_json_string(eventJ))) {
+ sLogger->log(Level::ERROR, TAG, "onDialogStateEvent: Failed to handle.");
+ return -1;
+ }
+
+ return 0;
+}
+
+CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: Voice service not initialized.");
+ return -1;
+ }
+
+ if (argsJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "loadVoiceAgentsConfig: No arguments supplied.");
+ return -1;
+ }
+
+ json agentsConfigJson = json::parse(json_object_to_json_string(argsJ));
+ if (agentsConfigJson.find(VA_JSON_ATTR_AGENTS) == agentsConfigJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "loadVoiceAgentsConfig: No agents object found in agents json");
+ return -1;
+ }
+
+ json agentsJson = agentsConfigJson[VA_JSON_ATTR_AGENTS];
+ for (auto agentIt = agentsJson.begin(); agentIt != agentsJson.end(); ++agentIt) {
+ json agentJson = *agentIt;
+
+ if (agentJson.find(VA_JSON_ATTR_ID) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_ACTIVE) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_NAME) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_API) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_WWS) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_ACTIVE_WW) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_DESCRIPTION) == agentJson.end() ||
+ agentJson.find(VA_JSON_ATTR_VENDOR) == agentJson.end()) {
+ std::stringstream error;
+ error << "loadVoiceAgentsConfig: One or more missing params in agent "
+ "config "
+ << agentJson.dump();
+ sLogger->log(Level::WARNING, TAG, error.str().c_str());
+ continue;
+ }
+
+ std::string id(agentJson[VA_JSON_ATTR_ID].get<string>());
+ std::string name(agentJson[VA_JSON_ATTR_NAME].get<string>());
+ std::string api(agentJson[VA_JSON_ATTR_API].get<string>());
+ std::string description(agentJson[VA_JSON_ATTR_DESCRIPTION].get<string>());
+ std::string vendor(agentJson[VA_JSON_ATTR_VENDOR].get<string>());
+ std::string activeWakeword(agentJson[VA_JSON_ATTR_ACTIVE_WW].get<string>());
+ bool isActive(agentJson[VA_JSON_ATTR_ACTIVE].get<bool>());
+
+ shared_ptr<unordered_set<string>> wakewords = std::make_shared<unordered_set<string>>();
+ json wakewordsJson = agentJson[VA_JSON_ATTR_WWS];
+ for (auto wwIt = wakewordsJson.begin(); wwIt != wakewordsJson.end(); ++wwIt) {
+ wakewords->insert(wwIt->get<string>());
+ }
+
+ sVoiceAgentsDataManager->addNewVoiceAgent(
+ id, name, description, api, vendor, activeWakeword, isActive, wakewords);
+ }
+
+ // Set the default agent.
+ if (agentsConfigJson.find(VA_JSON_ATTR_DEFAULT) == agentsConfigJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "loadVoiceAgentsConfig: No default agent found in agents json");
+ return -1;
+ }
+ std::string defaultAgentId(agentsConfigJson[VA_JSON_ATTR_DEFAULT].get<string>());
+ sVoiceAgentsDataManager->setDefaultVoiceAgent(defaultAgentId);
+
+ return 0;
+}
+
+CTLP_CAPI(startListening, source, argsJ, eventJ) {
+ if (sVRRequestProcessor == nullptr) {
+ return -1;
+ }
+
+ int result = 0;
+ string requestId = sVRRequestProcessor->startListening();
+
+ if (!requestId.empty()) {
+ json responseJson;
+ responseJson[STARTLISTENING_JSON_ATTR_REQUEST] = requestId;
+ AFB_ReqSuccess(source->request, json_tokener_parse(responseJson.dump().c_str()), NULL);
+ } else {
+ AFB_ReqFail(source->request, NULL, "Failed to startListening...");
+ }
+
+ return 0;
+}
+
+CTLP_CAPI(cancelListening, source, argsJ, eventJ) {
+ return 0;
+}
+
+CTLP_CAPI(enumerateVoiceAgents, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ return -1;
+ }
+
+ auto agents = sVoiceAgentsDataManager->getAllVoiceAgents();
+ std::string defaultAgentId(sVoiceAgentsDataManager->getDefaultVoiceAgent());
+
+ json responseJson;
+ json agentsJson = json::array();
+
+ for (auto agent : agents) {
+ json agentJson;
+ agentJson[VA_JSON_ATTR_ID] = agent->getId();
+ agentJson[VA_JSON_ATTR_NAME] = agent->getName();
+ agentJson[VA_JSON_ATTR_DESCRIPTION] = agent->getDescription();
+ agentJson[VA_JSON_ATTR_API] = agent->getApi();
+ agentJson[VA_JSON_ATTR_VENDOR] = agent->getVendor();
+ agentJson[VA_JSON_ATTR_ACTIVE] = agent->isActive();
+ agentJson[VA_JSON_ATTR_ACTIVE_WW] = agent->getActiveWakeword();
+
+ auto wakewords = agent->getWakeWords();
+ if (wakewords != nullptr) {
+ json wakewordsJson;
+ for (auto wakeword : *wakewords) {
+ wakewordsJson.push_back(wakeword);
+ }
+ agentJson[VA_JSON_ATTR_WWS] = wakewordsJson;
+ }
+
+ agentsJson.push_back(agentJson);
+ }
+
+ responseJson[VA_JSON_ATTR_AGENTS] = agentsJson;
+ responseJson[VA_JSON_ATTR_DEFAULT] = defaultAgentId;
+
+ AFB_ReqSuccess(source->request, json_tokener_parse(responseJson.dump().c_str()), NULL);
+
+ return 0;
+}
+
+CTLP_CAPI(subscribe, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "subscribe: No arguments supplied.");
+ return -1;
+ }
+
+ json subscribeJson = json::parse(json_object_to_json_string(eventJ));
+ if (subscribeJson.find(EVENTS_JSON_ATTR_VA_ID) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "subscribe: No voiceagent id found in subscribe json");
+ return -1;
+ }
+ std::string voiceAgentId(subscribeJson[EVENTS_JSON_ATTR_VA_ID].get<string>());
+
+ if (subscribeJson.find(EVENTS_JSON_ATTR_EVENTS) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "subscribe: No events array found in subscribe json");
+ return -1;
+ }
+ list<string> events(subscribeJson[EVENTS_JSON_ATTR_EVENTS].get<list<string>>());
+
+ // Subscribe this client for the listed events.
+ auto request = vshl::afb::AFBRequestImpl::create(source->request);
+ for (auto event : events) {
+ if (!sVoiceAgentsDataManager->subscribeToVshlEventFromVoiceAgent(*request, event, voiceAgentId)) {
+ sLogger->log(Level::ERROR, TAG, "subscribe: Failed to subscribe to event: " + event);
+ return -1;
+ }
+ }
+
+ AFB_ReqSuccess(source->request, json_object_new_string("Subscription to events successfully completed."), NULL);
+
+ return 0;
+}
+
+CTLP_CAPI(setDefaultVoiceAgent, source, argsJ, eventJ) {
+ if (sVoiceAgentsDataManager == nullptr) {
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "setDefaultVoiceAgent: No arguments supplied.");
+ return -1;
+ }
+
+ json jsonRequest = json::parse(json_object_to_json_string(eventJ));
+ if (jsonRequest.find(VA_JSON_ATTR_ID) == jsonRequest.end()) {
+ sLogger->log(Level::ERROR, TAG, "setDefaultVoiceAgent: voice agent id not found in request json");
+ return -1;
+ }
+
+ std::string voiceAgentId(jsonRequest[VA_JSON_ATTR_ID].get<string>());
+ if (!sVoiceAgentsDataManager->setDefaultVoiceAgent(voiceAgentId)) {
+ sLogger->log(Level::ERROR, TAG, "setDefaultVoiceAgent: Failed to set default agent");
+ return -1;
+ }
+
+ AFB_ReqSuccess(source->request, NULL, NULL);
+ return 0;
+}
+
+CTLP_CAPI(guiMetadataSubscribe, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> guMetadataCapability = sCapabilitiesFactory->getGuiMetadata();
+ if (!guMetadataCapability) {
+ sLogger->log(
+ Level::WARNING,
+ TAG,
+ "guimetadataSubscribe: Failed to "
+ "fetch guimetadata capability "
+ "object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "guimetadataSubscribe: No arguments supplied.");
+ return -1;
+ }
+
+ json subscribeJson = json::parse(json_object_to_json_string(eventJ));
+ if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "guimetadataSubscribe: No events array found in subscribe json");
+ return -1;
+ }
+ list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>());
+
+ // SUbscribe this client for the guimetadata events.
+ auto request = vshl::afb::AFBRequestImpl::create(source->request);
+ for (auto event : events) {
+ if (!sCapabilityMessagingService->subscribe(*request, guMetadataCapability, event)) {
+ sLogger->log(Level::ERROR, TAG, "guimetadataSubscribe: Failed to subscribe to event: " + event);
+ return -1;
+ }
+ }
+
+ AFB_ReqSuccess(
+ source->request, json_object_new_string("Subscription to guimetadata events successfully completed."), NULL);
+ return 0;
+}
+
+CTLP_CAPI(guiMetadataPublish, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> guMetadataCapability = sCapabilitiesFactory->getGuiMetadata();
+ if (!guMetadataCapability) {
+ sLogger->log(
+ Level::WARNING,
+ TAG,
+ "guimetadataPublish: Failed to fetch "
+ "guimetadata capability object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "guimetadataPublish: No arguments supplied.");
+ return -1;
+ }
+
+ json publishJson = json::parse(json_object_to_json_string(eventJ));
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_ACTION) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "guimetadataPublish: No action found in publish json");
+ return -1;
+ }
+ std::string action(publishJson[CAPABILITIES_JSON_ATTR_ACTION].get<string>());
+
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_PAYLOAD) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "guimetadataPublish: No payload found in publish json");
+ return -1;
+ }
+ std::string payload(publishJson[CAPABILITIES_JSON_ATTR_PAYLOAD].get<string>());
+
+ if (!sCapabilityMessagingService->publish(guMetadataCapability, action, payload)) {
+ sLogger->log(Level::ERROR, TAG, "guimetadataPublish: Failed to publish message: " + action);
+ return -1;
+ }
+
+ AFB_ReqSuccess(source->request, json_object_new_string("Successfully published guimetadata messages."), NULL);
+ return 0;
+}
+
+CTLP_CAPI(phonecontrolSubscribe, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> phoneControlCapability = sCapabilitiesFactory->getPhoneControl();
+ if (!phoneControlCapability) {
+ sLogger->log(Level::WARNING, TAG, "phoneControlSubscribe: Failed to fetch phone control capability object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "phoneControlSubscribe: No arguments supplied.");
+ return -1;
+ }
+
+ json subscribeJson = json::parse(json_object_to_json_string(eventJ));
+ if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "phoneControlSubscribe: No events array found in subscribe json");
+ return -1;
+ }
+ list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>());
+
+ // SUbscribe this client for the phone call control events.
+ auto request = vshl::afb::AFBRequestImpl::create(source->request);
+ for (auto event : events) {
+ if (!sCapabilityMessagingService->subscribe(*request, phoneControlCapability, event)) {
+ sLogger->log(Level::ERROR, TAG, "phoneControlSubscribe: Failed to subscribe to event: " + event);
+ return -1;
+ }
+ }
+
+ AFB_ReqSuccess(
+ source->request, json_object_new_string("Subscription to phone control events successfully completed."), NULL);
+ return 0;
+}
+
+CTLP_CAPI(phonecontrolPublish, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> phoneControlCapability = sCapabilitiesFactory->getPhoneControl();
+ if (!phoneControlCapability) {
+ sLogger->log(Level::WARNING, TAG, "phoneControlPublish: Failed to fetch navigation capability object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "phoneControlPublish: No arguments supplied.");
+ return -1;
+ }
+
+ json publishJson = json::parse(json_object_to_json_string(eventJ));
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_ACTION) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "phoneControlPublish: No action found in publish json");
+ return -1;
+ }
+ std::string action(publishJson[CAPABILITIES_JSON_ATTR_ACTION].get<string>());
+
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_PAYLOAD) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "phoneControlPublish: No payload found in publish json");
+ return -1;
+ }
+ std::string payload(publishJson[CAPABILITIES_JSON_ATTR_PAYLOAD].get<string>());
+
+ if (!sCapabilityMessagingService->publish(phoneControlCapability, action, payload)) {
+ sLogger->log(Level::ERROR, TAG, "phoneControlPublish: Failed to publish message: " + action);
+ return -1;
+ }
+
+ AFB_ReqSuccess(source->request, json_object_new_string("Successfully published phone control messages."), NULL);
+ return 0;
+}
+
+CTLP_CAPI(navigationSubscribe, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> navigationCapability = sCapabilitiesFactory->getNavigation();
+ if (!navigationCapability) {
+ sLogger->log(Level::WARNING, TAG, "navigationSubscribe: Failed to fetch navigation capability object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "navigationSubscribe: No arguments supplied.");
+ return -1;
+ }
+
+ json subscribeJson = json::parse(json_object_to_json_string(eventJ));
+ if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "navigationSubscribe: No events array found in subscribe json");
+ return -1;
+ }
+ list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>());
+
+ // SUbscribe this client for the navigation events.
+ auto request = vshl::afb::AFBRequestImpl::create(source->request);
+ for (auto event : events) {
+ if (!sCapabilityMessagingService->subscribe(*request, navigationCapability, event)) {
+ sLogger->log(Level::ERROR, TAG, "navigationSubscribe: Failed to subscribe to event: " + event);
+ return -1;
+ }
+ }
+
+ AFB_ReqSuccess(
+ source->request, json_object_new_string("Subscription to navigation events successfully completed."), NULL);
+ return 0;
+}
+
+CTLP_CAPI(navigationPublish, source, argsJ, eventJ) {
+ if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) {
+ return -1;
+ }
+
+ shared_ptr<vshl::common::interfaces::ICapability> navigationCapability = sCapabilitiesFactory->getNavigation();
+ if (!navigationCapability) {
+ sLogger->log(Level::WARNING, TAG, "navigationPublish: Failed to fetch navigation capability object.");
+ return -1;
+ }
+
+ if (eventJ == nullptr) {
+ sLogger->log(Level::WARNING, TAG, "navigationPublish: No arguments supplied.");
+ return -1;
+ }
+
+ json publishJson = json::parse(json_object_to_json_string(eventJ));
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_ACTION) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "navigationPublish: No action found in publish json");
+ return -1;
+ }
+ std::string action(publishJson[CAPABILITIES_JSON_ATTR_ACTION].get<string>());
+
+ if (publishJson.find(CAPABILITIES_JSON_ATTR_PAYLOAD) == publishJson.end()) {
+ sLogger->log(Level::ERROR, TAG, "navigationPublish: No payload found in publish json");
+ return -1;
+ }
+ std::string payload(publishJson[CAPABILITIES_JSON_ATTR_PAYLOAD].get<string>());
+
+ if (!sCapabilityMessagingService->publish(navigationCapability, action, payload)) {
+ sLogger->log(Level::ERROR, TAG, "navigationPublish: Failed to publish message: " + action);
+ return -1;
+ }
+
+ AFB_ReqSuccess(source->request, json_object_new_string("Successfully published navigation messages."), NULL);
+ return 0;
+}
diff --git a/src/plugins/VshlApi.h b/src/plugins/VshlApi.h
new file mode 100644
index 0000000..f228943
--- /dev/null
+++ b/src/plugins/VshlApi.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_API_INCLUDE
+#define VSHL_API_INCLUDE
+
+#include "ctl-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CTLP_ONLOAD(plugin, ret);
+CTLP_INIT(plugin, ret);
+int onAuthStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int onConnectionStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int onDialogStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int loadVoiceAgentsConfig(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int startListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int cancelListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int enumerateVoiceAgents(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int subscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int setDefaultVoiceAgent(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int guiMetadataSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int guiMetadataPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int phonecontrolSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int phonecontrolPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int navigationSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+int navigationPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // VSHL_API_INCLUDE
diff --git a/src/plugins/afb/AFBApiImpl.cpp b/src/plugins/afb/AFBApiImpl.cpp
new file mode 100644
index 0000000..88d1e7e
--- /dev/null
+++ b/src/plugins/afb/AFBApiImpl.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "afb/AFBApiImpl.h"
+
+#include "afb/include/AFBEventImpl.h"
+#include "utilities/logging/Logger.h"
+
+extern "C" {
+#define AFB_BINDING_VERSION 3
+#define FREEIF(x) \
+ if (!x) { \
+ free(x); \
+ }
+#define BREAKIF(x) \
+ if (x) { \
+ result = false; \
+ break; \
+ }
+
+#include "afb-definitions.h"
+}
+
+static std::string TAG = "vshl::afb::AFBApiImpl";
+
+/**
+ * Specifies the severity level of a log message
+ */
+using Level = vshl::common::interfaces::ILogger::Level;
+using namespace vshl::common::interfaces;
+using namespace vshl::utilities::logging;
+
+namespace vshl {
+namespace afb {
+
+std::unique_ptr<AFBApiImpl> AFBApiImpl::create(AFB_ApiT api) {
+ return std::unique_ptr<AFBApiImpl>(new AFBApiImpl(api));
+}
+
+AFBApiImpl::AFBApiImpl(AFB_ApiT api) : mApi(api), mLogger(Logger::create(api)) {
+}
+
+AFBApiImpl::~AFBApiImpl() {
+}
+
+std::shared_ptr<IAFBApi::IAFBEvent> AFBApiImpl::createEvent(const std::string& eventName) {
+ return AFBEventImpl::create(mLogger, mApi, eventName);
+}
+
+int AFBApiImpl::callSync(
+ const std::string& api,
+ const std::string& verb,
+ struct json_object* request,
+ struct json_object** result,
+ std::string& error,
+ std::string& info) {
+ char* errorStr = NULL;
+ char* infoStr = NULL;
+ int rc = AFB_ApiSync(mApi, api.c_str(), verb.c_str(), request, result, &errorStr, &infoStr);
+
+ if (errorStr) {
+ error = errorStr;
+ free(errorStr);
+ }
+
+ if (infoStr) {
+ info = infoStr;
+ free(infoStr);
+ }
+
+ return rc;
+}
+
+} // namespace afb
+} // namespace vshl
diff --git a/src/plugins/afb/AFBApiImpl.h b/src/plugins/afb/AFBApiImpl.h
new file mode 100644
index 0000000..74aa7ef
--- /dev/null
+++ b/src/plugins/afb/AFBApiImpl.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_AFB_AFBAPIIMPL_H_
+#define VSHL_AFB_AFBAPIIMPL_H_
+
+#include <memory>
+
+extern "C" {
+#include "ctl-plugin.h"
+}
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace afb {
+
+class AFBApiImpl : public vshl::common::interfaces::IAFBApi {
+public:
+ static std::unique_ptr<AFBApiImpl> create(AFB_ApiT api);
+
+ ~AFBApiImpl();
+
+ std::shared_ptr<IAFBEvent> createEvent(const std::string& eventName) override;
+
+ int callSync(
+ const std::string& api,
+ const std::string& verb,
+ struct json_object* request,
+ struct json_object** result,
+ std::string& error,
+ std::string& info) override;
+
+private:
+ AFBApiImpl(AFB_ApiT api);
+
+ // AFB API Binding
+ AFB_ApiT mApi;
+
+ // Logger
+ std::shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace afb
+} // namespace vshl
+
+#endif // VSHL_AFB_AFBAPIIMPL_H_
diff --git a/src/plugins/afb/AFBRequestImpl.cpp b/src/plugins/afb/AFBRequestImpl.cpp
new file mode 100644
index 0000000..8ec5691
--- /dev/null
+++ b/src/plugins/afb/AFBRequestImpl.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "afb/AFBRequestImpl.h"
+
+extern "C" {
+#include "afb-definitions.h"
+}
+
+namespace vshl {
+namespace afb {
+
+std::unique_ptr<AFBRequestImpl> AFBRequestImpl::create(AFB_ReqT afbRequest) {
+ return std::unique_ptr<AFBRequestImpl>(new AFBRequestImpl(afbRequest));
+}
+
+AFBRequestImpl::AFBRequestImpl(AFB_ReqT afbRequest) : mAfbRequest(afbRequest) {
+}
+
+void* AFBRequestImpl::getNativeRequest() {
+ return mAfbRequest;
+}
+
+} // namespace afb
+} // namespace vshl
diff --git a/src/plugins/afb/AFBRequestImpl.h b/src/plugins/afb/AFBRequestImpl.h
new file mode 100644
index 0000000..2e6f3ab
--- /dev/null
+++ b/src/plugins/afb/AFBRequestImpl.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_AFB_AFBREQUESTIMPL_H_
+#define VSHL_AFB_AFBREQUESTIMPL_H_
+
+#include <memory>
+
+extern "C" {
+#include "ctl-plugin.h"
+}
+
+#include "interfaces/afb/IAFBApi.h"
+
+namespace vshl {
+namespace afb {
+
+/**
+ * AFB Request impl
+ */
+class AFBRequestImpl : public vshl::common::interfaces::IAFBRequest {
+public:
+ static std::unique_ptr<AFBRequestImpl> create(AFB_ReqT afbRequest);
+
+ // {@c IAFBRequest Implementation
+ void *getNativeRequest() override;
+ // @c IAFBRequest Implementation }
+
+private:
+ AFBRequestImpl(AFB_ReqT afbRequest);
+
+ AFB_ReqT mAfbRequest;
+};
+
+} // namespace afb
+} // namespace vshl
+
+#endif // VSHL_AFB_AFBREQUESTIMPL_H_
diff --git a/src/plugins/afb/include/AFBEventImpl.h b/src/plugins/afb/include/AFBEventImpl.h
new file mode 100644
index 0000000..45f85f9
--- /dev/null
+++ b/src/plugins/afb/include/AFBEventImpl.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_AFB_EVENT_H_
+#define VSHL_AFB_EVENT_H_
+
+#include <memory>
+#include <string>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+extern "C" {
+#include "ctl-plugin.h"
+#include <json-c/json.h>
+}
+
+using namespace std;
+
+namespace vshl {
+namespace afb {
+/*
+ * This class encapsulates AFB Event.
+ */
+class AFBEventImpl : public vshl::common::interfaces::IAFBApi::IAFBEvent {
+public:
+ static unique_ptr<AFBEventImpl>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger, AFB_ApiT api,
+ const string &eventName);
+
+ // Destructor
+ ~AFBEventImpl();
+
+ /// { @c IAFBEvent implementation
+ string getName() const override;
+ bool isValid() override;
+ int publishEvent(struct json_object *payload) override;
+ bool subscribe(vshl::common::interfaces::IAFBRequest &request) override;
+ bool unsubscribe(vshl::common::interfaces::IAFBRequest &request) override;
+ /// @c IAFBEvent implementation }
+
+private:
+ AFBEventImpl(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ AFB_ApiT api, const string &eventName);
+
+ // Make the event. This is a lazy make that happens
+ // usually during the subscribe stage.
+ void makeEventIfNeccessary();
+
+ // Binding API reference
+ AFB_ApiT mAfbApi;
+
+ // AFB Event
+ afb_event_t mAfbEvent;
+
+ // Event Name
+ string mEventName;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace afb
+} // namespace vshl
+
+#endif // VSHL_AFB_EVENT_H_
diff --git a/src/plugins/afb/src/AFBEventImpl.cpp b/src/plugins/afb/src/AFBEventImpl.cpp
new file mode 100644
index 0000000..e3c902d
--- /dev/null
+++ b/src/plugins/afb/src/AFBEventImpl.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "afb/include/AFBEventImpl.h"
+
+static string TAG = "vshl::afb::Event";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+using namespace vshl::common::interfaces;
+
+namespace vshl {
+namespace afb {
+
+unique_ptr<AFBEventImpl> AFBEventImpl::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ AFB_ApiT api,
+ const string& eventName) {
+ return unique_ptr<AFBEventImpl>(new AFBEventImpl(logger, api, eventName));
+}
+
+AFBEventImpl::AFBEventImpl(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ AFB_ApiT api,
+ const string& eventName) :
+ mLogger(logger),
+ mAfbApi(api),
+ mEventName(eventName),
+ mAfbEvent(nullptr) {
+}
+
+AFBEventImpl::~AFBEventImpl() {
+}
+
+string AFBEventImpl::getName() const {
+ return mEventName;
+}
+
+bool AFBEventImpl::isValid() {
+ makeEventIfNeccessary();
+ return afb_event_is_valid(mAfbEvent) == 1 ? true : false;
+}
+
+bool AFBEventImpl::subscribe(IAFBRequest& requestInterface) {
+ makeEventIfNeccessary();
+ auto request = static_cast<AFB_ReqT>(requestInterface.getNativeRequest());
+ if (isValid() && afb_req_subscribe(request, mAfbEvent) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+bool AFBEventImpl::unsubscribe(IAFBRequest& requestInterface) {
+ makeEventIfNeccessary();
+ auto request = static_cast<AFB_ReqT>(requestInterface.getNativeRequest());
+ if (isValid() && afb_req_unsubscribe(request, mAfbEvent) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+int AFBEventImpl::publishEvent(struct json_object* payload) {
+ makeEventIfNeccessary();
+ return afb_event_push(mAfbEvent, payload);
+}
+
+void AFBEventImpl::makeEventIfNeccessary() {
+ if (mAfbEvent) {
+ return;
+ }
+
+ mLogger->log(Level::NOTICE, TAG, "Creating VSHL event: " + mEventName);
+ mAfbEvent = afb_api_make_event(mAfbApi, mEventName.c_str());
+}
+
+} // namespace afb
+} // namespace vshl
diff --git a/src/plugins/agreement.sh b/src/plugins/agreement.sh
new file mode 100755
index 0000000..2a8eb71
--- /dev/null
+++ b/src/plugins/agreement.sh
@@ -0,0 +1,43 @@
+#********************************************************************************
+# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+# A copy of the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file 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.
+#*********************************************************************************
+
+agreement_check() {
+ cat << EOF
+
+*******************************************************************************
+The scripts provided herein will retrieve several third-party libraries,
+environments, and/or other software packages at build-time
+("External Dependencies") from third-party sources. These are terms and
+conditions that you need to agree to abide by if you choose to build the
+External Dependencies. Licenses for the External Dependencies may be found at
+README.md. If you do not agree with every term and condition
+associated with the External Dependencies, enter “QUIT” in the command line
+when prompted by the script.
+*******************************************************************************
+
+EOF
+
+ answer="dummy"
+ while [ ! -z $answer ]; do
+ read -p "Type \"QUIT\" to exit the script now, press ENTER to continue: " -r answer
+ if [ "$answer" = "QUIT" ]; then
+ exit 1
+ fi
+ echo ""
+ done
+}
+
+agreement_check
+exit 0 \ No newline at end of file
diff --git a/src/plugins/capabilities/CapabilitiesFactory.cpp b/src/plugins/capabilities/CapabilitiesFactory.cpp
new file mode 100644
index 0000000..2f92519
--- /dev/null
+++ b/src/plugins/capabilities/CapabilitiesFactory.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/CapabilitiesFactory.h"
+
+#include "capabilities/communication/include/PhoneControlCapability.h"
+#include "capabilities/guimetadata/include/GuiMetadataCapability.h"
+#include "capabilities/navigation/include/NavigationCapability.h"
+
+static string TAG = "vshl::core::CapabilitiesFactory";
+
+using Level = vshl::utilities::logging::Logger::Level;
+
+namespace vshl {
+namespace capabilities {
+
+// Create CapabilitiesFactory
+std::unique_ptr<CapabilitiesFactory> CapabilitiesFactory::create() {
+ auto capabilitiesFactory = std::unique_ptr<CapabilitiesFactory>(new CapabilitiesFactory());
+ return capabilitiesFactory;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getGuiMetadata() {
+ if (!mGuiMetadata) {
+ mGuiMetadata = vshl::capabilities::guimetadata::GuiMetadata::create();
+ }
+ return mGuiMetadata;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getPhoneControl() {
+ if (!mPhoneControl) {
+ mPhoneControl = vshl::capabilities::phonecontrol::PhoneControl::create();
+ }
+ return mPhoneControl;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getNavigation() {
+ if (!mNavigation) {
+ mNavigation = vshl::capabilities::navigation::Navigation::create();
+ }
+ return mNavigation;
+}
+
+} // namespace capabilities
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/capabilities/CapabilitiesFactory.h b/src/plugins/capabilities/CapabilitiesFactory.h
new file mode 100644
index 0000000..b73909b
--- /dev/null
+++ b/src/plugins/capabilities/CapabilitiesFactory.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_
+#define VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "utilities/logging/Logger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+/*
+ * Factory for creating different capability objects.
+ */
+class CapabilitiesFactory {
+public:
+ // Create CapabilitiesFactory
+ static std::unique_ptr<CapabilitiesFactory> create();
+
+ // GUI Metadata capability
+ std::shared_ptr<common::interfaces::ICapability> getGuiMetadata();
+
+ // Phone call control capability
+ std::shared_ptr<common::interfaces::ICapability> getPhoneControl();
+
+ // Navigation capability
+ std::shared_ptr<common::interfaces::ICapability> getNavigation();
+
+ // Destructor
+ ~CapabilitiesFactory() = default;
+
+private:
+ // Constructor
+ CapabilitiesFactory() = default;
+
+ // Capabilities
+ shared_ptr<vshl::common::interfaces::ICapability> mGuiMetadata;
+ shared_ptr<vshl::common::interfaces::ICapability> mPhoneControl;
+ shared_ptr<vshl::common::interfaces::ICapability> mNavigation;
+
+ // Logger
+ unique_ptr<vshl::utilities::logging::Logger> mLogger;
+};
+
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_
diff --git a/src/plugins/capabilities/CapabilityMessagingService.cpp b/src/plugins/capabilities/CapabilityMessagingService.cpp
new file mode 100644
index 0000000..91b5f2b
--- /dev/null
+++ b/src/plugins/capabilities/CapabilityMessagingService.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/CapabilityMessagingService.h"
+
+#include "capabilities/core/include/PublisherForwarder.h"
+#include "capabilities/core/include/SubscriberForwarder.h"
+
+static string TAG = "vshl::capabilities::CapabilityMessagingService";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace capabilities {
+
+// Create a CapabilityMessagingService.
+unique_ptr<CapabilityMessagingService> CapabilityMessagingService::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ if (logger == nullptr) {
+ return nullptr;
+ }
+
+ if (afbApi == nullptr) {
+ logger->log(Level::ERROR, TAG, "Failed to create CapabilityMessagingService: AFB API null");
+ return nullptr;
+ }
+
+ auto capabilityMessageService =
+ std::unique_ptr<CapabilityMessagingService>(new CapabilityMessagingService(logger, afbApi));
+ return capabilityMessageService;
+}
+
+CapabilityMessagingService::~CapabilityMessagingService() {
+ mMessageChannelsMap.clear();
+}
+
+CapabilityMessagingService::CapabilityMessagingService(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mAfbApi(afbApi),
+ mLogger(logger) {
+}
+
+// Subscribe to capability specific messages.
+bool CapabilityMessagingService::subscribe(
+ vshl::common::interfaces::IAFBRequest& request,
+ shared_ptr<common::interfaces::ICapability> capability,
+ const string action) {
+ auto capabilityName = capability->getName();
+
+ if (capabilityName.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to subscribe to message. Invalid input.");
+ return false;
+ }
+
+ auto messageChannel = getMessageChannel(capability);
+ return messageChannel->subscribe(request, action);
+}
+
+// Publish capability messages.
+bool CapabilityMessagingService::publish(
+ shared_ptr<common::interfaces::ICapability> capability,
+ const string action,
+ const string payload) {
+ auto capabilityName = capability->getName();
+
+ if (capabilityName.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to publish message. Invalid input.");
+ return false;
+ }
+
+ auto messageChannelIt = mMessageChannelsMap.find(capabilityName);
+ if (messageChannelIt == mMessageChannelsMap.end()) {
+ mLogger->log(
+ Level::ERROR,
+ TAG,
+ "Failed to publish message. Message channel doesn't exist for capability " + capabilityName);
+ return false;
+ }
+
+ return messageChannelIt->second->publish(action, payload);
+}
+
+shared_ptr<vshl::capabilities::core::MessageChannel> CapabilityMessagingService::getMessageChannel(
+ shared_ptr<common::interfaces::ICapability> capability) {
+ auto capabilityName = capability->getName();
+
+ if (capabilityName.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to create message channel. Invalid input.");
+ return nullptr;
+ }
+
+ auto messageChannelIt = mMessageChannelsMap.find(capabilityName);
+ if (messageChannelIt == mMessageChannelsMap.end()) {
+ mLogger->log(Level::INFO, TAG, "Creating new message channel for capability: " + capabilityName);
+ auto messageChannel = vshl::capabilities::core::MessageChannel::create(mLogger, mAfbApi, capability);
+ mMessageChannelsMap.insert(make_pair(capabilityName, messageChannel));
+ return messageChannel;
+ }
+
+ return messageChannelIt->second;
+}
+
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/CapabilityMessagingService.h b/src/plugins/capabilities/CapabilityMessagingService.h
new file mode 100644
index 0000000..535e806
--- /dev/null
+++ b/src/plugins/capabilities/CapabilityMessagingService.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_
+#define VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "capabilities/core/include/MessageChannel.h"
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+/*
+ * This hosts service APIs that clients can use to subscribe and
+ * forward capability messages. Each capability has a name and
+ * direction (upstream/downstream). Upstream messages are from
+ * voiceagents to Apps and downstream messages are Apps to voiceagents.
+ * This class will use a factory to create publisher and subcribers for
+ * each capability and create assiociations between them.
+ */
+class CapabilityMessagingService {
+public:
+ // Create a CapabilityMessagingService.
+ static std::unique_ptr<CapabilityMessagingService>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Subscribe to capability specific messages.
+ bool subscribe(vshl::common::interfaces::IAFBRequest &request,
+ shared_ptr<common::interfaces::ICapability> capability,
+ const string action);
+
+ // Publish capability messages.
+ bool publish(shared_ptr<common::interfaces::ICapability> capability,
+ const string action, const string payload);
+
+ // Destructor
+ ~CapabilityMessagingService();
+
+private:
+ // Constructor
+ CapabilityMessagingService(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // Create a message channel for the capability.
+ shared_ptr<vshl::capabilities::core::MessageChannel>
+ getMessageChannel(shared_ptr<common::interfaces::ICapability> capability);
+
+ // Map of capabilities to message channels.
+ unordered_map<string, shared_ptr<vshl::capabilities::core::MessageChannel>>
+ mMessageChannelsMap;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_
diff --git a/src/plugins/capabilities/communication/include/PhoneControlCapability.h b/src/plugins/capabilities/communication/include/PhoneControlCapability.h
new file mode 100644
index 0000000..55ada8d
--- /dev/null
+++ b/src/plugins/capabilities/communication/include/PhoneControlCapability.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_
+#define VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+
+namespace vshl {
+namespace capabilities {
+namespace phonecontrol {
+
+/*
+ * PhoneControl capability. Calls are initiated in the endpoint.
+ */
+class PhoneControl : public common::interfaces::ICapability {
+public:
+ // Create a PhoneControl.
+ static std::shared_ptr<PhoneControl> create();
+
+ ~PhoneControl() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ PhoneControl() = default;
+};
+
+} // namespace phonecontrol
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_
diff --git a/src/plugins/capabilities/communication/include/PhoneControlMessages.h b/src/plugins/capabilities/communication/include/PhoneControlMessages.h
new file mode 100644
index 0000000..4c68455
--- /dev/null
+++ b/src/plugins/capabilities/communication/include/PhoneControlMessages.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_
+#define VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace phonecontrol {
+
+static string NAME = "phonecontrol";
+
+// Supported actions from VA -> Apps
+/* Dial message sent from VA to app handling the calling.
+ *
+ * Payload
+ * {
+ * "callId": "{{STRING}}",
+ * "callee": {
+ * "details": "{{STRING}}",
+ * "defaultAddress": {
+ * "protocol": "{{STRING}}",
+ * "format": "{{STRING}}",
+ * "value": "{{STRING}}"
+ * },
+ * "alternativeAddresses": [{
+ * "protocol": "{{STRING}}",
+ * "format": "{{STRING}}",
+ * "value": {{STRING}}
+ * }]
+ * }
+ * }
+ * }
+ *
+ * callId (required): A unique identifier for the call
+ * callee (required): The destination of the outgoing call
+ * callee.details (optional): Descriptive information about the callee
+ * callee.defaultAddress (required): The default address to use for calling the callee
+ * callee.alternativeAddresses (optional): An array of alternate addresses for the existing callee
+ * address.protocol (required): The protocol for this address of the callee (e.g. PSTN, SIP, H323, etc.)
+ * address.format (optional): The format for this address of the callee (e.g. E.164, E.163, E.123, DIN5008, etc.)
+ * address.value (required): The address of the callee.
+ *
+ */
+static string PHONECONTROL_DIAL = "dial";
+
+// Supported actions from Apps -> VA
+/*
+ * App notifies the voiceagents of a change in connection state of a calling device.
+ *
+ * Payload
+ * {
+ * "state" : "{{STRING}}" // CONNECTED or DISCONNECTED
+ * }
+ */
+static string PHONECONTROL_CONNECTIONSTATE_CHANGED = "connection_state_changed";
+/*
+ * App notifies the voiceagents that call is activated
+ *
+ * callId must match the one that is sent by VA with DIAL message above.
+ *
+ * Payload
+ * {
+ * "callId" : "{{STRING}}"
+ * }
+ */
+static string PHONECONTROL_CALL_ACTIVATED = "call_activated";
+/*
+ * App notifies the voiceagents of an error in initiating or maintaining a
+ * call on a calling device
+ *
+ * callId must match the one that is sent by VA with DIAL message above.
+ * error: below status codes.
+ * 4xx: Validation failure for the input from the DIAL message
+ * 500: Internal error on the platform unrelated to the cellular network
+ * 503: Error on the platform related to the cellular network
+ *
+ * Payload
+ * {
+ * "callId" : "{{STRING}}"
+ * "error" : "{{STRING}}"
+ * }
+ */
+static string PHONECONTROL_CALL_FAILED = "call_failed";
+/*
+ * App notifies the voiceagents that call is terminated
+ *
+ * callId must match the one that is sent by VA with DIAL message above.
+ *
+ * Payload
+ * {
+ * "callId" : "{{STRING}}"
+ * }
+ */
+static string PHONECONTROL_CALL_TERMINATED = "call_terminated";
+
+// List of actions that are delivered from VA -> Apps
+static list<string> PHONECONTROL_UPSTREAM_ACTIONS = {
+ PHONECONTROL_DIAL,
+};
+
+// List of actions that are delivered from Apps -> VA
+static list<string> PHONECONTROL_DOWNSTREAM_ACTIONS = {PHONECONTROL_CONNECTIONSTATE_CHANGED,
+ PHONECONTROL_CALL_ACTIVATED,
+ PHONECONTROL_CALL_FAILED,
+ PHONECONTROL_CALL_TERMINATED};
+
+} // namespace phonecontrol
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_
diff --git a/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp b/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp
new file mode 100644
index 0000000..6a74d5a
--- /dev/null
+++ b/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/communication/include/PhoneControlCapability.h"
+#include "capabilities/communication/include/PhoneControlMessages.h"
+
+namespace vshl {
+namespace capabilities {
+namespace phonecontrol {
+
+// Create a phonecontrol.
+shared_ptr<PhoneControl> PhoneControl::create() {
+ auto phonecontrol = std::shared_ptr<PhoneControl>(new PhoneControl());
+ return phonecontrol;
+}
+
+string PhoneControl::getName() const {
+ return NAME;
+}
+
+list<string> PhoneControl::getUpstreamMessages() const {
+ return PHONECONTROL_UPSTREAM_ACTIONS;
+}
+
+list<string> PhoneControl::getDownstreamMessages() const {
+ return PHONECONTROL_DOWNSTREAM_ACTIONS;
+}
+
+} // namespace phonecontrol
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/core/include/MessageChannel.h b/src/plugins/capabilities/core/include/MessageChannel.h
new file mode 100644
index 0000000..504e241
--- /dev/null
+++ b/src/plugins/capabilities/core/include/MessageChannel.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_
+#define VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_
+
+#include <memory>
+
+#include "capabilities/core/include/PublisherForwarder.h"
+#include "capabilities/core/include/SubscriberForwarder.h"
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+/*
+ * MessageChannel has one end as publisher forwarder and the other end
+ * as subscriber forwarder.
+ */
+class MessageChannel {
+public:
+ // Create a MessageChannel.
+ static std::shared_ptr<MessageChannel>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Sends the message
+ bool publish(const string action, const string payload);
+
+ // Subscribe
+ bool subscribe(vshl::common::interfaces::IAFBRequest &request,
+ const string action);
+
+ // Destructor
+ virtual ~MessageChannel() = default;
+
+private:
+ // Constructor
+ MessageChannel(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Forwarders
+ shared_ptr<PublisherForwarder> mPublisherForwarder;
+ shared_ptr<SubscriberForwarder> mSubscriberForwarder;
+};
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_
diff --git a/src/plugins/capabilities/core/include/PublisherForwarder.h b/src/plugins/capabilities/core/include/PublisherForwarder.h
new file mode 100644
index 0000000..9cc89b5
--- /dev/null
+++ b/src/plugins/capabilities/core/include/PublisherForwarder.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_
+#define VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_
+
+#include <memory>
+
+#include "capabilities/core/include/SubscriberForwarder.h"
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+/*
+ * This class is responsible for forwarding the messages to be published
+ * to subscriber forwarder. Subscriber forwarder will deliver the messages
+ * as AFB Events to all the subscribed clients.
+ * There is one PublisherForwarder and one SubscriberForwarder per capability.
+ */
+class PublisherForwarder {
+public:
+ // Create a PublisherForwarder.
+ static std::shared_ptr<PublisherForwarder> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Connect a subscriber forwarder to this publisher forwarder
+ void setSubscriberForwarder(shared_ptr<SubscriberForwarder> subscriberForwarder);
+
+ // Forward message to the subscriber forwarder
+ bool forwardMessage(const string action, const string payload);
+
+ // Destructor
+ ~PublisherForwarder();
+
+private:
+ // Constructor
+ PublisherForwarder(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Subscriber forwarder connected to this publisher forwarder.
+ shared_ptr<SubscriberForwarder> mSubscriberForwarder;
+
+ // Capability
+ shared_ptr<vshl::common::interfaces::ICapability> mCapability;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_
diff --git a/src/plugins/capabilities/core/include/SubscriberForwarder.h b/src/plugins/capabilities/core/include/SubscriberForwarder.h
new file mode 100644
index 0000000..94c04bf
--- /dev/null
+++ b/src/plugins/capabilities/core/include/SubscriberForwarder.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_
+#define VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+/*
+ * This class is responsible for forwarding the messages publishing
+ * to the actual clients using AFB.
+ */
+class SubscriberForwarder {
+public:
+ // Create a SubscriberForwarder.
+ static std::shared_ptr<SubscriberForwarder>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Publish a capability message to the actual client.
+ bool forwardMessage(const string action, const string payload);
+
+ // Subscribe
+ bool subscribe(vshl::common::interfaces::IAFBRequest &request,
+ const string action);
+
+ // Destructor
+ ~SubscriberForwarder();
+
+private:
+ // Constructor
+ SubscriberForwarder(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability);
+
+ // Creates both upstream and downstream events
+ void createEvents();
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // Capability
+ shared_ptr<vshl::common::interfaces::ICapability> mCapability;
+
+ // Maps of capability action events to its corresponding Event object.
+ // Event name maps to Action Name
+ unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>>
+ mUpstreamEventsMap;
+ unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>>
+ mDownstreamEventsMap;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_
diff --git a/src/plugins/capabilities/core/src/MessageChannel.cpp b/src/plugins/capabilities/core/src/MessageChannel.cpp
new file mode 100644
index 0000000..eaa1349
--- /dev/null
+++ b/src/plugins/capabilities/core/src/MessageChannel.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/core/include/MessageChannel.h"
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+
+// Create a MessageChannel.
+std::shared_ptr<MessageChannel> MessageChannel::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> api,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) {
+ auto messageChannel = std::shared_ptr<MessageChannel>(new MessageChannel(logger, api, capability));
+ return messageChannel;
+}
+
+MessageChannel::MessageChannel(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> api,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) {
+ // Subscriber forwarder
+ mSubscriberForwarder = SubscriberForwarder::create(logger, api, capability);
+ // Publisher forwarder
+ mPublisherForwarder = PublisherForwarder::create(logger, capability);
+ mPublisherForwarder->setSubscriberForwarder(mSubscriberForwarder);
+}
+
+bool MessageChannel::publish(const string action, const string payload) {
+ return mPublisherForwarder->forwardMessage(action, payload);
+}
+
+bool MessageChannel::subscribe(vshl::common::interfaces::IAFBRequest& request, const string action) {
+ return mSubscriberForwarder->subscribe(request, action);
+}
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/core/src/PublisherForwarder.cpp b/src/plugins/capabilities/core/src/PublisherForwarder.cpp
new file mode 100644
index 0000000..81de6a0
--- /dev/null
+++ b/src/plugins/capabilities/core/src/PublisherForwarder.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/core/include/PublisherForwarder.h"
+
+static string TAG = "vshl::capabilities::PublisherForwarder";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+
+// Create a PublisherForwarder.
+std::shared_ptr<PublisherForwarder> PublisherForwarder::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) {
+ if (logger == nullptr) {
+ return nullptr;
+ }
+
+ if (capability == nullptr) {
+ logger->log(Level::ERROR, TAG, "Failed to create PublisherForwarder: Capability null");
+ return nullptr;
+ }
+
+ auto publisherForwarder = std::shared_ptr<PublisherForwarder>(new PublisherForwarder(logger, capability));
+ return publisherForwarder;
+}
+
+// Constructor
+PublisherForwarder::PublisherForwarder(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) {
+ mCapability = capability;
+ mLogger = logger;
+}
+
+// Destructor
+PublisherForwarder::~PublisherForwarder() {
+}
+
+void PublisherForwarder::setSubscriberForwarder(shared_ptr<SubscriberForwarder> subscriberForwarder) {
+ mSubscriberForwarder = subscriberForwarder;
+}
+
+bool PublisherForwarder::forwardMessage(const string action, const string payload) {
+ if (!mSubscriberForwarder) {
+ mLogger->log(Level::ERROR, TAG, "Failed to forward message for capability: " + mCapability->getName());
+ return false;
+ }
+
+ return mSubscriberForwarder->forwardMessage(action, payload);
+}
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/core/src/SubscriberForwarder.cpp b/src/plugins/capabilities/core/src/SubscriberForwarder.cpp
new file mode 100644
index 0000000..ea42305
--- /dev/null
+++ b/src/plugins/capabilities/core/src/SubscriberForwarder.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/core/include/SubscriberForwarder.h"
+
+static string TAG = "vshl::capabilities::SubscriberForwarder";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace capabilities {
+namespace core {
+
+// Create a SubscriberForwarder.
+std::shared_ptr<SubscriberForwarder> SubscriberForwarder::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) {
+ if (logger == nullptr) {
+ return nullptr;
+ }
+
+ if (afbApi == nullptr) {
+ logger->log(Level::ERROR, TAG, "Failed to create SubscriberForwarder: AFB API null");
+ return nullptr;
+ }
+
+ if (capability == nullptr) {
+ logger->log(Level::ERROR, TAG, "Failed to create SubscriberForwarder: Capability null");
+ return nullptr;
+ }
+
+ auto subscriberForwarder =
+ std::shared_ptr<SubscriberForwarder>(new SubscriberForwarder(logger, afbApi, capability));
+ return subscriberForwarder;
+}
+
+SubscriberForwarder::SubscriberForwarder(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshl::common::interfaces::ICapability> capability) :
+ mAfbApi(afbApi),
+ mLogger(logger),
+ mCapability(capability) {
+ createEvents();
+}
+
+SubscriberForwarder::~SubscriberForwarder() {
+ mUpstreamEventsMap.clear();
+ mDownstreamEventsMap.clear();
+}
+
+void SubscriberForwarder::createEvents() {
+ if (!mCapability) {
+ mLogger->log(Level::NOTICE, TAG, "Create Events failed. No capability assigned.");
+ return;
+ }
+
+ // Upstream events
+ auto upstreamEvents = mCapability->getUpstreamMessages();
+ for (auto upstreamEventName : upstreamEvents) {
+ auto it = mUpstreamEventsMap.find(upstreamEventName);
+ if (it == mUpstreamEventsMap.end() && mAfbApi) {
+ // create a new event and add it to the map.
+ shared_ptr<common::interfaces::IAFBApi::IAFBEvent> event = mAfbApi->createEvent(upstreamEventName);
+ if (event == nullptr) {
+ mLogger->log(Level::ERROR, TAG, "Failed to create upstream event: " + upstreamEventName);
+ } else {
+ mUpstreamEventsMap.insert(make_pair(upstreamEventName, event));
+ }
+ }
+ }
+
+ // Downstream events
+ auto downstreamEvents = mCapability->getDownstreamMessages();
+ for (auto downstreamEventName : downstreamEvents) {
+ auto it = mDownstreamEventsMap.find(downstreamEventName);
+ if (it == mDownstreamEventsMap.end() && mAfbApi) {
+ // create a new event and add it to the map.
+ shared_ptr<common::interfaces::IAFBApi::IAFBEvent> event = mAfbApi->createEvent(downstreamEventName);
+ if (event == nullptr) {
+ mLogger->log(Level::ERROR, TAG, "Failed to create downstream event: " + downstreamEventName);
+ } else {
+ mDownstreamEventsMap.insert(make_pair(downstreamEventName, event));
+ }
+ }
+ }
+}
+
+bool SubscriberForwarder::forwardMessage(const string action, const string payload) {
+ auto upstreamEventIt = mUpstreamEventsMap.find(action);
+ if (upstreamEventIt != mUpstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Publishing upstream event: " + action);
+ upstreamEventIt->second->publishEvent(json_object_new_string(payload.c_str()));
+ return true;
+ }
+
+ auto downstreamEventIt = mDownstreamEventsMap.find(action);
+ if (downstreamEventIt != mDownstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Publishing downstream event: " + action);
+ downstreamEventIt->second->publishEvent(json_object_new_string(payload.c_str()));
+ return true;
+ }
+
+ mLogger->log(Level::NOTICE, TAG, "Failed to publish upstream event: " + action);
+ return false;
+}
+
+bool SubscriberForwarder::subscribe(vshl::common::interfaces::IAFBRequest& request, const string action) {
+ auto upstreamEventIt = mUpstreamEventsMap.find(action);
+ if (upstreamEventIt != mUpstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Subscribing to upstream event: " + action);
+ return upstreamEventIt->second->subscribe(request);
+ }
+
+ auto downstreamEventIt = mDownstreamEventsMap.find(action);
+ if (downstreamEventIt != mDownstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Subscribing to downstream event: " + action);
+ return downstreamEventIt->second->subscribe(request);
+ }
+
+ mLogger->log(Level::NOTICE, TAG, "Failed to subscribe to upstream event: " + action);
+ return false;
+}
+
+} // namespace core
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h b/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h
new file mode 100644
index 0000000..199f49a
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_
+#define VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+
+namespace vshl {
+namespace capabilities {
+namespace guimetadata {
+
+/*
+ * GuiMetadata capability
+ */
+class GuiMetadata : public common::interfaces::ICapability {
+public:
+ // Create a GuiMetadata.
+ static std::shared_ptr<GuiMetadata> create();
+
+ ~GuiMetadata() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ GuiMetadata() = default;
+};
+
+} // namespace guimetadata
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_
diff --git a/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h b/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h
new file mode 100644
index 0000000..783b401
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_
+#define VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace guimetadata {
+
+static string NAME = "guimetadata";
+
+// Supported actions from VA -> Apps
+static string GUIMETADATA_RENDER_TEMPLATE = "render_template";
+static string GUIMETADATA_CLEAR_TEMPLATE = "clear_template";
+static string GUIMETADATA_RENDER_PLAYER_INFO = "render_player_info";
+static string GUIMETADATA_CLEAR_PLAYER_INFO = "clear_player_info";
+
+// Supported actions from Apps -> VA
+
+// List of actions that are delivered from VA -> Apps
+static list<string> GUIMETADATA_UPSTREAM_ACTIONS = {GUIMETADATA_RENDER_TEMPLATE,
+ GUIMETADATA_CLEAR_TEMPLATE,
+ GUIMETADATA_RENDER_PLAYER_INFO,
+ GUIMETADATA_CLEAR_PLAYER_INFO};
+
+// List of actions that are delivered from Apps -> VA
+static list<string> GUIMETADATA_DOWNSTREAM_ACTIONS = {};
+
+} // namespace guimetadata
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_
diff --git a/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp b/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp
new file mode 100644
index 0000000..106fe99
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/guimetadata/include/GuiMetadataCapability.h"
+#include "capabilities/guimetadata/include/GuiMetadataMessages.h"
+
+namespace vshl {
+namespace capabilities {
+namespace guimetadata {
+
+// Create a GuiMetadata.
+shared_ptr<GuiMetadata> GuiMetadata::create() {
+ auto guiMetadata = std::shared_ptr<GuiMetadata>(new GuiMetadata());
+ return guiMetadata;
+}
+
+string GuiMetadata::getName() const {
+ return NAME;
+}
+
+list<string> GuiMetadata::getUpstreamMessages() const {
+ return GUIMETADATA_UPSTREAM_ACTIONS;
+}
+
+list<string> GuiMetadata::getDownstreamMessages() const {
+ return GUIMETADATA_DOWNSTREAM_ACTIONS;
+}
+
+} // namespace guimetadata
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/navigation/include/NavigationCapability.h b/src/plugins/capabilities/navigation/include/NavigationCapability.h
new file mode 100644
index 0000000..66109d5
--- /dev/null
+++ b/src/plugins/capabilities/navigation/include/NavigationCapability.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_
+#define VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+
+namespace vshl {
+namespace capabilities {
+namespace navigation {
+
+/*
+ * Navigation capability
+ */
+class Navigation : public common::interfaces::ICapability {
+public:
+ // Create a Navigation.
+ static std::shared_ptr<Navigation> create();
+
+ ~Navigation() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ Navigation() = default;
+};
+
+} // namespace navigation
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_
diff --git a/src/plugins/capabilities/navigation/include/NavigationMessages.h b/src/plugins/capabilities/navigation/include/NavigationMessages.h
new file mode 100644
index 0000000..aed9b9e
--- /dev/null
+++ b/src/plugins/capabilities/navigation/include/NavigationMessages.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_
+#define VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace capabilities {
+namespace navigation {
+
+static string NAME = "navigation";
+
+// Supported actions from VA -> Apps
+static string NAVIGATION_SET_DESTINATION = "set_destination";
+static string NAVIGATION_CANCEL = "cancel_navigation";
+
+// Supported actions from Apps -> VA
+
+// List of actions that are delivered from VA -> Apps
+static list<string> NAVIGATION_UPSTREAM_ACTIONS = {
+ NAVIGATION_SET_DESTINATION,
+ NAVIGATION_CANCEL,
+};
+
+// List of actions that are delivered from Apps -> VA
+static list<string> NAVIGATION_DOWNSTREAM_ACTIONS = {};
+
+} // namespace navigation
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_
diff --git a/src/plugins/capabilities/navigation/src/NavigationCapability.cpp b/src/plugins/capabilities/navigation/src/NavigationCapability.cpp
new file mode 100644
index 0000000..d4a5119
--- /dev/null
+++ b/src/plugins/capabilities/navigation/src/NavigationCapability.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "capabilities/navigation/include/NavigationCapability.h"
+#include "capabilities/navigation/include/NavigationMessages.h"
+
+namespace vshl {
+namespace capabilities {
+namespace navigation {
+
+// Create a Navigation.
+shared_ptr<Navigation> Navigation::create() {
+ auto navigation = std::shared_ptr<Navigation>(new Navigation());
+ return navigation;
+}
+
+string Navigation::getName() const {
+ return NAME;
+}
+
+list<string> Navigation::getUpstreamMessages() const {
+ return NAVIGATION_UPSTREAM_ACTIONS;
+}
+
+list<string> Navigation::getDownstreamMessages() const {
+ return NAVIGATION_DOWNSTREAM_ACTIONS;
+}
+
+} // namespace navigation
+} // namespace capabilities
+} // namespace vshl
diff --git a/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp b/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp
new file mode 100644
index 0000000..741a8aa
--- /dev/null
+++ b/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "capabilities/CapabilityMessagingService.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+#include "test/mocks/AFBEventMock.h"
+#include "test/mocks/AFBRequestMock.h"
+#include "test/mocks/CapabilityMock.h"
+
+using namespace vshl::common::interfaces;
+using namespace vshl::capabilities;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class CapabilityMessagingServiceTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::NiceMock<AFBApiMock>>();
+ }
+
+ std::shared_ptr<::testing::NiceMock<AFBApiMock>> mAfbApi;
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+};
+
+TEST_F(CapabilityMessagingServiceTest, failsInitializationOnInvalidParams) {
+ auto service = CapabilityMessagingService::create(mConsoleLogger, nullptr);
+ ASSERT_EQ(service, nullptr);
+
+ service = CapabilityMessagingService::create(nullptr, mAfbApi);
+ ASSERT_EQ(service, nullptr);
+}
+
+TEST_F(CapabilityMessagingServiceTest, initializesSuccessfully) {
+ auto service = CapabilityMessagingService::create(mConsoleLogger, mAfbApi);
+ ASSERT_NE(service, nullptr);
+}
+
+TEST_F(CapabilityMessagingServiceTest, canSubscribeAndPublishCapabilityEvents) {
+ auto service = CapabilityMessagingService::create(mConsoleLogger, mAfbApi);
+
+ auto capability = std::make_shared<::testing::NiceMock<CapabilityMock>>();
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+ std::string capabilityName = "weather";
+
+ ON_CALL(*capability, getName()).WillByDefault(::testing::Return(capabilityName));
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ // We don't care if and how many times subscribe method is called on event.
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::NiceMock<AFBEventMock>());
+ ON_CALL(*mockEvent, subscribe(::testing::_)).WillByDefault(::testing::Return(true));
+ ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true));
+ auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ mockEvent->setName(eventName);
+ return mockEvent;
+ };
+
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator));
+
+ auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>();
+ std::string payload = "The answer to life the universe and everything = 42";
+
+ bool result = service->publish(capability, *upstreamEvents.begin(), payload);
+ ASSERT_FALSE(result); // Without subscribing to the event. Publish should fail.
+
+ result = service->subscribe(*request, capability, *upstreamEvents.begin());
+ ASSERT_TRUE(result);
+
+ result = service->subscribe(*request, capability, *downstreamEvents.begin());
+ ASSERT_TRUE(result);
+
+ result = service->publish(capability, *downstreamEvents.begin(), payload);
+ ASSERT_TRUE(result);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/capabilities/test/PublisherForwarderTest.cpp b/src/plugins/capabilities/test/PublisherForwarderTest.cpp
new file mode 100644
index 0000000..d0ba319
--- /dev/null
+++ b/src/plugins/capabilities/test/PublisherForwarderTest.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "capabilities/core/include/PublisherForwarder.h"
+#include "capabilities/core/include/SubscriberForwarder.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+#include "test/mocks/AFBEventMock.h"
+#include "test/mocks/CapabilityMock.h"
+
+using namespace vshl::common::interfaces;
+using namespace vshl::capabilities::core;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class PublisherForwarderTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>();
+
+ mEventCreatorFn = [](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ mockEvent->setName(eventName);
+ return mockEvent;
+ };
+ }
+
+ std::shared_ptr<SubscriberForwarder> createSubscriberForwarder(
+ std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) {
+ EXPECT_CALL(*capability, getUpstreamMessages()).Times(1);
+ EXPECT_CALL(*capability, getDownstreamMessages()).Times(1);
+
+ return SubscriberForwarder::create(mConsoleLogger, mAfbApi, capability);
+ }
+
+ std::shared_ptr<PublisherForwarder> createPublisherForwarder(
+ std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) {
+ return PublisherForwarder::create(mConsoleLogger, capability);
+ }
+
+ std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi;
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::function<std::shared_ptr<IAFBApi::IAFBEvent>(const std::string&)> mEventCreatorFn;
+ std::shared_ptr<::testing::StrictMock<AFBEventMock>> mAfbEventMock;
+};
+
+TEST_F(PublisherForwarderTest, failsInitializationOnInvalidParams) {
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+
+ auto forwarder = PublisherForwarder::create(mConsoleLogger, nullptr);
+ ASSERT_EQ(forwarder, nullptr);
+
+ forwarder = PublisherForwarder::create(nullptr, capability);
+ ASSERT_EQ(forwarder, nullptr);
+}
+
+TEST_F(PublisherForwarderTest, initializesCorrectly) {
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ auto forwarder = createPublisherForwarder(capability);
+
+ ASSERT_NE(forwarder, nullptr);
+}
+
+TEST_F(PublisherForwarderTest, canForwardMessages) {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true));
+ EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(4);
+ auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ return mockEvent;
+ };
+
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto publisherForwarder = createPublisherForwarder(capability);
+ ASSERT_NE(publisherForwarder, nullptr);
+
+ auto subscriberForwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(subscriberForwarder, nullptr);
+
+ std::string payload = "The answer to life the universe and everything = 42";
+ publisherForwarder->setSubscriberForwarder(subscriberForwarder);
+
+ auto itCapability = downstreamEvents.begin();
+ ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload));
+ itCapability = upstreamEvents.begin();
+ ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload));
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/capabilities/test/SubscriberForwarderTest.cpp b/src/plugins/capabilities/test/SubscriberForwarderTest.cpp
new file mode 100644
index 0000000..ff438df
--- /dev/null
+++ b/src/plugins/capabilities/test/SubscriberForwarderTest.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "capabilities/core/include/SubscriberForwarder.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+#include "test/mocks/AFBEventMock.h"
+#include "test/mocks/AFBRequestMock.h"
+#include "test/mocks/CapabilityMock.h"
+
+using namespace vshl::common::interfaces;
+using namespace vshl::capabilities::core;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class SubscriberForwarderTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>();
+
+ mEventCreatorFn = [](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ mockEvent->setName(eventName);
+ return mockEvent;
+ };
+ }
+
+ std::shared_ptr<SubscriberForwarder> createSubscriberForwarder(
+ std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) {
+ EXPECT_CALL(*capability, getUpstreamMessages()).Times(1);
+ EXPECT_CALL(*capability, getDownstreamMessages()).Times(1);
+
+ return SubscriberForwarder::create(mConsoleLogger, mAfbApi, capability);
+ }
+
+ std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi;
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::function<std::shared_ptr<IAFBApi::IAFBEvent>(const std::string&)> mEventCreatorFn;
+ std::shared_ptr<::testing::StrictMock<AFBEventMock>> mAfbEventMock;
+};
+
+TEST_F(SubscriberForwarderTest, failsInitializationOnInvalidParams) {
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+
+ auto forwarder = SubscriberForwarder::create(mConsoleLogger, mAfbApi, nullptr);
+ ASSERT_EQ(forwarder, nullptr);
+
+ forwarder = SubscriberForwarder::create(mConsoleLogger, nullptr, capability);
+ ASSERT_EQ(forwarder, nullptr);
+
+ forwarder = SubscriberForwarder::create(nullptr, mAfbApi, capability);
+ ASSERT_EQ(forwarder, nullptr);
+}
+
+TEST_F(SubscriberForwarderTest, initializesCorrectly) {
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ auto forwarder = createSubscriberForwarder(capability);
+
+ ASSERT_NE(forwarder, nullptr);
+}
+
+TEST_F(SubscriberForwarderTest, createsEventsOnInitialization) {
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(mEventCreatorFn));
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto itCapability = upstreamEvents.begin();
+ EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1);
+ itCapability++;
+ EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1);
+
+ itCapability = downstreamEvents.begin();
+ EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1);
+ itCapability++;
+ EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1);
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+}
+
+TEST_F(SubscriberForwarderTest, canNotSubscribeToNonExistentEvents) {
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(mEventCreatorFn));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto nonExistentEvents = std::list<std::string>({"non", "existent", "events"});
+ auto itCapability = nonExistentEvents.begin();
+ auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>();
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+}
+
+TEST_F(SubscriberForwarderTest, canNotSubscribeOnEventCreationFailure) {
+ // Fail the event creation and return null event.
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Return(nullptr));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto itCapability = downstreamEvents.begin();
+ auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>();
+ std::string payload = "The answer to life the universe and everything = 42";
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+ itCapability = upstreamEvents.begin();
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++));
+}
+
+TEST_F(SubscriberForwarderTest, canSubscribeToEvents) {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ ON_CALL(*mockEvent, subscribe(::testing::_)).WillByDefault(::testing::Return(true));
+ EXPECT_CALL(*mockEvent, subscribe(::testing::_)).Times(4);
+ auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ return mockEvent;
+ };
+
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto itCapability = downstreamEvents.begin();
+ auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>();
+ ASSERT_TRUE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_TRUE(forwarder->subscribe(*request, *itCapability));
+ itCapability = upstreamEvents.begin();
+ ASSERT_TRUE(forwarder->subscribe(*request, *itCapability++));
+ ASSERT_TRUE(forwarder->subscribe(*request, *itCapability));
+}
+
+TEST_F(SubscriberForwarderTest, canNotPublishNonExistentEvents) {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(0);
+ auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ return mockEvent;
+ };
+
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto nonExistentEvents = std::list<std::string>({"non", "existent", "events"});
+ auto itCapability = nonExistentEvents.begin();
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload"));
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload"));
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload"));
+}
+
+TEST_F(SubscriberForwarderTest, canNotPublishOnEventCreationFailure) {
+ // Fail the event creation and return null event.
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Return(nullptr));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto itCapability = downstreamEvents.begin();
+ std::string payload = "The answer to life the universe and everything = 42";
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload));
+ itCapability = upstreamEvents.begin();
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload));
+}
+
+TEST_F(SubscriberForwarderTest, canPublishEvents) {
+ std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>());
+ ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true));
+ EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(4);
+ auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> {
+ return mockEvent;
+ };
+
+ ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator));
+ EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4);
+
+ std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"});
+ std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"});
+
+ auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>();
+ ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents));
+ ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents));
+
+ auto forwarder = createSubscriberForwarder(capability);
+ ASSERT_NE(forwarder, nullptr);
+
+ auto itCapability = downstreamEvents.begin();
+ std::string payload = "The answer to life the universe and everything = 42";
+ ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload));
+ itCapability = upstreamEvents.begin();
+ ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload));
+ ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload));
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/cmake/gtest.cmake b/src/plugins/cmake/gtest.cmake
new file mode 100644
index 0000000..def6559
--- /dev/null
+++ b/src/plugins/cmake/gtest.cmake
@@ -0,0 +1,44 @@
+# gtest
+
+find_package(Threads REQUIRED)
+
+# Enable ExternalProject CMake module
+INCLUDE(ExternalProject)
+
+ExternalProject_Add(
+ gtest
+ URL https://github.com/google/googletest/archive/release-1.8.1.zip
+ SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build"
+ INSTALL_COMMAND ""
+ LOG_DOWNLOAD ON
+ LOG_CONFIGURE ON
+ LOG_BUILD ON
+)
+
+# Get GTest source and binary directories from CMake project
+ExternalProject_Get_Property(gtest source_dir binary_dir)
+
+# Create a libgtest target to be used as a dependency by test programs
+ADD_LIBRARY(libgtest INTERFACE)
+TARGET_INCLUDE_DIRECTORIES(libgtest
+ INTERFACE
+ ${source_dir}/googletest/include
+)
+TARGET_LINK_LIBRARIES(libgtest
+ INTERFACE
+ ${binary_dir}/googlemock/gtest/libgtest.a
+ ${CMAKE_THREAD_LIBS_INIT}
+)
+
+# Create a libgmock target to be used as a dependency by test programs
+ADD_LIBRARY(libgmock INTERFACE)
+TARGET_INCLUDE_DIRECTORIES(libgmock
+ INTERFACE
+ ${source_dir}/googlemock/include
+)
+TARGET_LINK_LIBRARIES(libgmock
+ INTERFACE
+ ${binary_dir}/googlemock/libgmock.a
+ ${CMAKE_THREAD_LIBS_INIT}
+) \ No newline at end of file
diff --git a/src/plugins/core/VRRequestProcessor.h b/src/plugins/core/VRRequestProcessor.h
new file mode 100644
index 0000000..c349a19
--- /dev/null
+++ b/src/plugins/core/VRRequestProcessor.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CORE_INCLUDE_VR_REQUESTPROCESSOR_H_
+#define VSHL_CORE_INCLUDE_VR_REQUESTPROCESSOR_H_
+
+#include <memory>
+#include <unordered_map>
+
+#include "core/include/VRRequest.h"
+#include "core/include/VRRequestProcessorDelegate.h"
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgentsChangeObserver.h"
+
+using namespace std;
+
+namespace vshl {
+namespace core {
+/*
+ * This class is the entry point for all the voice recognition request
+ * creation and arbitration.
+ */
+class VRRequestProcessor {
+public:
+ // Create a VRRequestProcessor.
+ static unique_ptr<VRRequestProcessor> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::core::VRRequestProcessorDelegate> delegate);
+
+ // Triggers a voiceagent to start listening to user speech input.
+ // Returns the request ID. If start fails, then empty request ID
+ // is returned.
+ string startListening();
+
+ // Cancels all the active requests
+ void cancel();
+
+ // Returns the voiceagents observer that belongs to the core module.
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> getVoiceAgentsChangeObserver() const;
+
+ // Destructor
+ ~VRRequestProcessor();
+
+private:
+ // Constructor
+ VRRequestProcessor(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::core::VRRequestProcessorDelegate> delegate);
+
+ // Voiceagents observer
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> mVoiceAgentsChangeObserver;
+
+ // Request Processor Delegate
+ shared_ptr<vshl::core::VRRequestProcessorDelegate> mDelegate;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace core
+} // namespace vshl
+
+#endif // VSHL_CORE_INCLUDE_VR_REQUESTPROCESSOR_H_
diff --git a/src/plugins/core/VRRequestProcessorImpl.cpp b/src/plugins/core/VRRequestProcessorImpl.cpp
new file mode 100644
index 0000000..7441a7d
--- /dev/null
+++ b/src/plugins/core/VRRequestProcessorImpl.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "core/VRRequestProcessor.h"
+
+#include "core/include/VRAgentsObserver.h"
+
+static string TAG = "vshl::core::VRRequestProcessor";
+
+using Level = vshl::utilities::logging::Logger::Level;
+
+namespace vshl {
+namespace core {
+// Create a VRRequestProcessor.
+unique_ptr<VRRequestProcessor> VRRequestProcessor::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::core::VRRequestProcessorDelegate> delegate) {
+ auto processor = std::unique_ptr<VRRequestProcessor>(new VRRequestProcessor(logger, delegate));
+ return processor;
+}
+
+VRRequestProcessor::VRRequestProcessor(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::core::VRRequestProcessorDelegate> delegate) :
+ mLogger(logger),
+ mDelegate(delegate) {
+ mVoiceAgentsChangeObserver = VRAgentsObserver::create(mDelegate);
+}
+
+VRRequestProcessor::~VRRequestProcessor() {
+ mDelegate->cancelAllRequests();
+}
+
+string VRRequestProcessor::startListening() {
+ // Currently start is simply going to send the request to
+ // the default voice agent as the wake word detection is not
+ // enabled.
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> defaultVA = mDelegate->getDefaultVoiceAgent();
+ if (!defaultVA) {
+ mLogger->log(Level::ERROR, TAG, "Failed to start. No default voiceagent found.");
+ return "";
+ }
+
+ // If the requests container is not empty, then clear the
+ // existing requests in flight and create a new request.
+ mDelegate->cancelAllRequests();
+ return mDelegate->startRequestForVoiceAgent(defaultVA);
+}
+
+void VRRequestProcessor::cancel() {
+ // Cancel all pending requests
+ mDelegate->cancelAllRequests();
+}
+
+shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> VRRequestProcessor::getVoiceAgentsChangeObserver()
+ const {
+ return mVoiceAgentsChangeObserver;
+}
+
+} // namespace core
+} // namespace vshl
diff --git a/src/plugins/core/include/VRAgentsObserver.h b/src/plugins/core/include/VRAgentsObserver.h
new file mode 100644
index 0000000..d4c0c7b
--- /dev/null
+++ b/src/plugins/core/include/VRAgentsObserver.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CORE_INCLUDE_VR_AGENTS_OBSERVER_H_
+#define VSHL_CORE_INCLUDE_VR_AGENTS_OBSERVER_H_
+
+#include <memory>
+
+#include "core/include/VRRequestProcessorDelegate.h"
+#include "interfaces/voiceagents/IVoiceAgentsChangeObserver.h"
+#include "utilities/logging/Logger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace core {
+/*
+ * This class will observe the changes to the voiceagents data and transfers
+ * the actual handling responsibility to its delegate.
+ */
+class VRAgentsObserver
+ : public vshl::common::interfaces::IVoiceAgentsChangeObserver {
+public:
+ // Create a VRAgentsObserver.
+ static shared_ptr<VRAgentsObserver>
+ create(weak_ptr<VRRequestProcessorDelegate> delegate);
+
+ ~VRAgentsObserver();
+
+protected:
+ void OnDefaultVoiceAgentChanged(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> defaultVoiceAgent)
+ override;
+ void OnVoiceAgentAdded(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) override;
+ void OnVoiceAgentRemoved(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) override;
+ void OnVoiceAgentActiveWakeWordChanged(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) override;
+ void OnVoiceAgentActivated(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) override;
+ void OnVoiceAgentDeactivated(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) override;
+
+private:
+ // Constructor
+ VRAgentsObserver(weak_ptr<VRRequestProcessorDelegate> delegate);
+
+ // Delegate that needs to be informed of the voiceagent data changes.
+ weak_ptr<VRRequestProcessorDelegate> mWeakDelegate;
+};
+
+} // namespace core
+} // namespace vshl
+
+#endif // VSHL_CORE_INCLUDE_VR_AGENTS_OBSERVER_H_
diff --git a/src/plugins/core/include/VRRequest.h b/src/plugins/core/include/VRRequest.h
new file mode 100644
index 0000000..522ec78
--- /dev/null
+++ b/src/plugins/core/include/VRRequest.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CORE_INCLUDE_VR_REQUEST_H_
+#define VSHL_CORE_INCLUDE_VR_REQUEST_H_
+
+#include <memory>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace core {
+/*
+ * This class implements the notion of a Voice Recognition Request.
+ * Each VR Request is currently mapped to one voice agent.
+ */
+class VRRequest {
+public:
+ // API Verbs
+ static std::string VA_VERB_STARTLISTENING;
+ static std::string VA_VERB_CANCEL;
+
+ // Create a VRRequest.
+ static unique_ptr<VRRequest> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ string requestId,
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent);
+
+ // Destructor
+ ~VRRequest();
+
+ // Invokes the underlying voiceagent's startlistening API.
+ // Returns true if started successfully. False otherwise.
+ bool startListening();
+
+ // Cancels the voice recognition in the unlerlying voiceagent.
+ // Returns true if canceled successfully. False otherwise.
+ bool cancel();
+
+private:
+ // Constructor
+ VRRequest(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ const string requestId,
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent);
+
+ // Binding API reference.
+ shared_ptr<vshl::common::interfaces::IAFBApi> mApi;
+
+ // Voice agent associated with this request
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> mVoiceAgent;
+
+ // Request ID
+ string mRequestId;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace core
+} // namespace vshl
+
+#endif // VSHL_CORE_INCLUDE_VR_REQUEST_H_
diff --git a/src/plugins/core/include/VRRequestProcessorDelegate.h b/src/plugins/core/include/VRRequestProcessorDelegate.h
new file mode 100644
index 0000000..94b7304
--- /dev/null
+++ b/src/plugins/core/include/VRRequestProcessorDelegate.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_CORE_INCLUDE_VR_REQUESTPROCESSORDELEGATE_H_
+#define VSHL_CORE_INCLUDE_VR_REQUESTPROCESSORDELEGATE_H_
+
+#include <memory>
+#include <unordered_map>
+
+#include "core/include/VRRequest.h"
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+#include "utilities/uuid/UUIDGeneration.h"
+
+using namespace std;
+
+namespace vshl {
+namespace core {
+/*
+ * This is a delegate for VRRequestProcessor actions.
+ * The lifetime and dependencies of this object is managed
+ * by VRRequestProcessor. It plays the role of a Delegate in
+ * Delegate pattern and a one shop stop for most of the core
+ * module's state.
+ */
+class VRRequestProcessorDelegate {
+public:
+ // create method
+ static shared_ptr<VRRequestProcessorDelegate> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Destructor
+ ~VRRequestProcessorDelegate();
+
+ // Set default voiceagent
+ void setDefaultVoiceAgent(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent);
+
+ // Get the default voiceagent
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> getDefaultVoiceAgent() const;
+
+ // Add new request to the list and start processing it.
+ // New request is created and startListening on the
+ // voiceagent is called.
+ string startRequestForVoiceAgent(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent);
+
+ // Cancel all requests
+ void cancelAllRequests();
+
+ // Get All outstanding requests
+ // Used only by Test
+ unordered_map<string, shared_ptr<VRRequest>> getAllRequests();
+
+private:
+ // Constructor
+ VRRequestProcessorDelegate(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mApi;
+
+ // Default voiceagent
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> mDefaultVoiceAgent;
+
+ // A map of voiceagent IDs and their respective VR Request objects.
+ unordered_map<string, shared_ptr<VRRequest>> mVRRequests;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace core
+} // namespace vshl
+
+#endif // VSHL_CORE_INCLUDE_VR_REQUESTPROCESSORDELEGATE_H_
diff --git a/src/plugins/core/src/VRAgentsObserverImpl.cpp b/src/plugins/core/src/VRAgentsObserverImpl.cpp
new file mode 100644
index 0000000..7ee4a7e
--- /dev/null
+++ b/src/plugins/core/src/VRAgentsObserverImpl.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "core/include/VRAgentsObserver.h"
+
+namespace vshl {
+namespace core {
+
+shared_ptr<VRAgentsObserver> VRAgentsObserver::create(weak_ptr<VRRequestProcessorDelegate> delegate) {
+ auto observer = std::shared_ptr<VRAgentsObserver>(new VRAgentsObserver(delegate));
+ return observer;
+}
+
+VRAgentsObserver::VRAgentsObserver(weak_ptr<VRRequestProcessorDelegate> delegate) {
+ mWeakDelegate = delegate;
+}
+
+VRAgentsObserver::~VRAgentsObserver() {
+}
+
+void VRAgentsObserver::OnDefaultVoiceAgentChanged(shared_ptr<vshl::common::interfaces::IVoiceAgent> defaultVoiceAgent) {
+ if (auto delegate = mWeakDelegate.lock()) {
+ delegate->setDefaultVoiceAgent(defaultVoiceAgent);
+ }
+}
+
+void VRAgentsObserver::OnVoiceAgentAdded(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+}
+
+void VRAgentsObserver::OnVoiceAgentRemoved(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+}
+
+void VRAgentsObserver::OnVoiceAgentActiveWakeWordChanged(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+ // Not Implemented
+}
+
+void VRAgentsObserver::OnVoiceAgentActivated(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+}
+
+void VRAgentsObserver::OnVoiceAgentDeactivated(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+}
+} // namespace core
+} // namespace vshl
diff --git a/src/plugins/core/src/VRRequestImpl.cpp b/src/plugins/core/src/VRRequestImpl.cpp
new file mode 100644
index 0000000..00adf96
--- /dev/null
+++ b/src/plugins/core/src/VRRequestImpl.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "core/include/VRRequest.h"
+
+#define FREEIF(x) \
+ if (!x) { \
+ free(x); \
+ }
+#define BREAKIF(x) \
+ if (x) { \
+ result = false; \
+ break; \
+ }
+
+static string TAG = "vshl::core::VRRequest";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace core {
+
+string VRRequest::VA_VERB_STARTLISTENING = "startListening";
+string VRRequest::VA_VERB_CANCEL = "cancel";
+
+unique_ptr<VRRequest> VRRequest::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ const string requestId,
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+ if (logger == nullptr) {
+ return nullptr;
+ }
+
+ if (afbApi == nullptr) {
+ logger->log(Level::ERROR, TAG, "Invalid AFB API");
+ return nullptr;
+ }
+
+ auto request = std::unique_ptr<VRRequest>(new VRRequest(logger, afbApi, requestId, voiceAgent));
+ return request;
+}
+
+VRRequest::VRRequest(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi,
+ string requestId,
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) :
+ mApi(afbApi),
+ mRequestId(requestId),
+ mVoiceAgent(voiceAgent),
+ mLogger(logger) {
+}
+
+VRRequest::~VRRequest() {
+}
+
+bool VRRequest::startListening() {
+ json_object* object = NULL;
+ std::string error, info;
+ bool result = true;
+ int rc = mApi->callSync(mVoiceAgent->getApi(), VA_VERB_STARTLISTENING, NULL, &object, error, info);
+
+ FREEIF(object);
+
+ return true;
+}
+
+bool VRRequest::cancel() {
+ json_object* object = NULL;
+ std::string error, info;
+ bool result = true;
+ int rc = mApi->callSync(mVoiceAgent->getApi(), VA_VERB_CANCEL, NULL, &object, error, info);
+
+ FREEIF(object);
+
+ return true;
+}
+
+} // namespace core
+} // namespace vshl
diff --git a/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp b/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp
new file mode 100644
index 0000000..e20b22e
--- /dev/null
+++ b/src/plugins/core/src/VRRequestProcessorDelegateImpl.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "core/include/VRRequestProcessorDelegate.h"
+
+static string TAG = "vshl::core::VRRequestProcessorDelegate";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace core {
+shared_ptr<VRRequestProcessorDelegate> VRRequestProcessorDelegate::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ auto delegate = std::shared_ptr<VRRequestProcessorDelegate>(new VRRequestProcessorDelegate(logger, afbApi));
+ return delegate;
+}
+
+VRRequestProcessorDelegate::VRRequestProcessorDelegate(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mApi(afbApi),
+ mLogger(logger) {
+}
+
+VRRequestProcessorDelegate::~VRRequestProcessorDelegate() {
+ mVRRequests.clear();
+}
+
+string VRRequestProcessorDelegate::startRequestForVoiceAgent(
+ shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+ if (!mApi) {
+ mLogger->log(Level::ERROR, TAG, "Failed to startRequestForVoiceAgent: " + voiceAgent->getId() + ", No API.");
+ return "";
+ }
+
+ // Generate a new request ID.
+ string newReqId = vshl::utilities::uuid::generateUUID();
+
+ // Create a new request and start listening.
+ shared_ptr<VRRequest> newRequest = VRRequest::create(mLogger, mApi, newReqId, voiceAgent);
+
+ // mLogger->log(Level::DEBUG, TAG, "Starting request with ID: " + newReqId);
+ if (!newRequest->startListening()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to start listening.");
+ return "";
+ }
+
+ // Insert only if its started successfully.
+ mVRRequests.insert(make_pair(voiceAgent->getId(), newRequest));
+
+ return newReqId;
+}
+
+void VRRequestProcessorDelegate::cancelAllRequests() {
+ // Cancel Pending requests
+ if (!mVRRequests.empty()) {
+ auto vrRequestsIt = mVRRequests.begin();
+ while (vrRequestsIt != mVRRequests.end()) {
+ if (!vrRequestsIt->second->cancel()) {
+ mLogger->log(Level::WARNING, TAG, "Failed to cancel request: " + vrRequestsIt->first);
+ }
+ vrRequestsIt++;
+ }
+ mVRRequests.clear();
+ }
+}
+
+unordered_map<string, shared_ptr<VRRequest>> VRRequestProcessorDelegate::getAllRequests() {
+ return mVRRequests;
+}
+
+void VRRequestProcessorDelegate::setDefaultVoiceAgent(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent) {
+ mDefaultVoiceAgent = voiceAgent;
+}
+
+shared_ptr<vshl::common::interfaces::IVoiceAgent> VRRequestProcessorDelegate::getDefaultVoiceAgent() const {
+ return mDefaultVoiceAgent;
+}
+
+} // namespace core
+} // namespace vshl
diff --git a/src/plugins/core/test/VRRequestProcessorTest.cpp b/src/plugins/core/test/VRRequestProcessorTest.cpp
new file mode 100644
index 0000000..c1a37df
--- /dev/null
+++ b/src/plugins/core/test/VRRequestProcessorTest.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "core/VRRequestProcessor.h"
+#include "voiceagents/include/VoiceAgent.h"
+#include "voiceagents/test/VoiceAgentsTestData.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+
+using namespace vshl::core;
+using namespace vshl::voiceagents;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VRRequestProcessorTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>();
+
+ auto vaTestData = *(getVoiceAgentsTestData().begin());
+ mVoiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ vaTestData.id,
+ vaTestData.name,
+ vaTestData.description,
+ vaTestData.api,
+ vaTestData.vendor,
+ vaTestData.activeWakeword,
+ vaTestData.isActive,
+ vaTestData.wakewords);
+
+ mVRReqProcessorDelegate = VRRequestProcessorDelegate::create(mConsoleLogger, mAfbApi);
+ mVRRequestProcessor = VRRequestProcessor::create(mConsoleLogger, mVRReqProcessorDelegate);
+ }
+
+ std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi;
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::shared_ptr<VoiceAgent> mVoiceAgent;
+
+ std::shared_ptr<VRRequestProcessorDelegate> mVRReqProcessorDelegate;
+ std::shared_ptr<VRRequestProcessor> mVRRequestProcessor;
+};
+
+TEST_F(VRRequestProcessorTest, initializesCorrectly) {
+ ASSERT_NE(mVRRequestProcessor, nullptr);
+}
+
+TEST_F(VRRequestProcessorTest, startListeningFailsOnLackOfDefaultAgent) {
+ auto requestId = mVRRequestProcessor->startListening();
+ ASSERT_EQ(requestId, "");
+}
+
+TEST_F(VRRequestProcessorTest, startListeningAndCancelWorks) {
+ mVRReqProcessorDelegate->setDefaultVoiceAgent(mVoiceAgent);
+
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_STARTLISTENING,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_CANCEL,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+ }
+
+ mVRRequestProcessor->startListening();
+ auto requests = mVRReqProcessorDelegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 1);
+
+ mVRRequestProcessor->cancel();
+ requests = mVRReqProcessorDelegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 0);
+}
+
+TEST_F(VRRequestProcessorTest, requestIsCancelledOnObjectDestruction) {
+ auto delegate = VRRequestProcessorDelegate::create(mConsoleLogger, mAfbApi);
+ auto processor = VRRequestProcessor::create(mConsoleLogger, delegate);
+
+ delegate->setDefaultVoiceAgent(mVoiceAgent);
+
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_STARTLISTENING,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_CANCEL,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+ }
+
+ auto requestId = processor->startListening();
+ ASSERT_NE(requestId, "");
+
+ auto requests = delegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 1);
+}
+
+TEST_F(VRRequestProcessorTest, backToBackStartListeningCancelsEarlierRequest) {
+ mVRReqProcessorDelegate->setDefaultVoiceAgent(mVoiceAgent);
+
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_STARTLISTENING,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_CANCEL,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_STARTLISTENING,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_CANCEL,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+ }
+
+ mVRRequestProcessor->startListening();
+ auto requests = mVRReqProcessorDelegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 1);
+
+ mVRRequestProcessor->startListening();
+ requests = mVRReqProcessorDelegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 1);
+
+ mVRRequestProcessor->cancel();
+ requests = mVRReqProcessorDelegate->getAllRequests();
+ ASSERT_EQ(requests.size(), 0);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/core/test/VRRequestTest.cpp b/src/plugins/core/test/VRRequestTest.cpp
new file mode 100644
index 0000000..b1cd0a6
--- /dev/null
+++ b/src/plugins/core/test/VRRequestTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "core/include/VRRequest.h"
+#include "voiceagents/include/VoiceAgent.h"
+#include "voiceagents/test/VoiceAgentsTestData.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+
+using namespace vshl::core;
+using namespace vshl::voiceagents;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VRRequestTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mRequestId = "Req-0001";
+
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>();
+
+ auto vaTestData = *(getVoiceAgentsTestData().begin());
+ mVoiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ vaTestData.id,
+ vaTestData.name,
+ vaTestData.description,
+ vaTestData.api,
+ vaTestData.vendor,
+ vaTestData.activeWakeword,
+ vaTestData.isActive,
+ vaTestData.wakewords);
+
+ mVRRequest = VRRequest::create(mConsoleLogger, mAfbApi, mRequestId, mVoiceAgent);
+ }
+
+ std::string mRequestId;
+ std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi;
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::shared_ptr<VoiceAgent> mVoiceAgent;
+
+ std::shared_ptr<VRRequest> mVRRequest;
+};
+
+TEST_F(VRRequestTest, initializesCorrectly) {
+ ASSERT_NE(mVRRequest, nullptr);
+}
+
+TEST_F(VRRequestTest, failsCreationOnInvalidParams) {
+ auto vrRequest = VRRequest::create(mConsoleLogger, nullptr, mRequestId, mVoiceAgent);
+ ASSERT_EQ(vrRequest, nullptr);
+
+ vrRequest = VRRequest::create(nullptr, mAfbApi, mRequestId, mVoiceAgent);
+ ASSERT_EQ(vrRequest, nullptr);
+}
+
+TEST_F(VRRequestTest, startsListeningSuccessfully) {
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(),
+ VRRequest::VA_VERB_STARTLISTENING,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_,
+ ::testing::_))
+ .Times(1);
+
+ ASSERT_TRUE(mVRRequest->startListening());
+}
+
+TEST_F(VRRequestTest, cancelsSuccessfully) {
+ EXPECT_CALL(
+ *mAfbApi,
+ callSync(
+ mVoiceAgent->getApi(), VRRequest::VA_VERB_CANCEL, ::testing::_, ::testing::_, ::testing::_, ::testing::_))
+ .Times(1);
+
+ ASSERT_TRUE(mVRRequest->cancel());
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/interfaces/afb/IAFBApi.h b/src/plugins/interfaces/afb/IAFBApi.h
new file mode 100644
index 0000000..cd98006
--- /dev/null
+++ b/src/plugins/interfaces/afb/IAFBApi.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_AFBAPI_H_
+#define VSHL_COMMON_INTERFACES_AFBAPI_H_
+
+#include <memory>
+#include <string>
+
+#include <json-c/json_object.h>
+
+using namespace std;
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+
+/**
+ * Interface to represent AFB Request.
+ */
+class IAFBRequest {
+public:
+ /**
+ * Gets the native request object.
+ */
+ virtual void* getNativeRequest() = 0;
+};
+
+/**
+ * Interface to encapsulate all AFB (AGL Application Framework Binding)
+ * functions.
+ */
+class IAFBApi {
+public:
+ /**
+ * Interface to represent AFB Event
+ */
+ class IAFBEvent {
+ public:
+ /**
+ * Gets human readable name of the event.
+ */
+ virtual std::string getName() const = 0;
+
+ /**
+ * Returns true if event is valid. False otherwise.
+ */
+ virtual bool isValid() = 0;
+
+ /**
+ * Publish event to all observers.
+ *
+ * @return The number of observers that received the event.
+ */
+ virtual int publishEvent(struct json_object* payload) = 0;
+
+ /**
+ * Subscribe to the event
+ *
+ * @c request Party interested in the event.
+ */
+ virtual bool subscribe(IAFBRequest& request) = 0;
+
+ /**
+ * Unsubscribe to the event
+ *
+ * @c request Party no longer interested in the event.
+ */
+ virtual bool unsubscribe(IAFBRequest& request) = 0;
+ };
+
+ virtual std::shared_ptr<IAFBEvent> createEvent(const std::string& eventName) = 0;
+
+ virtual int callSync(
+ const std::string& api,
+ const std::string& verb,
+ struct json_object* request,
+ struct json_object** result,
+ std::string& error,
+ std::string& info) = 0;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_AFBAPI_H_
diff --git a/src/plugins/interfaces/capabilities/ICapability.h b/src/plugins/interfaces/capabilities/ICapability.h
new file mode 100644
index 0000000..4b134b7
--- /dev/null
+++ b/src/plugins/interfaces/capabilities/ICapability.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_ICAPABILITY_H_
+#define VSHL_COMMON_INTERFACES_ICAPABILITY_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+
+/*
+ * This interface defines the structure for a specific voiceagent capability.
+ */
+class ICapability {
+public:
+ /*
+ * Returns the capability's name.
+ */
+ virtual string getName() const = 0;
+
+ /*
+ * Returns the list of upstream messages.
+ */
+ virtual list<string> getUpstreamMessages() const = 0;
+
+ /*
+ * Returns the list of downstream messages
+ */
+ virtual list<string> getDownstreamMessages() const = 0;
+
+ /**
+ * Virtual destructor to assure proper cleanup of derived types.
+ */
+ virtual ~ICapability() = default;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_ICAPABILITY_H_
diff --git a/src/plugins/interfaces/utilities/events/IEventFilter.h b/src/plugins/interfaces/utilities/events/IEventFilter.h
new file mode 100644
index 0000000..33d8202
--- /dev/null
+++ b/src/plugins/interfaces/utilities/events/IEventFilter.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_IEVENTFILTER_H_
+#define VSHL_COMMON_INTERFACES_IEVENTFILTER_H_
+
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+/*
+ * This is an abstract class that is responsible for filtering the events
+ * that are delivered to the high level voice service from apps or voiceagents.
+ */
+class IEventFilter {
+public:
+ // Name of the event filter.
+ virtual string getName() = 0;
+
+ // Every event filter needs to implement this method and
+ // return true if consuming the event or false otherwise.
+ virtual bool onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) = 0;
+
+ // Destructor
+ virtual ~IEventFilter() = default;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_IEVENTFILTER_H_
diff --git a/src/plugins/interfaces/utilities/logging/ILogger.h b/src/plugins/interfaces/utilities/logging/ILogger.h
new file mode 100644
index 0000000..a2618bc
--- /dev/null
+++ b/src/plugins/interfaces/utilities/logging/ILogger.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_LOGGER_H_
+#define VSHL_COMMON_INTERFACES_LOGGER_H_
+
+#include <string>
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+
+class ILogger {
+public:
+ enum Level {
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ NOTICE,
+ };
+
+ virtual void log(Level level, const std::string &tag,
+ const std::string &message) = 0;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_LOGGER_H_
diff --git a/src/plugins/interfaces/voiceagents/IVoiceAgent.h b/src/plugins/interfaces/voiceagents/IVoiceAgent.h
new file mode 100644
index 0000000..367ad72
--- /dev/null
+++ b/src/plugins/interfaces/voiceagents/IVoiceAgent.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_IVOICEAGENT_H_
+#define VSHL_COMMON_INTERFACES_IVOICEAGENT_H_
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+
+using namespace std;
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+
+/*
+ * This interface define the structure for VoiceAgent Information.
+ * The implementation of this structure is owned by voiceagents module.
+ */
+class IVoiceAgent {
+public:
+ /*
+ * Set the active wakeword for this voiceagent
+ */
+ virtual bool setActiveWakeWord(const string &wakeword) = 0;
+
+ /*
+ * Sets the activation state of this voiceagent
+ */
+ virtual void setIsActive(bool active) = 0;
+
+ /*
+ * Returns the voiceagent's ID.
+ */
+ virtual string getId() const = 0;
+
+ /*
+ * Returns the voiceagent's name.
+ */
+ virtual string getName() const = 0;
+
+ /*
+ * Returns the voiceagent's description.
+ */
+ virtual string getDescription() const = 0;
+
+ /*
+ * Returns the voiceagent's API.
+ */
+ virtual string getApi() const = 0;
+
+ /*
+ * Returns the voiceagent's vendor information/
+ */
+ virtual string getVendor() const = 0;
+
+ /*
+ * Returns the list of wakewords mapped to the voiceagent.
+ */
+ virtual shared_ptr<unordered_set<string>> getWakeWords() const = 0;
+
+ /*
+ * Returns true if the voiceagent is active. False otherwise.
+ */
+ virtual bool isActive() const = 0;
+
+ /*
+ * Returns the active wakeword for the voiceagent.
+ */
+ virtual string getActiveWakeword() const = 0;
+
+ /**
+ * Virtual destructor to assure proper cleanup of derived types.
+ */
+ virtual ~IVoiceAgent() = default;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_IVOICEAGENT_H_
diff --git a/src/plugins/interfaces/voiceagents/IVoiceAgentsChangeObserver.h b/src/plugins/interfaces/voiceagents/IVoiceAgentsChangeObserver.h
new file mode 100644
index 0000000..e552ab5
--- /dev/null
+++ b/src/plugins/interfaces/voiceagents/IVoiceAgentsChangeObserver.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_COMMON_INTERFACES_IVOICEAGENTSCHANGEOBSERVER_H_
+#define VSHL_COMMON_INTERFACES_IVOICEAGENTSCHANGEOBSERVER_H_
+
+#include <memory>
+#include <string>
+
+#include "interfaces/voiceagents/IVoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace common {
+namespace interfaces {
+
+/*
+ * This interface is used to observe changes to the voiceagents datastore.
+ * The voiceagents data store is contained in the voiceagents module.
+ */
+class IVoiceAgentsChangeObserver {
+public:
+ /**
+ * This method notifies the observers that the default voiceagent selection
+ * has been updated.
+ */
+ virtual void
+ OnDefaultVoiceAgentChanged(shared_ptr<IVoiceAgent> defaultVoiceAgent) = 0;
+
+ /**
+ * This method notifies the observers that a new voiceagent has been added.
+ */
+ virtual void OnVoiceAgentAdded(shared_ptr<IVoiceAgent> voiceAgent) = 0;
+
+ /**
+ * This method notifies the observers that a voiceagent is removed.
+ */
+ virtual void OnVoiceAgentRemoved(shared_ptr<IVoiceAgent> voiceAgent) = 0;
+
+ /**
+ * This method notifies the observers that active wakeword for a voiceagent is
+ * updated.
+ */
+ virtual void
+ OnVoiceAgentActiveWakeWordChanged(shared_ptr<IVoiceAgent> voiceAgent) = 0;
+
+ /**
+ * This method notifies the observers that the voiceagent has been activated.
+ */
+ virtual void OnVoiceAgentActivated(shared_ptr<IVoiceAgent> voiceAgent) = 0;
+
+ /**
+ * This method notifies the observers that the voiceagent has been activated.
+ */
+ virtual void OnVoiceAgentDeactivated(shared_ptr<IVoiceAgent> voiceAgent) = 0;
+
+ /**
+ * Virtual destructor to assure proper cleanup of derived types.
+ */
+ virtual ~IVoiceAgentsChangeObserver() = default;
+};
+
+} // namespace interfaces
+} // namespace common
+} // namespace vshl
+
+#endif // VSHL_COMMON_INTERFACES_IVOICEAGENTSCHANGEOBSERVER_H_
diff --git a/src/plugins/test/common/ConsoleLogger.cpp b/src/plugins/test/common/ConsoleLogger.cpp
new file mode 100644
index 0000000..d4f9eef
--- /dev/null
+++ b/src/plugins/test/common/ConsoleLogger.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <iostream>
+
+#include "test/common/ConsoleLogger.h"
+
+namespace vshl {
+namespace test {
+namespace common {
+
+void ConsoleLogger::log(Level level, const std::string& tag, const std::string& message) {
+ string format_msg = "Tag: " + tag + ", message: " + message;
+ std::cout << format_msg << std::endl;
+}
+
+} // namespace common
+} // namespace test
+} // namespace vshl
diff --git a/src/plugins/test/common/ConsoleLogger.h b/src/plugins/test/common/ConsoleLogger.h
new file mode 100644
index 0000000..11bc0d8
--- /dev/null
+++ b/src/plugins/test/common/ConsoleLogger.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_COMMON_CONSOLE_LOGGER_H_
+#define VSHL_TEST_COMMON_CONSOLE_LOGGER_H_
+
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace test {
+namespace common {
+
+class ConsoleLogger : public vshl::common::interfaces::ILogger {
+public:
+ // ILogger interface
+ void log(Level level, const std::string &tag,
+ const std::string &message) override;
+};
+
+} // namespace common
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_COMMON_CONSOLE_LOGGER_H_
diff --git a/src/plugins/test/mocks/AFBApiMock.h b/src/plugins/test/mocks/AFBApiMock.h
new file mode 100644
index 0000000..46e2e99
--- /dev/null
+++ b/src/plugins/test/mocks/AFBApiMock.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_MOCKS_AFBAPIMOCK_H_
+#define VSHL_TEST_MOCKS_AFBAPIMOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "interfaces/afb/IAFBApi.h"
+
+namespace vshl {
+namespace test {
+
+class AFBApiMock : public vshl::common::interfaces::IAFBApi {
+public:
+ MOCK_METHOD1(createEvent, std::shared_ptr<IAFBEvent>(const std::string& eventName));
+ MOCK_METHOD6(
+ callSync,
+ int(const std::string& api,
+ const std::string& verb,
+ struct json_object* request,
+ struct json_object** result,
+ std::string& error,
+ std::string& info));
+};
+
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_MOCKS_AFBAPIMOCK_H_ \ No newline at end of file
diff --git a/src/plugins/test/mocks/AFBEventMock.h b/src/plugins/test/mocks/AFBEventMock.h
new file mode 100644
index 0000000..3d78e9f
--- /dev/null
+++ b/src/plugins/test/mocks/AFBEventMock.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_MOCKS_AFBEVENTMOCK_H_
+#define VSHL_TEST_MOCKS_AFBEVENTMOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "interfaces/afb/IAFBApi.h"
+
+namespace vshl {
+namespace test {
+
+class AFBEventMock : public vshl::common::interfaces::IAFBApi::IAFBEvent {
+public:
+ void setName(const std::string& name) {
+ mName = name;
+ }
+
+ std::string getName() const override {
+ return mName;
+ }
+
+ MOCK_METHOD0(isValid, bool());
+ MOCK_METHOD1(publishEvent, int(struct json_object* payload));
+ MOCK_METHOD1(subscribe, bool(vshl::common::interfaces::IAFBRequest& request));
+ MOCK_METHOD1(unsubscribe, bool(vshl::common::interfaces::IAFBRequest& request));
+
+private:
+ std::string mName;
+};
+
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_MOCKS_AFBEVENTMOCK_H_ \ No newline at end of file
diff --git a/src/plugins/test/mocks/AFBRequestMock.h b/src/plugins/test/mocks/AFBRequestMock.h
new file mode 100644
index 0000000..5557565
--- /dev/null
+++ b/src/plugins/test/mocks/AFBRequestMock.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_
+#define VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "interfaces/afb/IAFBApi.h"
+
+namespace vshl {
+namespace test {
+
+class AFBRequestMock : public vshl::common::interfaces::IAFBRequest {
+public:
+ MOCK_METHOD0(getNativeRequest, void*());
+};
+
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_ \ No newline at end of file
diff --git a/src/plugins/test/mocks/CapabilityMock.h b/src/plugins/test/mocks/CapabilityMock.h
new file mode 100644
index 0000000..a2201df
--- /dev/null
+++ b/src/plugins/test/mocks/CapabilityMock.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_MOCKS_CAPABILITYMOCK_H_
+#define VSHL_TEST_MOCKS_CAPABILITYMOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "interfaces/capabilities/ICapability.h"
+
+namespace vshl {
+namespace test {
+
+class CapabilityMock : public vshl::common::interfaces::ICapability {
+public:
+ MOCK_CONST_METHOD0(getName, std::string());
+ MOCK_CONST_METHOD0(getUpstreamMessages, std::list<std::string>());
+ MOCK_CONST_METHOD0(getDownstreamMessages, std::list<std::string>());
+};
+
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_MOCKS_CAPABILITYMOCK_H_ \ No newline at end of file
diff --git a/src/plugins/test/mocks/VoiceAgentsChangeObserverMock.h b/src/plugins/test/mocks/VoiceAgentsChangeObserverMock.h
new file mode 100644
index 0000000..6edeea2
--- /dev/null
+++ b/src/plugins/test/mocks/VoiceAgentsChangeObserverMock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_TEST_MOCKS_VOICEAGENTSCHANGEOBSERVERMOCK_H_
+#define VSHL_TEST_MOCKS_VOICEAGENTSCHANGEOBSERVERMOCK_H_
+
+#include <gmock/gmock.h>
+
+#include "interfaces/voiceagents/IVoiceAgentsChangeObserver.h"
+
+namespace vshl {
+namespace test {
+
+class VoiceAgentsChangeObserverMock : public vshl::common::interfaces::IVoiceAgentsChangeObserver {
+public:
+ MOCK_METHOD1(OnDefaultVoiceAgentChanged, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> defaultVoiceAgent));
+ MOCK_METHOD1(OnVoiceAgentAdded, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent));
+ MOCK_METHOD1(OnVoiceAgentRemoved, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent));
+ MOCK_METHOD1(OnVoiceAgentActiveWakeWordChanged, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent));
+ MOCK_METHOD1(OnVoiceAgentActivated, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent));
+ MOCK_METHOD1(OnVoiceAgentDeactivated, void(shared_ptr<vshl::common::interfaces::IVoiceAgent> voiceAgent));
+};
+
+} // namespace test
+} // namespace vshl
+
+#endif // VSHL_TEST_MOCKS_VOICEAGENTSCHANGEOBSERVERMOCK_H_ \ No newline at end of file
diff --git a/src/plugins/utilities/events/EventRouter.cpp b/src/plugins/utilities/events/EventRouter.cpp
new file mode 100644
index 0000000..999c3dd
--- /dev/null
+++ b/src/plugins/utilities/events/EventRouter.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "utilities/events/EventRouter.h"
+
+static string TAG = "vshl::utilities::events::EventRouter";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace utilities {
+namespace events {
+
+unique_ptr<EventRouter> EventRouter::create(shared_ptr<vshl::common::interfaces::ILogger> logger) {
+ return std::unique_ptr<EventRouter>(new EventRouter(logger));
+}
+
+EventRouter::EventRouter(shared_ptr<vshl::common::interfaces::ILogger> logger) : mLogger(logger) {
+}
+
+EventRouter::~EventRouter() {
+ mEventFilters.clear();
+}
+
+bool EventRouter::handleIncomingEvent(const string eventName, const string voiceAgentId, const string payload) {
+ for (auto eventFilter : mEventFilters) {
+ if (eventFilter->onIncomingEvent(eventName, voiceAgentId, payload)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool EventRouter::addEventFilter(shared_ptr<vshl::common::interfaces::IEventFilter> filter) {
+ if (!filter) {
+ mLogger->log(Level::ERROR, TAG, "Failed to add event filter. Invalid arguments.");
+ return false;
+ }
+
+ mEventFilters.insert(filter);
+ return true;
+}
+
+bool EventRouter::removeEventFilter(shared_ptr<vshl::common::interfaces::IEventFilter> filter) {
+ if (!filter) {
+ mLogger->log(Level::ERROR, TAG, "Failed to add remove filter. Invalid arguments.");
+ return false;
+ }
+
+ mEventFilters.erase(filter);
+ return true;
+}
+
+} // namespace events
+} // namespace utilities
+} // namespace vshl
diff --git a/src/plugins/utilities/events/EventRouter.h b/src/plugins/utilities/events/EventRouter.h
new file mode 100644
index 0000000..fd7f0c2
--- /dev/null
+++ b/src/plugins/utilities/events/EventRouter.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_UTILITIES_EVENTS_EVENTMANAGER_H_
+#define VSHL_UTILITIES_EVENTS_EVENTMANAGER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+
+#include "interfaces/utilities/events/IEventFilter.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace utilities {
+namespace events {
+/*
+ * This class is responsible for routing incoming events to
+ * the appropriate event listener for consumption.
+ * Note: The listeners should implement the IEventFilter class.
+ */
+class EventRouter {
+public:
+ static unique_ptr<EventRouter> create(shared_ptr<vshl::common::interfaces::ILogger> logger);
+
+ // Destructor
+ ~EventRouter();
+
+ // Add event filter as listerner.
+ bool addEventFilter(shared_ptr<vshl::common::interfaces::IEventFilter> filter);
+
+ // Remove event filter as listerner.
+ bool removeEventFilter(shared_ptr<vshl::common::interfaces::IEventFilter> filter);
+
+ // This method is called by the controller for routing
+ // the event to appropriate listener.
+ bool handleIncomingEvent(const string eventName, const string voiceAgentId, const string payload);
+
+private:
+ EventRouter(shared_ptr<vshl::common::interfaces::ILogger> logger);
+
+ // set of event filters.
+ unordered_set<shared_ptr<vshl::common::interfaces::IEventFilter>> mEventFilters;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace events
+} // namespace utilities
+} // namespace vshl
+
+#endif // VSHL_UTILITIES_EVENTS_EVENTROUTER_H_
diff --git a/src/plugins/utilities/logging/Logger.cpp b/src/plugins/utilities/logging/Logger.cpp
new file mode 100644
index 0000000..6374e19
--- /dev/null
+++ b/src/plugins/utilities/logging/Logger.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "utilities/logging/Logger.h"
+
+namespace vshl {
+namespace utilities {
+namespace logging {
+
+// Constructor
+Logger::Logger(AFB_ApiT api) {
+ mApi = api;
+}
+
+unique_ptr<Logger> Logger::create(AFB_ApiT api) {
+ auto logger = std::unique_ptr<Logger>(new Logger(api));
+ return logger;
+}
+
+void Logger::log(Level level, const std::string& tag, const std::string& message) {
+ string format_msg = "Tag: " + tag + ", message: " + message;
+ switch (level) {
+ case Level::NOTICE:
+ AFB_ApiNotice(mApi, format_msg.c_str());
+ break;
+ case Level::WARNING:
+ AFB_ApiWarning(mApi, format_msg.c_str());
+ break;
+ case Level::DEBUG:
+ AFB_ApiDebug(mApi, format_msg.c_str());
+ break;
+ case Level::ERROR:
+ AFB_ApiError(mApi, format_msg.c_str());
+ break;
+ case Level::INFO:
+ AFB_ApiInfo(mApi, format_msg.c_str());
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace logging
+} // namespace utilities
+} // namespace vshl
diff --git a/src/plugins/utilities/logging/Logger.h b/src/plugins/utilities/logging/Logger.h
new file mode 100644
index 0000000..79c89a5
--- /dev/null
+++ b/src/plugins/utilities/logging/Logger.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_UTILITIES_LOGGING_LOGGER_H_
+#define VSHL_UTILITIES_LOGGING_LOGGER_H_
+
+#include <memory>
+
+extern "C" {
+#define AFB_BINDING_VERSION 3
+#include "afb-definitions.h"
+#include "ctl-plugin.h"
+};
+
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshl {
+namespace utilities {
+namespace logging {
+
+class Logger : public vshl::common::interfaces::ILogger {
+public:
+ static std::unique_ptr<Logger> create(AFB_ApiT api);
+
+ // ILogger interface
+ void log(Level level, const std::string &tag,
+ const std::string &message) override;
+
+private:
+ Logger(AFB_ApiT api);
+
+ // Binding API reference
+ AFB_ApiT mApi;
+};
+
+} // namespace logging
+} // namespace utilities
+} // namespace vshl
+
+#endif // VSHL_UTILITIES_LOGGING_LOGGER_H_
diff --git a/src/plugins/utilities/uuid/UUIDGeneration.cpp b/src/plugins/utilities/uuid/UUIDGeneration.cpp
new file mode 100644
index 0000000..bb03fc6
--- /dev/null
+++ b/src/plugins/utilities/uuid/UUIDGeneration.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+// This code is copied from https://github.com/alexa/avs-device-sdk/
+// Minor modifications are made to the original file.
+
+#include <random>
+#include <chrono>
+#include <sstream>
+#include <iomanip>
+#include <mutex>
+#include <climits>
+#include <algorithm>
+#include <functional>
+
+#include "utilities/uuid/UUIDGeneration.h"
+
+namespace vshl {
+namespace utilities {
+namespace uuid {
+
+/// String to identify log entries originating from this file.
+static const std::string TAG("UUIDGeneration");
+
+/// The UUID version (Version 4), shifted into the correct position in the byte.
+static const uint8_t UUID_VERSION_VALUE = 4 << 4;
+
+/// The UUID variant (Variant 1), shifted into the correct position in the byte.
+static const size_t UUID_VARIANT_VALUE = 2 << 6;
+
+/// Separator used between UUID fields.
+static const std::string SEPARATOR("-");
+
+/// Number of bits in the replacement value.
+static const size_t MAX_NUM_REPLACEMENT_BITS = CHAR_BIT;
+
+/// Number of bits in a hex digit.
+static const size_t BITS_IN_HEX_DIGIT = 4;
+
+/**
+ * Randomly generate a string of hex digits. Before the conversion of hex to string,
+ * the function allows replacement of the bits of the first two generated hex digits.
+ * Replacement happens starting at the most significant, to the least significant bit.
+ *
+ * @param ibe A random number generator.
+ * @param numDigits The number of hex digits [0-9],[a-f] to generate.
+ * @param replacementBits The replacement value for up to the first two digits generated.
+ * @param numReplacementBits The number of bits of @c replacementBits to use.
+ *
+ * @return A hex string of length @c numDigits.
+ */
+static const std::string generateHexWithReplacement(
+ std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>& ibe,
+ unsigned int numDigits,
+ uint8_t replacementBits,
+ unsigned short numReplacementBits) {
+ if (numReplacementBits > MAX_NUM_REPLACEMENT_BITS) {
+ return "";
+ }
+
+ if (numReplacementBits > (numDigits * BITS_IN_HEX_DIGIT)) {
+ return "";
+ }
+
+ // Makes assumption that 1 digit = 4 bits.
+ std::vector<uint8_t> bytes(ceil(numDigits / 2.0));
+ std::generate(bytes.begin(), bytes.end(), std::ref(ibe));
+
+ // Replace the specified number of bits from the first byte.
+ bytes.at(0) &= (0xff >> numReplacementBits);
+ replacementBits &= (0xff << (MAX_NUM_REPLACEMENT_BITS - numReplacementBits));
+ bytes.at(0) |= replacementBits;
+
+ std::ostringstream oss;
+ for (const auto& byte : bytes) {
+ oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(byte);
+ }
+
+ std::string bytesText = oss.str();
+ // Remove the last digit for odd numDigits case.
+ bytesText.resize(numDigits);
+
+ return bytesText;
+}
+
+/**
+ * Randomly generate a string of hex digits.
+ *
+ * @param ibe A random number generator.
+ * @param numDigits The number of hex digits [0-9],[a-f] to generate.
+ *
+ * @return A hex string of length @c numDigits.
+ */
+static const std::string generateHex(
+ std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>& ibe,
+ unsigned int numDigits) {
+ return generateHexWithReplacement(ibe, numDigits, 0, 0);
+}
+
+const std::string generateUUID() {
+ static bool seeded = false;
+ static std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t> ibe;
+ static std::mutex mutex;
+ std::unique_lock<std::mutex> lock(mutex);
+ if (!seeded) {
+ std::random_device rd;
+ ibe.seed(
+ rd() +
+ std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch())
+ .count());
+ seeded = true;
+ }
+
+ std::ostringstream uuidText;
+ uuidText << generateHex(ibe, 8) << SEPARATOR << generateHex(ibe, 4) << SEPARATOR
+ << generateHexWithReplacement(ibe, 4, UUID_VERSION_VALUE, 4) << SEPARATOR
+ << generateHexWithReplacement(ibe, 4, UUID_VARIANT_VALUE, 2) << SEPARATOR << generateHex(ibe, 12);
+
+ lock.unlock();
+
+ return uuidText.str();
+}
+
+} // namespace uuid
+} // namespace utilities
+} // namespace vshl
diff --git a/src/plugins/utilities/uuid/UUIDGeneration.h b/src/plugins/utilities/uuid/UUIDGeneration.h
new file mode 100644
index 0000000..7af9cb1
--- /dev/null
+++ b/src/plugins/utilities/uuid/UUIDGeneration.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+// This code is copied from https://github.com/alexa/avs-device-sdk/
+// Minor modifications are made to the original file.
+
+#ifndef VSHL_UTILITIES_UUID_UUIDGENERATION_H_
+#define VSHL_UTILITIES_UUID_UUIDGENERATION_H_
+
+#include <string>
+
+namespace vshl {
+namespace utilities {
+namespace uuid {
+
+/**
+ * Generates a variant 1, version 4 universally unique identifier (UUID) consisting of 32 hexadecimal digits.
+ * The UUID generated is of the format xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx where M indicates the version, and the two
+ * most significant bits of N indicates the variant. M is 0100 (binary) for version 4 and N is 10xx(binary) for
+ * variant 1.
+ * @see https://tools.ietf.org/html/rfc4122.
+ *
+ * @return A uuid as a string.
+ */
+const std::string generateUUID();
+
+} // namespace uuid
+} // namespace utilities
+} // namespace vshl
+
+#endif // VSHL_UTILITIES_UUID_UUIDGENERATION_H_ \ No newline at end of file
diff --git a/src/plugins/voiceagents/VoiceAgentEventNames.h b/src/plugins/voiceagents/VoiceAgentEventNames.h
new file mode 100644
index 0000000..4575528
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentEventNames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+
+static string VSHL_EVENT_AUTH_STATE_EVENT = "voice_authstate_event";
+static string VSHL_EVENT_CONNECTION_STATE_EVENT = "voice_connectionstate_event";
+static string VSHL_EVENT_DIALOG_STATE_EVENT = "voice_dialogstate_event";
+
+static list<string> VSHL_EVENTS = {
+ VSHL_EVENT_AUTH_STATE_EVENT, VSHL_EVENT_CONNECTION_STATE_EVENT,
+ VSHL_EVENT_DIALOG_STATE_EVENT,
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
diff --git a/src/plugins/voiceagents/VoiceAgentsDataManager.h b/src/plugins/voiceagents/VoiceAgentsDataManager.h
new file mode 100644
index 0000000..a4c9143
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentsDataManager.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/events/IEventFilter.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+#include "interfaces/voiceagents/IVoiceAgentsChangeObserver.h"
+#include "voiceagents/include/VoiceAgent.h"
+#include "voiceagents/include/VoiceAgentEventsHandler.h"
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * This class implements the data model for voiceagents.
+ * Supports add, remove and query operations on voiceagent data.
+ * Notifies the observers of the changes in the voiceagents data model.
+ */
+class VoiceAgentsDataManager {
+public:
+ // Create a VoiceAgentsDataManager.
+ static std::unique_ptr<VoiceAgentsDataManager> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ /**
+ * Activates the list of voiceagents.
+ *
+ * @return Number of activated agents
+ */
+ uint32_t activateVoiceAgents(const unordered_set<string>& activeVoiceAgentIds);
+
+ /**
+ * Deactivates the list of voiceagents.
+ *
+ * @return Number of de-activated agents
+ */
+ uint32_t deactivateVoiceAgents(const unordered_set<string>& inactiveVoiceAgentIds);
+
+ // Sets the default voiceagent.
+ bool setDefaultVoiceAgent(const string& voiceAgentId);
+
+ // Sets the default voiceagent.
+ std::string getDefaultVoiceAgent();
+
+ // Sets the active wakeword for the voiceagent.
+ bool setActiveWakeWord(const string& voiceAgentId, const string& wakeword);
+
+ // Adds a new voiceagent to the cache and also persists the information in a
+ // database.
+ // This call would notify all the observers about the new voiceagent addition.
+ bool addNewVoiceAgent(
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Removes the voiceagent from thecache and also from persistent database.
+ // This call would notify all the observers about the removal of the
+ // voiceagent.
+ bool removeVoiceAgent(const string& voiceAgentId);
+
+ // Returns the set of all voice agents in @c VoiceAgentsDataManger cache
+ std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> getAllVoiceAgents();
+
+ // Returns the event filter that belongs to the core module.
+ shared_ptr<vshl::common::interfaces::IEventFilter> getEventFilter() const;
+
+ // Subscribe to an event coming from the voiceagent.
+ bool subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const string voiceagentId);
+
+ // Adds a new voiceagent change observer.
+ bool addVoiceAgentsChangeObserver(shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer);
+
+ // Removes the voiceagent change observer from the list.
+ bool removeVoiceAgentsChangeObserver(shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer);
+
+ // Destructor
+ ~VoiceAgentsDataManager();
+
+private:
+ // Constructor
+ VoiceAgentsDataManager(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // A list of all the voiceagent change observers
+ unordered_set<shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver>> mVoiceAgentChangeObservers;
+
+ // A map of voiceagents grouped by ID
+ unordered_map<string, shared_ptr<VoiceAgent>> mVoiceAgents;
+
+ // Voiceagent event handler.
+ shared_ptr<VoiceAgentEventsHandler> mVoiceAgentEventsHandler;
+
+ // Default voiceagent
+ string mDefaultVoiceAgentId;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
diff --git a/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp b/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp
new file mode 100644
index 0000000..626a7fc
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "voiceagents/VoiceAgentsDataManager.h"
+
+#include "voiceagents/include/VoiceAgentEventsHandler.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgentsDataManager";
+
+/**
+ * Specifies the severity level of a log message
+ */
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace voiceagents {
+
+std::unique_ptr<VoiceAgentsDataManager> VoiceAgentsDataManager::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ return std::unique_ptr<VoiceAgentsDataManager>(new VoiceAgentsDataManager(logger, afbApi));
+}
+
+// Constructor
+VoiceAgentsDataManager::VoiceAgentsDataManager(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mLogger(logger),
+ mAfbApi(afbApi) {
+ mVoiceAgentEventsHandler = VoiceAgentEventsHandler::create(mLogger, mAfbApi);
+}
+
+// Destructor
+VoiceAgentsDataManager::~VoiceAgentsDataManager() {
+ // Clear the observers
+ mVoiceAgentChangeObservers.clear();
+ // Clear the voiceagents
+ mVoiceAgents.clear();
+}
+
+uint32_t VoiceAgentsDataManager::activateVoiceAgents(const unordered_set<string>& activeVoiceAgentIds) {
+ if (activeVoiceAgentIds.empty() || mVoiceAgents.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to activate voiceagents");
+ return 0;
+ }
+
+ uint32_t agentsActivated = 0;
+ for (auto voiceAgentId : activeVoiceAgentIds) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt != mVoiceAgents.end()) {
+ // activate the voiceagent
+ ++agentsActivated;
+ if (!voiceAgentIt->second->isActive()) {
+ voiceAgentIt->second->setIsActive(true);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentActivated(voiceAgentIt->second);
+ }
+ }
+ }
+ }
+ return agentsActivated;
+}
+
+uint32_t VoiceAgentsDataManager::deactivateVoiceAgents(const unordered_set<string>& inactiveVoiceAgentIds) {
+ if (inactiveVoiceAgentIds.empty() || mVoiceAgents.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to deactivate voiceagents");
+ return 0;
+ }
+
+ uint32_t agentsDeactivated = 0;
+ for (auto voiceAgentId : inactiveVoiceAgentIds) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt != mVoiceAgents.end()) {
+ ++agentsDeactivated;
+ if (voiceAgentIt->second->isActive()) {
+ // deactivate the voiceagent
+ voiceAgentIt->second->setIsActive(false);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentDeactivated(voiceAgentIt->second);
+ }
+ }
+ }
+ }
+
+ return agentsDeactivated;
+}
+
+bool VoiceAgentsDataManager::setDefaultVoiceAgent(const string& voiceAgentId) {
+ if (mVoiceAgents.empty() || voiceAgentId.empty()) {
+ string message = string("Failed to set default voiceagent id: ") + voiceAgentId;
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto defaultVoiceAgentIt = mVoiceAgents.find(voiceAgentId);
+
+ if (defaultVoiceAgentIt != mVoiceAgents.end()) {
+ if (mDefaultVoiceAgentId != voiceAgentId) {
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnDefaultVoiceAgentChanged(defaultVoiceAgentIt->second);
+ }
+ }
+ mDefaultVoiceAgentId = voiceAgentId;
+ } else {
+ mLogger->log(Level::ERROR, TAG, "Can't set default agent. Invalid voice agent id:" + voiceAgentId);
+ return false;
+ }
+
+ return true;
+}
+
+std::string VoiceAgentsDataManager::getDefaultVoiceAgent() {
+ return mDefaultVoiceAgentId;
+}
+
+bool VoiceAgentsDataManager::setActiveWakeWord(const string& voiceAgentId, const string& wakeword) {
+ if (mVoiceAgents.empty() || wakeword.empty()) {
+ string message =
+ string("Failed to set active wakeword: ") + wakeword + string(" for voiceagent id: ") + voiceAgentId;
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ return false;
+ }
+
+ string oldWakeWord = voiceAgentIt->second->getActiveWakeword();
+ if (oldWakeWord != wakeword) {
+ voiceAgentIt->second->setActiveWakeWord(wakeword);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentActiveWakeWordChanged(voiceAgentIt->second);
+ }
+ }
+
+ return true;
+}
+
+bool VoiceAgentsDataManager::addNewVoiceAgent(
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) {
+ shared_ptr<VoiceAgent> voiceAgent =
+ VoiceAgent::create(mLogger, id, name, description, api, vendor, activeWakeword, isActive, wakewords);
+
+ if (voiceAgent.get() == nullptr || voiceAgent->getId().empty()) {
+ string message = string("Invalid Arguments: Failed to add new voiceagent");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ if (!mVoiceAgents.empty() && mVoiceAgents.find(voiceAgent->getId()) != mVoiceAgents.end()) {
+ string message =
+ string("Failed to add new voiceagent. Voiceagent: ") + voiceAgent->getId() + string(" already exists.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ mVoiceAgents.insert(make_pair(voiceAgent->getId(), voiceAgent));
+
+ // Notify the observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentAdded(voiceAgent);
+ }
+
+ // Create all vshl events for the voiceagent.
+ mVoiceAgentEventsHandler->createVshlEventsForVoiceAgent(voiceAgent->getId());
+
+ return true;
+}
+
+bool VoiceAgentsDataManager::removeVoiceAgent(const string& voiceAgentId) {
+ if (mVoiceAgents.empty()) {
+ string message = string("Failed to remove voiceagent: ") + voiceAgentId + string(". Voiceagents data empty.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ string message = string("Failed to remove voiceagent: ") + voiceAgentId + string(". Doesn't exist.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgent = voiceAgentIt->second;
+ // Remove from the map
+ mVoiceAgents.erase(voiceAgentId);
+ // Notify the observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentRemoved(voiceAgent);
+ }
+
+ // Remove all vshl events for the voiceagent.
+ mVoiceAgentEventsHandler->removeVshlEventsForVoiceAgent(voiceAgent->getId());
+
+ return true;
+}
+
+std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> VoiceAgentsDataManager::getAllVoiceAgents() {
+ std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> voiceAgentsSet;
+ for (auto element : mVoiceAgents) {
+ voiceAgentsSet.insert(element.second);
+ }
+
+ return voiceAgentsSet;
+}
+
+// Returns the event filter that belongs to the core module.
+shared_ptr<vshl::common::interfaces::IEventFilter> VoiceAgentsDataManager::getEventFilter() const {
+ return mVoiceAgentEventsHandler;
+}
+
+bool VoiceAgentsDataManager::subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const string voiceAgentId) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ mLogger->log(
+ Level::ERROR,
+ TAG,
+ "\
+ Failed to subscribe to VSHL events from voiceagent. VoiceAgent: " +
+ voiceAgentId + " doesn't exist.");
+ return false;
+ }
+ return mVoiceAgentEventsHandler->subscribeToVshlEventFromVoiceAgent(request, eventName, voiceAgentIt->second);
+}
+
+bool VoiceAgentsDataManager::addVoiceAgentsChangeObserver(
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer) {
+ if (!observer) {
+ return false;
+ }
+
+ mVoiceAgentChangeObservers.insert(observer);
+ return true;
+}
+
+bool VoiceAgentsDataManager::removeVoiceAgentsChangeObserver(
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer) {
+ if (!observer) {
+ return false;
+ }
+
+ mVoiceAgentChangeObservers.erase(observer);
+ return true;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/include/VoiceAgent.h b/src/plugins/voiceagents/include/VoiceAgent.h
new file mode 100644
index 0000000..4dd55d4
--- /dev/null
+++ b/src/plugins/voiceagents/include/VoiceAgent.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
+
+#include <memory>
+#include <unordered_set>
+
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * Default implementation of IVoiceAgent interface.
+ */
+class VoiceAgent : public vshl::common::interfaces::IVoiceAgent {
+public:
+ // Creates @c VoiceAgent instance
+ static shared_ptr<VoiceAgent>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger, const string &id,
+ const string &name, const string &description, const string &api,
+ const string &vendor, const string &activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Destructor
+ ~VoiceAgent();
+
+ // IVoiceAgent overriden methods
+ bool setActiveWakeWord(const string &wakeword) override;
+ void setIsActive(bool active) override;
+ string getId() const override;
+ string getName() const override;
+ string getDescription() const override;
+ string getApi() const override;
+ string getVendor() const override;
+ shared_ptr<unordered_set<string>> getWakeWords() const override;
+ bool isActive() const override;
+ string getActiveWakeword() const override;
+
+private:
+ // Constructor
+ VoiceAgent(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string &id, const string &name, const string &description,
+ const string &api, const string &vendor,
+ const string &activeWakeword, const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+
+ // Id
+ string mId;
+
+ // Name
+ string mName;
+
+ // Description
+ string mDescription;
+
+ // API
+ string mApi;
+
+ // Vendor
+ string mVendor;
+
+ // Active wakeword
+ string mActiveWakeword;
+
+ // Active ??
+ bool mIsActive;
+
+ // Wakewords
+ shared_ptr<unordered_set<string>> mWakewords;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
diff --git a/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h b/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h
new file mode 100644
index 0000000..3c1ca6c
--- /dev/null
+++ b/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
+
+#include <algorithm>
+#include <memory>
+#include <unordered_map>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/events/IEventFilter.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "voiceagents/VoiceAgentEventNames.h"
+#include "voiceagents/include/VoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * This class is reponsible for handling agent specific events
+ * subscription and delivery on behalf of the high level voice service.
+ * This class also listen to the incoming events from voice agents
+ * and implements propagation to application layer.
+ */
+class VoiceAgentEventsHandler : public vshl::common::interfaces::IEventFilter {
+public:
+ // Create a VREventFilter.
+ static shared_ptr<VoiceAgentEventsHandler> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Creates all the vshl events for a specific voiceagent id.
+ // For e.g if voiceagent is VA-001 then a new vshl event
+ // voice_authstate_event#VA-001 for auth state will be created.
+ // Please see VoiceAgentEventNames.h for all the event names.
+ void createVshlEventsForVoiceAgent(const string voiceAgentId);
+
+ // Removes the events from its bookkeeping.
+ void removeVshlEventsForVoiceAgent(const string voiceAgentId);
+
+ // Subscribe to a vshl event corresponding to a voiceagent.
+ bool subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const shared_ptr<VoiceAgent> voiceAgent);
+
+ ~VoiceAgentEventsHandler();
+
+protected:
+ string getName() override;
+
+ // IEventFilter override
+ bool onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) override;
+
+private:
+ // Constructor
+ VoiceAgentEventsHandler(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Helper method to generate the event name with voiceagent Id
+ // concatenated.
+ string createEventNameWithVAId(string eventName, string voiceAgentId);
+
+ // call subscribe verb on the voiceagent. True if subscription successful.
+ // False otherwise.
+ bool callSubscribeVerb(const shared_ptr<VoiceAgent> voiceAgent);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // A map of VSHL event ID to its Event object
+ unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>> mEventsMap;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
diff --git a/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
new file mode 100644
index 0000000..4952721
--- /dev/null
+++ b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 "voiceagents/include/VoiceAgentEventsHandler.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgentEventsHandler";
+static string VA_VERB_SUBSCRIBE = "subscribe";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+using namespace vshl::common::interfaces;
+
+namespace vshl {
+namespace voiceagents {
+
+shared_ptr<VoiceAgentEventsHandler> VoiceAgentEventsHandler::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ auto eventFilter = std::shared_ptr<VoiceAgentEventsHandler>(new VoiceAgentEventsHandler(logger, afbApi));
+ return eventFilter;
+}
+
+VoiceAgentEventsHandler::VoiceAgentEventsHandler(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mAfbApi(afbApi),
+ mLogger(logger) {
+}
+
+VoiceAgentEventsHandler::~VoiceAgentEventsHandler() {
+ mEventsMap.clear();
+}
+
+string VoiceAgentEventsHandler::getName() {
+ return TAG;
+}
+
+void VoiceAgentEventsHandler::createVshlEventsForVoiceAgent(const string voiceAgentId) {
+ // Update the events map with all the VSHL Events.
+ for (auto eventName : VSHL_EVENTS) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it == mEventsMap.end() && mAfbApi) {
+ // create a new event and add it to the map.
+ shared_ptr<IAFBApi::IAFBEvent> event = mAfbApi->createEvent(eventNameWithVAId);
+ mEventsMap.insert(make_pair(eventNameWithVAId, event));
+ }
+ }
+}
+
+void VoiceAgentEventsHandler::removeVshlEventsForVoiceAgent(const string voiceAgentId) {
+ // Update the events map with all the VSHL Events.
+ for (auto eventName : VSHL_EVENTS) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it != mEventsMap.end()) {
+ mEventsMap.erase(it);
+ }
+ }
+}
+
+bool VoiceAgentEventsHandler::subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const shared_ptr<VoiceAgent> voiceAgent) {
+ auto supportedEventsIt = find(VSHL_EVENTS.begin(), VSHL_EVENTS.end(), eventName);
+ if (supportedEventsIt == VSHL_EVENTS.end()) {
+ mLogger->log(Level::ERROR, TAG, "Event: " + eventName + " not a known event.");
+ return false;
+ }
+
+ // Check if the entry for the voiceagent is present in the
+ // events map. If not then return false because the responsibility
+ // of adding to the map lies in the hands of AddVoiceAgent method.
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgent->getId());
+ auto createdEventsIt = mEventsMap.find(eventNameWithVAId);
+ if (createdEventsIt == mEventsMap.end()) {
+ mLogger->log(Level::ERROR, TAG, "Not able to subscribe. Event doesn't exist, " + eventNameWithVAId);
+ return false;
+ }
+ createdEventsIt->second->subscribe(request);
+
+ if (!callSubscribeVerb(voiceAgent)) {
+ mLogger->log(Level::WARNING, TAG, "Failed to subscribe to voiceagent: " + voiceAgent->getId());
+ }
+
+ return true;
+}
+
+// IEventFilter override.
+bool VoiceAgentEventsHandler::onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it != mEventsMap.end()) {
+ return it->second->publishEvent(json_object_new_string(payload.c_str()));
+ }
+
+ return true;
+}
+
+string VoiceAgentEventsHandler::createEventNameWithVAId(string eventName, string voiceAgentId) {
+ return eventName + "#" + voiceAgentId;
+}
+
+bool VoiceAgentEventsHandler::callSubscribeVerb(const shared_ptr<VoiceAgent> voiceAgent) {
+ if (!voiceAgent) {
+ mLogger->log(Level::ERROR, TAG, "Failed to callSubscribeVerb. Invalid input parameter.");
+ return false;
+ }
+
+ if (!mAfbApi) {
+ mLogger->log(
+ Level::ERROR, TAG, "Failed to callSubscribeVerb on voicegent: " + voiceAgent->getId() + ", No API.");
+ return false;
+ }
+
+ // TODO(Naveen): Move to utilities.
+ json_object* object = NULL;
+ std::string error, info;
+ int rc = mAfbApi->callSync(voiceAgent->getApi(), VA_VERB_SUBSCRIBE, NULL, &object, error, info);
+
+ if (object) {
+ free(object);
+ }
+
+ return true;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/src/VoiceAgentImpl.cpp b/src/plugins/voiceagents/src/VoiceAgentImpl.cpp
new file mode 100644
index 0000000..f2ef8a1
--- /dev/null
+++ b/src/plugins/voiceagents/src/VoiceAgentImpl.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <sstream>
+
+#include "voiceagents/include/VoiceAgent.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgent";
+
+/**
+ * Specifies the severity level of a log message
+ */
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace voiceagents {
+// Creates @c VoiceAgent instance
+shared_ptr<VoiceAgent> VoiceAgent::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) {
+ if (wakewords == nullptr) {
+ logger->log(Level::ERROR, TAG, "Wakeword list null");
+ return nullptr;
+ }
+
+ auto voiceAgent = std::unique_ptr<VoiceAgent>(
+ new VoiceAgent(logger, id, name, description, api, vendor, activeWakeword, isActive, wakewords));
+ if (!voiceAgent->setActiveWakeWord(activeWakeword)) {
+ return nullptr;
+ }
+
+ return voiceAgent;
+}
+
+VoiceAgent::VoiceAgent(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) :
+ mLogger(logger),
+ mId(id),
+ mName(name),
+ mDescription(description),
+ mApi(api),
+ mVendor(vendor),
+ mActiveWakeword(activeWakeword),
+ mIsActive(isActive),
+ mWakewords(wakewords) {
+}
+
+// Destructor
+VoiceAgent::~VoiceAgent() {
+}
+
+// Set the active wakeword for this voiceagent
+bool VoiceAgent::setActiveWakeWord(const string& wakeword) {
+ if (mWakewords->find(wakeword) != mWakewords->end()) {
+ mActiveWakeword = wakeword;
+ return true;
+ }
+
+ mLogger->log(Level::ERROR, TAG, "Wakeword: " + wakeword + " doesn't exist in wakeword list");
+ return false;
+}
+
+// Sets the activation state of this voiceagent
+void VoiceAgent::setIsActive(bool active) {
+ mIsActive = active;
+}
+
+string VoiceAgent::getId() const {
+ return mId;
+}
+
+string VoiceAgent::getName() const {
+ return mName;
+}
+
+string VoiceAgent::getDescription() const {
+ return mDescription;
+}
+
+string VoiceAgent::getApi() const {
+ return mApi;
+}
+
+string VoiceAgent::getVendor() const {
+ return mVendor;
+}
+
+shared_ptr<unordered_set<string>> VoiceAgent::getWakeWords() const {
+ return mWakewords;
+}
+
+bool VoiceAgent::isActive() const {
+ return mIsActive;
+}
+
+string VoiceAgent::getActiveWakeword() const {
+ return mActiveWakeword;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/test/VoiceAgentTest.cpp b/src/plugins/voiceagents/test/VoiceAgentTest.cpp
new file mode 100644
index 0000000..e5ad15e
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "voiceagents/include/VoiceAgent.h"
+
+#include "voiceagents/test/VoiceAgentsTestData.h"
+#include "test/common/ConsoleLogger.h"
+
+using namespace vshl::voiceagents;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VoiceAgentTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+
+ mVoiceAgentData = *(getVoiceAgentsTestData().begin());
+ mVoiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ mVoiceAgentData.id,
+ mVoiceAgentData.name,
+ mVoiceAgentData.description,
+ mVoiceAgentData.api,
+ mVoiceAgentData.vendor,
+ mVoiceAgentData.activeWakeword,
+ mVoiceAgentData.isActive,
+ mVoiceAgentData.wakewords);
+ }
+
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::shared_ptr<VoiceAgent> mVoiceAgent;
+ VoiceAgentTestData mVoiceAgentData;
+};
+
+TEST_F(VoiceAgentTest, InitializesCorrectly) {
+ ASSERT_NE(mVoiceAgent, nullptr);
+ ASSERT_EQ(mVoiceAgent->getId(), mVoiceAgentData.id);
+ ASSERT_EQ(mVoiceAgent->getName(), mVoiceAgentData.name);
+ ASSERT_EQ(mVoiceAgent->getDescription(), mVoiceAgentData.description);
+ ASSERT_EQ(mVoiceAgent->getApi(), mVoiceAgentData.api);
+ ASSERT_EQ(mVoiceAgent->getVendor(), mVoiceAgentData.vendor);
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), mVoiceAgentData.activeWakeword);
+ ASSERT_EQ(mVoiceAgent->isActive(), mVoiceAgentData.isActive);
+
+ std::unordered_set<std::string> wakeWords = *mVoiceAgentData.wakewords;
+ ASSERT_EQ(*(mVoiceAgent->getWakeWords()), wakeWords);
+}
+
+TEST_F(VoiceAgentTest, FailsCreationOnNonExistentWakeword) {
+ std::string nonExistentWW = "non-existent";
+ auto voiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ mVoiceAgentData.id,
+ mVoiceAgentData.name,
+ mVoiceAgentData.description,
+ mVoiceAgentData.api,
+ mVoiceAgentData.vendor,
+ nonExistentWW,
+ mVoiceAgentData.isActive,
+ mVoiceAgentData.wakewords);
+ ASSERT_EQ(voiceAgent, nullptr);
+}
+
+TEST_F(VoiceAgentTest, SetsWakewordCorrectly) {
+ std::string wakeword = *(mVoiceAgentData.wakewords->begin());
+ ASSERT_TRUE(mVoiceAgent->setActiveWakeWord(wakeword));
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), wakeword);
+}
+
+TEST_F(VoiceAgentTest, FailsToSetNonExistentWakeword) {
+ std::string nonExistentWW = "non-existent";
+ ASSERT_FALSE(mVoiceAgent->setActiveWakeWord(nonExistentWW));
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), mVoiceAgentData.activeWakeword);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp b/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp
new file mode 100644
index 0000000..58c62ed
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <gtest/gtest.h>
+
+#include "voiceagents/VoiceAgentsDataManager.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+#include "test/mocks/VoiceAgentsChangeObserverMock.h"
+#include "voiceagents/test/VoiceAgentsTestData.h"
+
+using namespace vshl::common::interfaces;
+using namespace vshl::voiceagents;
+
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VoiceAgentDataManagerTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::NiceMock<AFBApiMock>>();
+ mVADataManager = VoiceAgentsDataManager::create(mConsoleLogger, mAfbApi);
+
+ mAgentsChangeObserver = std::make_shared<
+ ::testing::StrictMock<VoiceAgentsChangeObserverMock>>();
+ mVADataManager->addVoiceAgentsChangeObserver(mAgentsChangeObserver);
+
+ mVoiceAgentsData = getVoiceAgentsTestData();
+ }
+
+ void TearDown() override {
+ mVADataManager->removeVoiceAgentsChangeObserver(mAgentsChangeObserver);
+ }
+
+ static bool addVoiceAgent(VoiceAgentsDataManager &mgr,
+ VoiceAgentTestData &data) {
+ return mgr.addNewVoiceAgent(data.id, data.name, data.description, data.api,
+ data.vendor, data.activeWakeword, data.isActive,
+ data.wakewords);
+ }
+
+ static bool isEqual(const VoiceAgentTestData &lhs, const IVoiceAgent &rhs) {
+ return lhs.id == rhs.getId() && lhs.name == rhs.getName() &&
+ lhs.description == rhs.getDescription() && lhs.api == rhs.getApi() &&
+ lhs.vendor == rhs.getVendor() &&
+ lhs.activeWakeword == rhs.getActiveWakeword() &&
+ lhs.isActive == rhs.isActive() &&
+ *lhs.wakewords == *rhs.getWakeWords();
+ }
+
+ static std::shared_ptr<IVoiceAgent>
+ findVoiceAgent(std::set<std::shared_ptr<IVoiceAgent>> &voiceAgents,
+ std::string &vaId) {
+ for (auto va : voiceAgents) {
+ if (va->getId() == vaId)
+ return va;
+ }
+
+ return nullptr;
+ }
+
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+
+ // It is a NiceMock because we don't want gtest to produce warnings about non
+ // interesting calls.
+ // The non interesting calls like createEvent is internal implementation
+ // detail for many of the
+ // tests in this class, and hence suppression of these warnings with NiceMock.
+ std::shared_ptr<::testing::NiceMock<AFBApiMock>> mAfbApi;
+
+ // It is a StrictMock because we want to fail the test for all non interesting
+ // calls.
+ std::shared_ptr<::testing::StrictMock<VoiceAgentsChangeObserverMock>>
+ mAgentsChangeObserver;
+
+ std::vector<VoiceAgentTestData> mVoiceAgentsData;
+ std::unique_ptr<VoiceAgentsDataManager> mVADataManager;
+};
+
+TEST_F(VoiceAgentDataManagerTest, InitializesCorrectly) {
+ ASSERT_NE(mVADataManager, nullptr);
+ ASSERT_EQ(mVADataManager->getAllVoiceAgents().size(), 0);
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), std::string());
+}
+
+TEST_F(VoiceAgentDataManagerTest, addingVoiceAgentWithSameIdFails) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(1);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_FALSE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+}
+
+TEST_F(VoiceAgentDataManagerTest,
+ addingVoiceAgentWithNonExistentActiveWakewordFails) {
+ auto voiceAgetData = mVoiceAgentsData[0];
+ voiceAgetData.activeWakeword = "non-existent";
+ ASSERT_FALSE(addVoiceAgent(*mVADataManager, voiceAgetData));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, canAddNewVoiceAgents) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 2);
+
+ for (auto va : allVoiceAgents) {
+ bool voiceAgentFound = false;
+ for (auto vaData : mVoiceAgentsData) {
+ if (isEqual(vaData, *va)) {
+ voiceAgentFound = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(voiceAgentFound);
+ }
+}
+
+TEST_F(VoiceAgentDataManagerTest, removingUnknonwVoiceAgentFails) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(1);
+
+ ASSERT_FALSE(mVADataManager->removeVoiceAgent("non-existent-vaid"));
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_FALSE(mVADataManager->removeVoiceAgent("non-existent-vaid"));
+}
+
+TEST_F(VoiceAgentDataManagerTest, canRemoveVoiceAgents) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentRemoved(::testing::_))
+ .Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 2);
+
+ ASSERT_TRUE(mVADataManager->removeVoiceAgent(mVoiceAgentsData[0].id));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+
+ ASSERT_TRUE(mVADataManager->removeVoiceAgent(mVoiceAgentsData[1].id));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, activatingNonExistentVoiceAgentsFails) {
+ uint32_t result = mVADataManager->activateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ result = mVADataManager->activateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, deactivatingNonExistentVoiceAgentsFails) {
+ uint32_t result = mVADataManager->deactivateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ result = mVADataManager->deactivateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, canActivateDeactivateVoiceAgents) {
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_))
+ .Times(2);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentDeactivated(::testing::_))
+ .Times(1);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentActivated(::testing::_))
+ .Times(1);
+ }
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ std::string vaId = mVoiceAgentsData[0].id;
+
+ auto allVA = mVADataManager->getAllVoiceAgents();
+ auto voiceAgent = findVoiceAgent(allVA, vaId);
+ ASSERT_NE(voiceAgent, nullptr);
+ ASSERT_TRUE(voiceAgent->isActive());
+
+ uint32_t result =
+ mVADataManager->deactivateVoiceAgents({"non-existent", vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_FALSE(voiceAgent->isActive());
+
+ // Try de-activating already de-activated agent
+ result = mVADataManager->deactivateVoiceAgents({vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_FALSE(voiceAgent->isActive());
+
+ result = mVADataManager->activateVoiceAgents({"non-existent", vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_TRUE(voiceAgent->isActive());
+
+ // Try activating already activated agent
+ result = mVADataManager->activateVoiceAgents({vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_TRUE(voiceAgent->isActive());
+}
+
+TEST_F(VoiceAgentDataManagerTest,
+ NoDefaultAgentIsReturnedWhenNoDefaultAgentIsSet) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ std::string defaultAgentId = mVADataManager->getDefaultVoiceAgent();
+ ASSERT_EQ(defaultAgentId, "");
+}
+
+TEST_F(VoiceAgentDataManagerTest, DefaultAgentCanBeSet) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ auto allAgents = mVADataManager->getAllVoiceAgents();
+ std::string vaId1 = mVoiceAgentsData[1].id;
+ std::string vaId2 = mVoiceAgentsData[0].id;
+ auto va1 = findVoiceAgent(allAgents, vaId1);
+ auto va2 = findVoiceAgent(allAgents, vaId2);
+
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnDefaultVoiceAgentChanged(va1))
+ .Times(1);
+ EXPECT_CALL(*mAgentsChangeObserver, OnDefaultVoiceAgentChanged(va2))
+ .Times(1);
+ }
+
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId1));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId1);
+
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId2));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+
+ ASSERT_FALSE(mVADataManager->setDefaultVoiceAgent("non-existent"));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+
+ // Setting default agent to already default agent shouldn't result in extra
+ // callback to OnDefaultVoiceAgentChanged
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId2));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/voiceagents/test/VoiceAgentsTestData.h b/src/plugins/voiceagents/test/VoiceAgentsTestData.h
new file mode 100644
index 0000000..ced068f
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentsTestData.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+namespace vshl {
+namespace test {
+
+typedef std::shared_ptr<std::unordered_set<std::string>> WakeWords;
+
+struct VoiceAgentTestData {
+ std::string id;
+ std::string name;
+ std::string description;
+ std::string api;
+ std::string vendor;
+ std::string activeWakeword;
+ bool isActive;
+ WakeWords wakewords;
+};
+
+static std::vector<VoiceAgentTestData> getVoiceAgentsTestData() {
+ std::vector<VoiceAgentTestData> voiceAgentsTestData{
+ {
+ "VA-001", // Id
+ "Foundation", // Name
+ "Voice Agent For Galactic Empire", // Description
+ "api-1", // API
+ "Asimov", // Vendor
+ "Hari Seldon", // Active Wakeword
+ true, // Is Active
+ std::shared_ptr<std::unordered_set<std::string>>(
+ new std::unordered_set<std::string>{"Hari Seldon", "Cleon I ", "Eto Demerzel"}) // Wake Words
+ },
+ {
+ "VA-002", // Id
+ "Betelgeuse", // Name
+ "Voice Agent For Galaxy hopper", // Description
+ "api-2", // API
+ "Douglas Adams", // Vendor
+ "Ford Prefect", // Active Wakeword
+ true, // Is Active
+ std::shared_ptr<std::unordered_set<std::string>>(
+ new std::unordered_set<std::string>{"Ford Prefect", "Zaphod Beeblebrox"}) // Wake Words
+ },
+ };
+
+ return voiceAgentsTestData;
+}
+
+} // namespace test
+} // namespace vshl
diff --git a/src/vshl-apidef.h b/src/vshl-apidef.h
new file mode 100644
index 0000000..7a14498
--- /dev/null
+++ b/src/vshl-apidef.h
@@ -0,0 +1,43 @@
+
+static const char _afb_description_vshl[] =
+ "{\"openapi\":\"3.0.0\",\"$schema\":\"http://iot.bzh/download/openapi/sch"
+ "ema-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\""
+ "High Level Voice Service API\",\"version\":\"1.0\",\"x-binding-c-generat"
+ "or\":{\"api\":\"vshl\",\"version\":3,\"prefix\":\"afv_\",\"postfix\":\"\""
+ ",\"start\":null,\"onevent\":null,\"init\":\"init\",\"scope\":\"\",\"priv"
+ "ate\":false,\"noconcurrency\":true}},\"servers\":[{\"url\":\"ws://{host}"
+ ":{port}/api/monitor\",\"description\":\"TS caching binding\",\"variables"
+ "\":{\"host\":{\"default\":\"localhost\"},\"port\":{\"default\":\"1234\"}"
+ "},\"x-afb-events\":[{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"c"
+ "omponents\":{\"schemas\":{\"afb-reply\":{\"$ref\":\"#/components/schemas"
+ "/afb-reply-v3\"},\"afb-event\":{\"$ref\":\"#/components/schemas/afb-even"
+ "t-v3\"},\"afb-reply-v3\":{\"title\":\"Generic response.\",\"type\":\"obj"
+ "ect\",\"required\":[\"jtype\",\"request\"],\"properties\":{\"jtype\":{\""
+ "type\":\"string\",\"const\":\"afb-reply\"},\"request\":{\"type\":\"objec"
+ "t\",\"required\":[\"status\"],\"properties\":{\"status\":{\"type\":\"str"
+ "ing\"},\"info\":{\"type\":\"string\"},\"token\":{\"type\":\"string\"},\""
+ "uuid\":{\"type\":\"string\"},\"reqid\":{\"type\":\"string\"}}},\"respons"
+ "e\":{\"type\":\"object\"}}},\"afb-event-v3\":{\"type\":\"object\",\"requ"
+ "ired\":[\"jtype\",\"event\"],\"properties\":{\"jtype\":{\"type\":\"strin"
+ "g\",\"const\":\"afb-event\"},\"event\":{\"type\":\"string\"},\"data\":{\""
+ "type\":\"object\"}}}},\"responses\":{\"200\":{\"description\":\"A comple"
+ "x object array response\",\"content\":{\"application/json\":{\"schema\":"
+ "{\"$ref\":\"#/components/schemas/afb-reply\"}}}}}}}";
+
+static const struct afb_verb_v3 _afb_verbs_vshl[] = {
+ {.verb = NULL, .callback = NULL, .auth = NULL, .info = NULL, .vcbdata = NULL, .session = 0, .glob = 0}};
+
+int init(afb_api_t api);
+
+const struct afb_binding_v3 afbBindingV3 = {.api = "vshl",
+ .specification = _afb_description_vshl,
+ .info = "",
+ .verbs = _afb_verbs_vshl,
+ .preinit = NULL,
+ .init = init,
+ .onevent = NULL,
+ .userdata = NULL,
+ .provide_class = NULL,
+ .require_class = NULL,
+ .require_api = NULL,
+ .noconcurrency = 1};
diff --git a/src/vshl-apidef.json b/src/vshl-apidef.json
new file mode 100644
index 0000000..67bda88
--- /dev/null
+++ b/src/vshl-apidef.json
@@ -0,0 +1,109 @@
+{
+ "openapi": "3.0.0",
+ "$schema": "http://iot.bzh/download/openapi/schema-3.0/default-schema.json",
+ "info": {
+ "description": "",
+ "title": "High Level Voice Service API",
+ "version": "1.0",
+ "x-binding-c-generator": {
+ "api": "vshl",
+ "version": 3,
+ "prefix": "afv_",
+ "postfix": "",
+ "start": null,
+ "onevent": null,
+ "init": "init",
+ "scope": "",
+ "private": false,
+ "noconcurrency": true
+ }
+ },
+ "servers": [{
+ "url": "ws://{host}:{port}/api/monitor",
+ "description": "TS caching binding",
+ "variables": {
+ "host": {
+ "default": "localhost"
+ },
+ "port": {
+ "default": "1234"
+ }
+ },
+ "x-afb-events": [{
+ "$ref": "#/components/schemas/afb-event"
+ }]
+ }],
+ "components": {
+ "schemas": {
+ "afb-reply": {
+ "$ref": "#/components/schemas/afb-reply-v3"
+ },
+ "afb-event": {
+ "$ref": "#/components/schemas/afb-event-v3"
+ },
+ "afb-reply-v3": {
+ "title": "Generic response.",
+ "type": "object",
+ "required": ["jtype", "request"],
+ "properties": {
+ "jtype": {
+ "type": "string",
+ "const": "afb-reply"
+ },
+ "request": {
+ "type": "object",
+ "required": ["status"],
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "info": {
+ "type": "string"
+ },
+ "token": {
+ "type": "string"
+ },
+ "uuid": {
+ "type": "string"
+ },
+ "reqid": {
+ "type": "string"
+ }
+ }
+ },
+ "response": {
+ "type": "object"
+ }
+ }
+ },
+ "afb-event-v3": {
+ "type": "object",
+ "required": ["jtype", "event"],
+ "properties": {
+ "jtype": {
+ "type": "string",
+ "const": "afb-event"
+ },
+ "event": {
+ "type": "string"
+ },
+ "data": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "A complex object array response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/afb-reply"
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/vshl-binding.c b/src/vshl-binding.c
new file mode 100644
index 0000000..17afbad
--- /dev/null
+++ b/src/vshl-binding.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+#define _GNU_SOURCE
+#include "vshl-binding.h"
+
+afb_dynapi* AFB_default;
+
+// Config Section definition (note: controls section index should match handle
+// retrieval in HalConfigExec)
+static CtlSectionT ctrlSections[] = {{.key = "plugins", .loadCB = PluginConfig},
+ {.key = "onload", .loadCB = OnloadConfig},
+ {.key = "controls", .loadCB = ControlConfig},
+ {.key = "events", .loadCB = EventConfig},
+ {.key = NULL}};
+
+static AFB_ApiVerbs ctrlApiVerbs[] = {
+ {.verb = NULL} /* marker for end of the array */
+};
+
+static int ctrlLoadStaticVerbs(afb_dynapi* apiHandle, AFB_ApiVerbs* verbs) {
+ int errcount = 0;
+
+ for (int idx = 0; verbs[idx].verb; idx++) {
+ errcount += afb_dynapi_add_verb(
+ apiHandle,
+ ctrlApiVerbs[idx].verb,
+ NULL,
+ ctrlApiVerbs[idx].callback,
+ (void*)&ctrlApiVerbs[idx],
+ ctrlApiVerbs[idx].auth,
+ 0);
+ }
+
+ return errcount;
+};
+
+// next generation dynamic API-V3 mode
+#include <signal.h>
+
+static int ctrlLoadOneApi(void* cbdata, AFB_ApiT apiHandle) {
+ CtlConfigT* ctrlConfig = (CtlConfigT*)cbdata;
+
+ // save closure as api's data context
+ afb_dynapi_set_userdata(apiHandle, ctrlConfig);
+
+ // add static controls verbs
+ int err = ctrlLoadStaticVerbs(apiHandle, ctrlApiVerbs);
+ if (err) {
+ AFB_ApiError(apiHandle, "ctrlLoadStaticVerbs fail to register static V2 verbs");
+ return ERROR;
+ }
+
+ // load section for corresponding API
+ err = CtlLoadSections(apiHandle, ctrlConfig, ctrlSections);
+ if (err) {
+ AFB_ApiError(apiHandle, "CtlLoadSections fail to load the sections");
+ return ERROR;
+ }
+
+ // declare an event event manager for this API;
+ afb_dynapi_on_event(apiHandle, CtrlDispatchApiEvent);
+
+ // init API function (does not receive user closure ???
+ // afb_dynapi_on_init(apiHandle, CtrlInitOneApi);
+
+ afb_dynapi_seal(apiHandle);
+ return err;
+}
+
+int afbBindingEntry(afb_dynapi* apiHandle) {
+ AFB_default = apiHandle;
+ AFB_ApiNotice(apiHandle, "Controller in afbBindingEntry");
+
+ const char* dirList = getenv("CONTROL_CONFIG_PATH");
+ if (!dirList) dirList = CONTROL_CONFIG_PATH;
+
+ const char* configPath = CtlConfigSearch(apiHandle, dirList, "");
+ if (!configPath) {
+ AFB_ApiError(apiHandle, "CtlPreInit: No %s* config found in %s ", GetBinderName(), dirList);
+ return ERROR;
+ }
+
+ // load config file and create API
+ CtlConfigT* ctrlConfig = CtlLoadMetaData(apiHandle, configPath);
+ if (!ctrlConfig) {
+ AFB_ApiError(apiHandle, "CtrlBindingDyn No valid control config file in:\n-- %s", configPath);
+ return ERROR;
+ }
+
+ if (!ctrlConfig->api) {
+ AFB_ApiError(apiHandle, "CtrlBindingDyn API Missing from metadata in:\n-- %s", configPath);
+ return ERROR;
+ }
+
+ AFB_ApiNotice(apiHandle, "Controller API='%s' info='%s'", ctrlConfig->api, ctrlConfig->info);
+
+ // create one API per config file (Pre-V3 return code ToBeChanged)
+ int status = afb_dynapi_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, ctrlLoadOneApi, ctrlConfig);
+
+ // config exec should be done after api init in order to enable onload to use newly defined ctl API.
+ if (!status) status = CtlConfigExec(apiHandle, ctrlConfig);
+
+ return status;
+}
diff --git a/src/vshl-binding.h b/src/vshl-binding.h
new file mode 100644
index 0000000..75e7c91
--- /dev/null
+++ b/src/vshl-binding.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 _CTL_BINDING_INCLUDE_
+#define _CTL_BINDING_INCLUDE_
+
+#define AFB_BINDING_VERSION 3
+#include <ctl-config.h>
+
+#ifndef ERROR
+#define ERROR -1
+#endif
+
+#endif /* _CTL_BINDING_INCLUDE_ */ \ No newline at end of file