summaryrefslogtreecommitdiffstats
path: root/src/plugins/capabilities
diff options
context:
space:
mode:
authorNaveen Bobbili <nbobbili@amazon.com>2019-02-25 21:00:09 -0800
committerNaveen Bobbili <nbobbili@amazon.com>2019-02-25 21:06:18 -0800
commit533f49cc00b0846c4f2ebd763b86b917f5023cdc (patch)
tree0950a62bc5c75c24712fefb8532ff82a23f1b947 /src/plugins/capabilities
parent800ec166dd48283fd7f3035685e6b6a73091552d (diff)
vshl-capabilities:
This API is responsible for brokering capbilities related messages from voiceagents to apps and vice versa. Verbs exposed are navigation/publish navigation/subscribe phonecontrol/publish phonecontrol/subscribe playbackcontroller/publish playbackcontroller/subscribe guiMetadata/publish guiMetadata/subscribe This API exposes publish and subscribe methods for all the speech framework domains/capabilities. For eg. navigation, phonecontrol etc. This API is used by apps and low level voice agent binding to subscribe and publish these capability messages whenever applicable. This specific commit is for vshl-capabilities API. Change-Id: I822c2e8589e39574d707a7c199bea91a686dced7 Signed-off-by: Naveen Bobbili <nbobbili@amazon.com>
Diffstat (limited to 'src/plugins/capabilities')
-rw-r--r--src/plugins/capabilities/CapabilitiesFactory.cpp71
-rw-r--r--src/plugins/capabilities/CapabilitiesFactory.h69
-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.h57
-rw-r--r--src/plugins/capabilities/communication/include/PhoneControlMessages.h208
-rw-r--r--src/plugins/capabilities/communication/src/PhoneControlCapability.cpp52
-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.h57
-rw-r--r--src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h59
-rw-r--r--src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp52
-rw-r--r--src/plugins/capabilities/navigation/include/NavigationCapability.h57
-rw-r--r--src/plugins/capabilities/navigation/include/NavigationMessages.h56
-rw-r--r--src/plugins/capabilities/navigation/src/NavigationCapability.cpp52
-rw-r--r--src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h57
-rw-r--r--src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h64
-rw-r--r--src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp52
-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
25 files changed, 2120 insertions, 0 deletions
diff --git a/src/plugins/capabilities/CapabilitiesFactory.cpp b/src/plugins/capabilities/CapabilitiesFactory.cpp
new file mode 100644
index 0000000..7a65842
--- /dev/null
+++ b/src/plugins/capabilities/CapabilitiesFactory.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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"
+#include "capabilities/playbackcontroller/include/PlaybackControllerCapability.h"
+
+static string TAG = "vshlcapabilities::core::CapabilitiesFactory";
+
+namespace vshlcapabilities {
+namespace capabilities {
+
+// Create CapabilitiesFactory
+std::unique_ptr<CapabilitiesFactory> CapabilitiesFactory::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ auto capabilitiesFactory = std::unique_ptr<CapabilitiesFactory>(new CapabilitiesFactory(logger));
+ return capabilitiesFactory;
+}
+
+CapabilitiesFactory::CapabilitiesFactory(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ mLogger = logger;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getGuiMetadata() {
+ if (!mGuiMetadata) {
+ mGuiMetadata = vshlcapabilities::capabilities::guimetadata::GuiMetadata::create(mLogger);
+ }
+ return mGuiMetadata;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getPhoneControl() {
+ if (!mPhoneControl) {
+ mPhoneControl = vshlcapabilities::capabilities::phonecontrol::PhoneControl::create(mLogger);
+ }
+ return mPhoneControl;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getNavigation(
+ ) {
+ if (!mNavigation) {
+ mNavigation = vshlcapabilities::capabilities::navigation::Navigation::create(mLogger);
+ }
+ return mNavigation;
+}
+
+std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getPlaybackController(
+ ) {
+ if (!mPlaybackController) {
+ mPlaybackController = vshlcapabilities::capabilities::playbackcontroller::PlaybackController::create(mLogger);
+ }
+ return mPlaybackController;
+}
+
+} // 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..b97fcfe
--- /dev/null
+++ b/src/plugins/capabilities/CapabilitiesFactory.h
@@ -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.
+ */
+#ifndef VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_
+#define VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+using namespace std;
+
+namespace vshlcapabilities {
+namespace capabilities {
+/*
+ * Factory for creating different capability objects.
+ */
+class CapabilitiesFactory {
+public:
+ // Create CapabilitiesFactory
+ static std::unique_ptr<CapabilitiesFactory> create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ // 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();
+
+ // PLaybackcontroller capability
+ std::shared_ptr<common::interfaces::ICapability> getPlaybackController();
+
+ // Destructor
+ ~CapabilitiesFactory() = default;
+
+private:
+ // Constructor
+ CapabilitiesFactory(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ // Capabilities
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> mGuiMetadata;
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> mPhoneControl;
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> mNavigation;
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> mPlaybackController;
+
+ // Logger
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> 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..8dd6e26
--- /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 = "vshlcapabilities::capabilities::CapabilityMessagingService";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+
+// Create a CapabilityMessagingService.
+unique_ptr<CapabilityMessagingService> CapabilityMessagingService::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi) :
+ mAfbApi(afbApi),
+ mLogger(logger) {
+}
+
+// Subscribe to capability specific messages.
+bool CapabilityMessagingService::subscribe(
+ vshlcapabilities::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,
+ json_object* 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,
+ "No one subscribed for a message of capability. " + capabilityName);
+ return false;
+ }
+
+ return messageChannelIt->second->publish(action, payload);
+}
+
+shared_ptr<vshlcapabilities::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 = vshlcapabilities::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..79edfa8
--- /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 vshlcapabilities {
+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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi);
+
+ // Subscribe to capability specific messages.
+ bool subscribe(vshlcapabilities::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, json_object* payload);
+
+ // Destructor
+ ~CapabilityMessagingService();
+
+private:
+ // Constructor
+ CapabilityMessagingService(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi);
+
+ // Binding API reference
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> mAfbApi;
+
+ // Create a message channel for the capability.
+ shared_ptr<vshlcapabilities::capabilities::core::MessageChannel>
+ getMessageChannel(shared_ptr<common::interfaces::ICapability> capability);
+
+ // Map of capabilities to message channels.
+ unordered_map<string, shared_ptr<vshlcapabilities::capabilities::core::MessageChannel>>
+ mMessageChannelsMap;
+
+ // Logger
+ shared_ptr<vshlcapabilities::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..87dc339
--- /dev/null
+++ b/src/plugins/capabilities/communication/include/PhoneControlCapability.h
@@ -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.
+ */
+
+#ifndef VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_
+#define VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+namespace vshlcapabilities {
+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(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ ~PhoneControl() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ PhoneControl(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger;
+};
+
+} // 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..23d2fae
--- /dev/null
+++ b/src/plugins/capabilities/communication/include/PhoneControlMessages.h
@@ -0,0 +1,208 @@
+/*
+ * 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 vshlcapabilities {
+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";
+/**
+ * Notifies the platform implementation to redial the last called phone number.
+ *
+ * After returning @c true, if no stored number is available to be redialed, @c PhoneCallController::callFailed with
+ * @c CallError::NO_NUMBER_FOR_REDIAL should be called.
+ *
+ * @param [in] payload Details of the redial request in structured JSON format. See the following
+ * payload:
+ * {
+ * "callId": "{{STRING}}"
+ * }
+ * @li callId (required): A unique identifier for the call
+ *
+ * @return @c true if the platform implementation successfully handled the call
+ */
+static string PHONECONTROL_REDIAL = "redial";
+/**
+ * Notifies the platform implementation to answer an inbound call
+ *
+ * @param [in] payload Details of the answer request in structured JSON format. See the following
+ * payload:
+ * {
+ * "callId": "{{STRING}}",
+ * }
+ * @li callId (required): The unique identifier for the call to answer
+ */
+static string PHONECONTROL_ANSWER = "answer";
+/**
+ * Notifies the platform implementation to end an ongoing call or stop inbound or outbound call setup
+ *
+ * @param [in] payload Details of the stop request in structured JSON format. See the following
+ * payload:
+ * {
+ * "callId": "{{STRING}}"
+ * }
+ * @li callId (required): The unique identifier for the call to be stopped
+ */
+static string PHONECONTROL_STOP = "stop";
+/**
+ * Notifies the platform implementation to send a DTMF signal to the calling device
+ *
+ * @param [in] payload Details of the DTMF request in structured JSON format. See the following
+ * payload:
+ * {
+ * "callId": "{{STRING}}",
+ * "signal": "{{STRING}}"
+ * }
+ * @li callId (required): The unique identifier for the call
+ * @li signal (required): The DTMF string to be sent to the calling device associated with the callId
+ */
+static string PHONECONTROL_SEND_DTMF = "send_dtmf";
+
+/*
+ ******************************************************************************************************
+ * 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}}"
+ * "state" : "{{STRING}}" // IDLE, ACTIVE, TRYING, INBOUND_RINGING, OUTBOUND_RINGING, INVITED
+ * }
+ */
+static string PHONECONTROL_CALL_STATE_CHANGED = "call_state_changed";
+/*
+ * 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 a caller id was received for an inbound call
+ * payload :
+ * {
+ * "callId" : "{{STRING}}"
+ * "callerId" : "{{STRING}}"
+ * }
+ *
+ * @param [in] callId The unique identifier for the call associated with the callId
+ * @param [in] callerId The caller's identifier or phone number
+ */
+static string PHONECONTROL_CALLERID_RECEIVED = "caller_id_received";
+
+/**
+ * Notifies the Engine that sending the DTMF signal succeeded.
+ * payload :
+ * {
+ * "callId" : "{{STRING}}"
+ * }
+ * @param [in] callId The unique identifier for the associated call
+ *
+ * PhoneCallController::sendDTMF
+ */
+static string PHONECONTROL_SEND_DTMF_SUCCEEDED = "send_dtmf_succeeded";
+
+// List of actions that are delivered from VA -> Apps
+static list<string> PHONECONTROL_UPSTREAM_ACTIONS = {
+ PHONECONTROL_DIAL,
+ PHONECONTROL_REDIAL,
+ PHONECONTROL_ANSWER,
+ PHONECONTROL_STOP,
+ PHONECONTROL_SEND_DTMF
+};
+
+// List of actions that are delivered from Apps -> VA
+static list<string> PHONECONTROL_DOWNSTREAM_ACTIONS = {PHONECONTROL_CONNECTIONSTATE_CHANGED,
+ PHONECONTROL_CALL_STATE_CHANGED,
+ PHONECONTROL_CALL_FAILED,
+ PHONECONTROL_CALLERID_RECEIVED,
+ PHONECONTROL_SEND_DTMF_SUCCEEDED};
+
+} // 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..75296bf
--- /dev/null
+++ b/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp
@@ -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.
+ */
+#include "capabilities/communication/include/PhoneControlCapability.h"
+#include "capabilities/communication/include/PhoneControlMessages.h"
+
+const string TAG = "vshlcapabilities::capabilities::phonecontrol";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace phonecontrol {
+
+// Create a phonecontrol.
+shared_ptr<PhoneControl> PhoneControl::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ auto phonecontrol = std::shared_ptr<PhoneControl>(new PhoneControl(logger));
+ return phonecontrol;
+}
+
+PhoneControl::PhoneControl(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ mLogger = logger;
+}
+
+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..79e4eab
--- /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 vshlcapabilities {
+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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability);
+
+ // Sends the message
+ bool publish(const string action, json_object* payload);
+
+ // Subscribe
+ bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request,
+ const string action);
+
+ // Destructor
+ virtual ~MessageChannel() = default;
+
+private:
+ // Constructor
+ MessageChannel(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::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..01e9523
--- /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 vshlcapabilities {
+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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::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, json_object* payload);
+
+ // Destructor
+ ~PublisherForwarder();
+
+private:
+ // Constructor
+ PublisherForwarder(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability);
+
+ // Subscriber forwarder connected to this publisher forwarder.
+ shared_ptr<SubscriberForwarder> mSubscriberForwarder;
+
+ // Capability
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> mCapability;
+
+ // Logger
+ shared_ptr<vshlcapabilities::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..ac469b3
--- /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 vshlcapabilities {
+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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability);
+
+ // Publish a capability message to the actual client.
+ bool forwardMessage(const string action, json_object* payload);
+
+ // Subscribe
+ bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request,
+ const string action);
+
+ // Destructor
+ ~SubscriberForwarder();
+
+private:
+ // Constructor
+ SubscriberForwarder(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability);
+
+ // Creates both upstream and downstream events
+ void createEvents();
+
+ // Binding API reference
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> mAfbApi;
+
+ // Capability
+ shared_ptr<vshlcapabilities::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<vshlcapabilities::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..f4e55dd
--- /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 vshlcapabilities {
+namespace capabilities {
+namespace core {
+
+// Create a MessageChannel.
+std::shared_ptr<MessageChannel> MessageChannel::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> api,
+ shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) {
+ auto messageChannel = std::shared_ptr<MessageChannel>(new MessageChannel(logger, api, capability));
+ return messageChannel;
+}
+
+MessageChannel::MessageChannel(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> api,
+ shared_ptr<vshlcapabilities::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, json_object* payload) {
+ return mPublisherForwarder->forwardMessage(action, payload);
+}
+
+bool MessageChannel::subscribe(vshlcapabilities::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..8f8f542
--- /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 = "vshlcapabilities::capabilities::PublisherForwarder";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace core {
+
+// Create a PublisherForwarder.
+std::shared_ptr<PublisherForwarder> PublisherForwarder::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::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, json_object* 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..970dbf0
--- /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 = "vshlcapabilities::capabilities::SubscriberForwarder";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace core {
+
+// Create a SubscriberForwarder.
+std::shared_ptr<SubscriberForwarder> SubscriberForwarder::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::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<vshlcapabilities::common::interfaces::ILogger> logger,
+ shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi,
+ shared_ptr<vshlcapabilities::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, json_object* payload) {
+ auto upstreamEventIt = mUpstreamEventsMap.find(action);
+ if (upstreamEventIt != mUpstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Publishing upstream event: " + action);
+ upstreamEventIt->second->publishEvent(payload);
+ return true;
+ }
+
+ auto downstreamEventIt = mDownstreamEventsMap.find(action);
+ if (downstreamEventIt != mDownstreamEventsMap.end()) {
+ mLogger->log(Level::NOTICE, TAG, "Publishing downstream event: " + action);
+ downstreamEventIt->second->publishEvent(payload);
+ return true;
+ }
+
+ mLogger->log(Level::NOTICE, TAG, "Failed to publish upstream event: " + action);
+ return false;
+}
+
+bool SubscriberForwarder::subscribe(vshlcapabilities::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..ecaf09f
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h
@@ -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.
+ */
+
+#ifndef VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_
+#define VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace guimetadata {
+
+/*
+ * GuiMetadata capability
+ */
+class GuiMetadata : public common::interfaces::ICapability {
+public:
+ // Create a GuiMetadata.
+ static std::shared_ptr<GuiMetadata> create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ ~GuiMetadata() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ GuiMetadata(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger;
+};
+
+} // 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..07f4725
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h
@@ -0,0 +1,59 @@
+/*
+ * 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 vshlcapabilities {
+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";
+
+
+// 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};
+
+/*
+ ******************************************************************************************************
+ * Supported actions from APPS -> VA
+ ******************************************************************************************************
+ */
+
+// 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..df90c91
--- /dev/null
+++ b/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp
@@ -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.
+ */
+#include "capabilities/guimetadata/include/GuiMetadataCapability.h"
+#include "capabilities/guimetadata/include/GuiMetadataMessages.h"
+
+const string TAG = "vshlcapabilities::capabilities::guimetadata";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace guimetadata {
+
+// Create a GuiMetadata.
+shared_ptr<GuiMetadata> GuiMetadata::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ auto guiMetadata = std::shared_ptr<GuiMetadata>(new GuiMetadata(logger));
+ return guiMetadata;
+}
+
+GuiMetadata::GuiMetadata(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ mLogger = logger;
+}
+
+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..bb055a8
--- /dev/null
+++ b/src/plugins/capabilities/navigation/include/NavigationCapability.h
@@ -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.
+ */
+
+#ifndef VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_
+#define VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace navigation {
+
+/*
+ * Navigation capability
+ */
+class Navigation : public common::interfaces::ICapability {
+public:
+ // Create a Navigation.
+ static std::shared_ptr<Navigation> create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ ~Navigation() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ Navigation(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger;
+};
+
+} // 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..00c0c65
--- /dev/null
+++ b/src/plugins/capabilities/navigation/include/NavigationMessages.h
@@ -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.
+ */
+#ifndef VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_
+#define VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshlcapabilities {
+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,
+};
+
+/*
+ ******************************************************************************************************
+ * Supported actions 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..e1c6cda
--- /dev/null
+++ b/src/plugins/capabilities/navigation/src/NavigationCapability.cpp
@@ -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.
+ */
+#include "capabilities/navigation/include/NavigationCapability.h"
+#include "capabilities/navigation/include/NavigationMessages.h"
+
+const string TAG = "vshlcapabilities::capabilities::navigation";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace navigation {
+
+// Create a Navigation.
+shared_ptr<Navigation> Navigation::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ auto navigation = std::shared_ptr<Navigation>(new Navigation(logger));
+ return navigation;
+}
+
+Navigation::Navigation(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ mLogger = logger;
+}
+
+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/playbackcontroller/include/PlaybackControllerCapability.h b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h
new file mode 100644
index 0000000..e80b213
--- /dev/null
+++ b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h
@@ -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.
+ */
+
+#ifndef VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_
+#define VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_
+
+#include <memory>
+
+#include "interfaces/capabilities/ICapability.h"
+#include "interfaces/utilities/logging/ILogger.h"
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace playbackcontroller {
+
+/*
+ * PlaybackController capability. Calls are initiated in the endpoint.
+ */
+class PlaybackController : public common::interfaces::ICapability {
+public:
+ // Create a PlaybackController.
+ static std::shared_ptr<PlaybackController> create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ ~PlaybackController() = default;
+
+protected:
+ string getName() const override;
+
+ list<string> getUpstreamMessages() const override;
+
+ list<string> getDownstreamMessages() const override;
+
+private:
+ PlaybackController(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger);
+
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace playbackcontroller
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_
diff --git a/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h
new file mode 100644
index 0000000..224b8dd
--- /dev/null
+++ b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h
@@ -0,0 +1,64 @@
+/*
+ * 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_PLAYBACKCONTROLLER_MESSAGES_H_
+#define VSHL_CAPABILITIES_PLAYBACKCONTROLLER_MESSAGES_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace playbackcontroller {
+
+static string NAME = "playbackcontroller";
+
+/*
+ ******************************************************************************************************
+ * Supported actions from APPS -> VA
+ ******************************************************************************************************
+ */
+// List of actions that are delivered from VA -> Apps
+static list<string> PLAYBACKCONTROLLER_UPSTREAM_ACTIONS = {
+
+};
+
+
+/*
+ ******************************************************************************************************
+ * Supported actions from VA -> APPS
+ ******************************************************************************************************
+ */
+// List of actions that are delivered from Apps -> VA
+/*
+ * payload :
+ * {
+ * "button" : "{{STRING}}" // play, pause, next, previous, skip-forward, skip-backward
+ * }
+ * @param [in] button Button pressed by the user.
+ *
+ */
+static string PLAYBACKCONTROLLER_BUTTONPRESSED = "button_pressed";
+
+static list<string> PLAYBACKCONTROLLER_DOWNSTREAM_ACTIONS = {
+ PLAYBACKCONTROLLER_BUTTONPRESSED
+};
+
+} // namespace playbackcontroller
+} // namespace capabilities
+} // namespace vshl
+
+#endif // VSHL_CAPABILITIES_PLAYBACKCONTROLLER_MESSAGES_H_
diff --git a/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp b/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp
new file mode 100644
index 0000000..a5abbed
--- /dev/null
+++ b/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp
@@ -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.
+ */
+#include "capabilities/playbackcontroller/include/PlaybackControllerCapability.h"
+#include "capabilities/playbackcontroller/include/PlaybackControllerMessages.h"
+
+const string TAG = "vshlcapabilities::capabilities::playbackcontroller";
+
+using Level = vshlcapabilities::common::interfaces::ILogger::Level;
+
+namespace vshlcapabilities {
+namespace capabilities {
+namespace playbackcontroller {
+
+// Create a playbackcontroller.
+shared_ptr<PlaybackController> PlaybackController::create(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ auto playbackcontroller = std::shared_ptr<PlaybackController>(new PlaybackController(logger));
+ return playbackcontroller;
+}
+
+PlaybackController::PlaybackController(
+ shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) {
+ mLogger = logger;
+}
+
+string PlaybackController::getName() const {
+ return NAME;
+}
+
+list<string> PlaybackController::getUpstreamMessages() const {
+ return PLAYBACKCONTROLLER_UPSTREAM_ACTIONS;
+}
+
+list<string> PlaybackController::getDownstreamMessages() const {
+ return PLAYBACKCONTROLLER_DOWNSTREAM_ACTIONS;
+}
+
+} // namespace playbackcontroller
+} // 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..5d3971d
--- /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 vshlcapabilities::common::interfaces;
+using namespace vshlcapabilities::capabilities;
+using namespace vshlcapabilities::test::common;
+
+namespace vshlcapabilities {
+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..8fb499c
--- /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 vshlcapabilities::common::interfaces;
+using namespace vshlcapabilities::capabilities::core;
+using namespace vshlcapabilities::test::common;
+
+namespace vshlcapabilities {
+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..c3d72de
--- /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 vshlcapabilities::common::interfaces;
+using namespace vshlcapabilities::capabilities::core;
+using namespace vshlcapabilities::test::common;
+
+namespace vshlcapabilities {
+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