Version 1.18.0 alexa-client-sdk

Changes in this update:

**Enhancements**

* Added support for [Bluetooth Interface 2.0](https://developer.amazon.com/docs/alexa/alexa-voice-service/bluetooth.html). This interface adds support for multiple simultaneous connections to Bluetooth peripherals.
* Added support for [Audio Focus Manager Library (AFML) Multi Activity](https://developer.amazon.com/docs/alexa/avs-device-sdk/sdk-interaction-model.html). This interface enhances the behavior of a device so it can handle more than one Activity per Channel.
* Added the `obfuscatePrivateData` logging method to help remove certain data from logs.
* Updated `MediaPlayerObserverInterface` to include metadata about playback states.
* Added SDK extension point. You can integrate CMake projects into the SDK without cloning those projects into a subdirectory.

**Bug fixes**

* Fixed Mac/OSX issue that caused an unresponsive Sample App when not connected to the internet.
* Fixed issue that prevented sample app from exiting various states.
* Fixed `UIManager` issue that caused an error in the logs when the device with built without the wake word enabled.
* Fixed volume issue that caused timers to ascend in volume when setting up ascending alarms.
* Fixed alert volume issue that caused any changes to the alert volume to notify observers.
* Fixed EQ issue where changes to the EQ band levels didn't notify observers.
* Fixed Bluetooth bug that caused short notification sounds from a connected phone to stop audio playback on the device.

**Known Issues**

* Build errors can occur on the Raspberry Pi due to incorrect linking of the atomic library. A suggested workaround is to add the following `set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic")` to the top most CMake file.
* The WebVTT dependency required for `captions` isn't supported for Windows/Android.
* Exiting from the setting option takes you back to the Options Menu directly. It doesn't provide a message to indicate that you're back in the main menu.
* Failing Unit Tests and AIP Unit tests are disabled on Windows
* `AudioInputProcessor` unit tests don't build on Windows when with the `-DCMAKE_BUILD_TYPE=DEBUG` cmake parameter.
* Music playback history isn't displayed in the Alexa app for certain account and device types.
* When using Gnu Compiler Collection 8+ (GCC 8+), `-Wclass-memaccess` triggers warnings. You can ignore these, they don't cause the build to fail.
* Android error `libDefaultClient.so not found` might occur. Resolve this by upgrading to ADB version 1.0.40.
* If a device loses a network connection, the lost connection status isn't returned though local TTS.
* ACL encounters issues if it receives audio attachments but doesn't consume them.
* Media streamed through Bluetooth might abruptly stop. To restart playback, resume the media in the source application or toggle next/previous.
* If a connected Bluetooth device is inactive, the Alexa app might indicates that audio is playing.
* The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation isn't yet supported.
* When using some products, interrupted Bluetooth playback might not resume if other content is locally streamed.
* `make integration` isn't available for Android. To run Android integration tests, manually upload the test binary and input file and run ADB.
* Alexa might truncate the beginning of speech when responding to text-to-speech (TTS)  user events. This only impacts Raspberry Pi devices running Android Things with HDMI output audio.
* A reminder TTS message doesn't play if the sample app restarts and loses a network connection. Instead, the default alarm tone plays twice.
* `ServerDisconnectIntegratonTest` tests are disabled until they are updated to reflect new service behavior.
* The `DirectiveSequencerTest.test_handleBlockingThenImmediatelyThenNonBockingOnSameDialogId` test fails intermittently.
This commit is contained in:
Caleigh Minshall 2020-02-19 10:35:26 -08:00
parent f2353770ac
commit b1ef879eb0
393 changed files with 18460 additions and 3961 deletions
ACL
ADSL
AFML
AVSCommon

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2016-2020 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2016-2020 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.
@ -27,16 +27,18 @@
#include <unordered_set>
#include <AVSCommon/AVS/Attachment/AttachmentManager.h>
#include <AVSCommon/Utils/HTTP2/HTTP2ConnectionInterface.h>
#include <AVSCommon/SDKInterfaces/AuthDelegateInterface.h>
#include <AVSCommon/SDKInterfaces/PostConnectSendMessageInterface.h>
#include <AVSCommon/Utils/HTTP2/HTTP2ConnectionInterface.h>
#include <AVSCommon/Utils/HTTP2/HTTP2ConnectionObserverInterface.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include "ACL/Transport/MessageConsumerInterface.h"
#include "ACL/Transport/PingHandler.h"
#include "ACL/Transport/PostConnectFactoryInterface.h"
#include "ACL/Transport/PostConnectObserverInterface.h"
#include "ACL/Transport/TransportInterface.h"
#include "ACL/Transport/TransportObserverInterface.h"
#include "ACL/Transport/SynchronizedMessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
@ -50,6 +52,7 @@ class HTTP2Transport
, public PostConnectObserverInterface
, public avsCommon::sdkInterfaces::PostConnectSendMessageInterface
, public avsCommon::sdkInterfaces::AuthObserverInterface
, public avsCommon::utils::http2::HTTP2ConnectionObserverInterface
, public ExchangeHandlerContextInterface {
public:
/*
@ -75,7 +78,9 @@ public:
* @param attachmentManager The attachment manager that manages the attachments.
* @param transportObserver The observer of the new instance of TransportInterface.
* @param postConnectFactory The object used to create @c PostConnectInterface instances.
* @param sharedRequestQueue Request queue shared by all instances of HTTPTransportInterface.
* @param configuration An optional configuration to specify HTTP2/2 connection settings.
* @param metricRecorder The metric recorder.
* @return A shared pointer to a HTTP2Transport object.
*/
static std::shared_ptr<HTTP2Transport> create(
@ -86,7 +91,9 @@ public:
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
std::shared_ptr<TransportObserverInterface> transportObserver,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
Configuration configuration = Configuration());
std::shared_ptr<SynchronizedMessageRequestQueue> sharedRequestQueue,
Configuration configuration = Configuration(),
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
/**
* Method to add a TransportObserverInterface instance.
@ -114,7 +121,7 @@ public:
bool connect() override;
void disconnect() override;
bool isConnected() override;
void send(std::shared_ptr<avsCommon::avs::MessageRequest> request) override;
void onRequestEnqueued() override;
/// @}
/// @name PostConnectSendMessageInterface methods.
@ -157,6 +164,10 @@ public:
std::string getAVSGateway() override;
/// @}
/// @name HTTP2ConnectionObserverInterface methods.
void onGoawayReceived() override;
/// @}
private:
/**
* Enum to track the (internal) state of the HTTP2Transport
@ -196,7 +207,9 @@ private:
* @param attachmentManager The attachment manager that manages the attachments.
* @param transportObserver The observer of the new instance of TransportInterface.
* @param postConnect The object used to create PostConnectInterface instances.
* @param sharedRequestQueue Request queue shared by all instances of HTTPTransportInterface.
* @param configuration The HTTP2/2 connection settings.
* @param metricRecorder The metric recorder.
*/
HTTP2Transport(
std::shared_ptr<avsCommon::sdkInterfaces::AuthDelegateInterface> authDelegate,
@ -206,7 +219,9 @@ private:
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
std::shared_ptr<TransportObserverInterface> transportObserver,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
Configuration configuration);
std::shared_ptr<SynchronizedMessageRequestQueue> sharedRequestQueue,
Configuration configuration,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder);
/**
* Main loop for servicing the various states.
@ -277,12 +292,17 @@ private:
State handleShutdown();
/**
* Enqueue a MessageRequest for sending.
* Monitor the shared message queue while waiting for a state change. If messages have been sitting in the
* queue for too long (e.g. during prolonged internet connectivity outtage), complete them as TIMEDOUT.
*
* @param request The MessageRequest to enqueue.
* @param beforeConnected Whether or not to only allow enqueuing of messages before connected.
* @param whileState The @c State to keep waiting in.
* @param wakeTime The max time point to wait for.
* @return The new state.
*/
void enqueueRequest(std::shared_ptr<avsCommon::avs::MessageRequest> request, bool beforeConnected);
State monitorSharedQueueWhileWaiting(
State whileState,
std::chrono::time_point<std::chrono::steady_clock> maxWakeTime =
std::chrono::time_point<std::chrono::steady_clock>::max());
/**
* Handle sending @c MessageRequests and pings while in @c State::POST_CONNECTING or @c State::CONNECTED.
@ -290,7 +310,7 @@ private:
* @param whileState Continue sending @c MessageRequests and pings while in this state.
* @return The current value of @c m_state.
*/
State sendMessagesAndPings(State whileState);
State sendMessagesAndPings(State whileState, MessageRequestQueueInterface& requestQueue);
/**
* Set the state to a new state.
@ -342,6 +362,9 @@ private:
*/
State getState();
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
/// Mutex for accessing @c m_state and @c m_messageQueue
std::mutex m_mutex;
@ -369,6 +392,9 @@ private:
/// Factory for creating @c PostConnectInterface instances.
std::shared_ptr<PostConnectFactoryInterface> m_postConnectFactory;
/// Queue of @c MessageRequest instances to send that are shared between instances of @c HTTP2Transport.
std::shared_ptr<SynchronizedMessageRequestQueue> m_sharedRequestQueue;
/// Mutex to protect access to the m_observers variable.
std::mutex m_observerMutex;
@ -381,15 +407,12 @@ private:
/// PostConnect object is used to perform activities required once a connection is established.
std::shared_ptr<PostConnectInterface> m_postConnect;
/// Queue of @c MessageRequest instances to send. Serialized by @c m_mutex.
std::deque<std::shared_ptr<avsCommon::avs::MessageRequest>> m_requestQueue;
/// Queue of @c MessageRequest instances to send during the POST_CONNECTING state. Serialized by @c m_mutex.
MessageRequestQueue m_requestQueue;
/// Number of times connecting has been retried.
int m_connectRetryCount;
/// Is a message handler awaiting a response?
bool m_isMessageHandlerAwaitingResponse;
/// The number of message handlers that are not finished with their request.
int m_countOfUnfinishedMessageHandlers;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -22,6 +22,7 @@
#include <AVSCommon/AVS/Attachment/AttachmentManager.h>
#include <AVSCommon/SDKInterfaces/AuthDelegateInterface.h>
#include <AVSCommon/Utils/HTTP2/HTTP2ConnectionFactoryInterface.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include "ACL/Transport/MessageConsumerInterface.h"
#include "ACL/Transport/PostConnectFactoryInterface.h"
@ -41,10 +42,12 @@ public:
*
* @param connectionFactory Object used to create instances of HTTP2ConnectionInterface.
* @param postConnectFactory Object used to create instances of the PostConnectInterface.
* @param metricRecorder The metric recorder.
*/
HTTP2TransportFactory(
std::shared_ptr<avsCommon::utils::http2::HTTP2ConnectionFactoryInterface> connectionFactory,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory);
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
/// @name TransportFactoryInterface methods.
/// @{
@ -53,7 +56,8 @@ public:
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
const std::string& avsGateway,
std::shared_ptr<MessageConsumerInterface> messageConsumerInterface,
std::shared_ptr<TransportObserverInterface> transportObserverInterface) override;
std::shared_ptr<TransportObserverInterface> transportObserverInterface,
std::shared_ptr<SynchronizedMessageRequestQueue> sharedMessageRequestQueue) override;
/// @}
/**
@ -67,6 +71,9 @@ private:
/// Save a pointer to the object used to create instances of the PostConnectInterface.
std::shared_ptr<PostConnectFactoryInterface> m_postConnectFactory;
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
};
} // namespace acl

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2016-2020 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.
@ -18,9 +18,10 @@
#include <memory>
#include <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/AVS/Attachment/AttachmentManager.h>
#include <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/HTTP2/HTTP2MimeRequestSourceInterface.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include "ACL/Transport/ExchangeHandler.h"
#include "ACL/Transport/MessageConsumerInterface.h"
@ -51,6 +52,7 @@ public:
* @param messageRequest The MessageRequest to send.
* @param messageConsumer Where to send messages.
* @param attachmentManager Where to get attachments to write to.
* @param metricRecorder The metric recorder.
* @return A new MessageRequestHandler or nullptr if the operation fails.
*/
static std::shared_ptr<MessageRequestHandler> create(
@ -58,7 +60,8 @@ public:
const std::string& authToken,
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest,
std::shared_ptr<MessageConsumerInterface> messageConsumer,
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager);
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder);
private:
/**
@ -67,11 +70,13 @@ private:
* @param context The ExchangeContext in which this MessageRequest handler will operate.
* @param authToken The token to use to authorize the request.
* @param messageRequest The MessageRequest to send.
* @param metricRecorder The metric recorder.
*/
MessageRequestHandler(
std::shared_ptr<ExchangeHandlerContextInterface> context,
const std::string& authToken,
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest);
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder);
/**
* Notify the associated HTTP2Transport instance that the message request failed or was acknowledged by AVS.
@ -116,13 +121,16 @@ private:
/// Reader for current attachment (if any).
std::shared_ptr<avsCommon::avs::MessageRequest::NamedReader> m_namedReader;
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
/// Whether acknowledge of the @c MessageRequest was reported.
bool m_wasMessageRequestAcknowledgeReported;
/// Whether finish of the @c MessageRequest was reported.
bool m_wasMessageRequestFinishedReported;
/// Response code received through @c onReciveResponseCode (or zero).
/// Response code received through @c onReceiveResponseCode (or zero).
long m_responseCode;
};

View File

@ -0,0 +1,87 @@
/*
* Copyright 2019-2020 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 ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUE_H_
#define ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUE_H_
#include <deque>
#include <memory>
#include <unordered_map>
#include <utility>
#include <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/functional/hash.h>
#include "ACL/Transport/MessageRequestQueueInterface.h"
namespace alexaClientSDK {
namespace acl {
/**
* Class to manage @c MessageRequest send queues in HTTP2Transport.
*
* Note: This class is not thread safe. The user should ensure thread safety.
*/
class MessageRequestQueue : public MessageRequestQueueInterface {
public:
/// Helper structure to keep track of the SendQueue.
struct MessageRequestQueueStruct {
/// Indicates if the queue is waiting on a response.
bool isQueueWaitingForResponse;
/// The queue used to send @c MessageRequest.
std::deque<std::pair<
std::chrono::time_point<std::chrono::steady_clock>,
std::shared_ptr<avsCommon::avs::MessageRequest>>>
queue;
MessageRequestQueueStruct() : isQueueWaitingForResponse{false} {
}
};
/**
* Constructor.
*/
MessageRequestQueue();
/**
* Destructor.
*/
~MessageRequestQueue() override;
/// Override MessageRequestQueueInterface methods
/// @{
void enqueueRequest(std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest) override;
avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>> peekRequestTime() override;
std::shared_ptr<avsCommon::avs::MessageRequest> dequeueRequest() override;
bool isMessageRequestAvailable() const override;
void setWaitingFlagForQueue() override;
void clearWaitingFlagForQueue() override;
bool empty() const override;
void clear() override;
/// @}
private:
/// Member to keep track of the current @c MessageRequests present.
int m_size;
/// The struct that contains the message queue
MessageRequestQueueStruct m_sendQueue;
};
} // namespace acl
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUE_H_

View File

@ -0,0 +1,101 @@
/*
* Copyright 2019-2020 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 ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUEINTERFACE_H_
#define ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUEINTERFACE_H_
#include <chrono>
#include <deque>
#include <memory>
#include <unordered_map>
#include <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/Optional.h>
namespace alexaClientSDK {
namespace acl {
/**
* An interface that abstracts the operations of a @c MessageRequestQueuesMap between the standard version
* and the synchronized implementation.
*/
class MessageRequestQueueInterface {
public:
/**
* Destructor.
*/
virtual ~MessageRequestQueueInterface() = default;
/**
* Enqueues the @c MessageRequest to the corresponding send queue based on the send queue type.
* If send queue type is not present, a new queue is created before enqueuing the request.
*/
virtual void enqueueRequest(std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest) = 0;
/**
* Peek at the next item in the queue and retrieve the time that the request was queued.
*
* @return The time that the next request (if any) was queued.
*/
virtual avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>> peekRequestTime() = 0;
/**
* Dequeues the next available @c MessageRequest by taking into account if the queue is waiting for
* a response. This method keeps track of the queue being processed and once a request from the queue
* is dequeued moves it to the next queue. It also loops back to the starting queue at the end and exits
* the loop if a valid MessageRequest is found or if it reaches the place it started.
*
* @return @c MessageRequest if an available, else return nullptr.
*/
virtual std::shared_ptr<avsCommon::avs::MessageRequest> dequeueRequest() = 0;
/**
* This method checks if there is a @c MessageRequest available to be sent.
*
* @return true if @c MessageRequest is available to be sent, else false.
*/
virtual bool isMessageRequestAvailable() const = 0;
/**
* Sets the waiting flag for the queue specified by the given send queue type.
*
* @param sendQueueType the send queue type of the queue to set the waiting flag on.
*/
virtual void setWaitingFlagForQueue() = 0;
/**
* Clear the waiting flag for the queue specified by the given send queue type.
*
* @param sendQueueType the send queue type of the queue to clear the waiting flag on.
*/
virtual void clearWaitingFlagForQueue() = 0;
/**
* Checks if there are any @c MessageRequests.
*
* @return true if there are no messageRequests in the queue, else false.
*/
virtual bool empty() const = 0;
/**
* Clears all the @c MessageRequests along with the corresponding queues.
*/
virtual void clear() = 0;
};
} // namespace acl
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_MESSAGEREQUESTQUEUEINTERFACE_H_

View File

@ -33,6 +33,7 @@
#include "ACL/Transport/TransportFactoryInterface.h"
#include "ACL/Transport/TransportInterface.h"
#include "ACL/Transport/TransportObserverInterface.h"
#include "ACL/Transport/SynchronizedMessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
@ -202,6 +203,9 @@ private:
/// The transport factory.
std::shared_ptr<TransportFactoryInterface> m_transportFactory;
/// The synchonized queue of messages to send that is shared between transports.
std::shared_ptr<SynchronizedMessageRequestQueue> m_requestQueue;
protected:
/**
* Executor to perform asynchronous operations:

View File

@ -0,0 +1,67 @@
/*
* Copyright 2019-2020 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 ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_SYNCHRONIZEDMESSAGEREQUESTQUEUE_H_
#define ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_SYNCHRONIZEDMESSAGEREQUESTQUEUE_H_
#include <deque>
#include <memory>
#include <unordered_map>
#include <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/functional/hash.h>
#include <ACL/Transport/MessageRequestQueue.h>
#include "ACL/Transport/MessageRequestQueueInterface.h"
namespace alexaClientSDK {
namespace acl {
/**
* Class to manage @c MessageRequest send queue that is shared between instances of HTTP2Transport.
*/
class SynchronizedMessageRequestQueue : public MessageRequestQueueInterface {
public:
/**
* Constructor.
*/
SynchronizedMessageRequestQueue() = default;
/**
* Destructor.
*/
~SynchronizedMessageRequestQueue() override;
/// Override MessageRequestQueueInterface methods
/// @{
void enqueueRequest(std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest) override;
avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>> peekRequestTime() override;
std::shared_ptr<avsCommon::avs::MessageRequest> dequeueRequest() override;
bool isMessageRequestAvailable() const override;
void setWaitingFlagForQueue() override;
void clearWaitingFlagForQueue() override;
bool empty() const override;
void clear() override;
/// @}
private:
mutable std::mutex m_mutex;
MessageRequestQueue m_requestQueue;
};
} // namespace acl
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_SYNCHRONIZEDMESSAGEREQUESTQUEUE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -28,6 +28,11 @@ public:
/// Retry Timer Object for transport.
static avsCommon::utils::RetryTimer RETRY_TIMER;
/// Static function member to get RETRY_TIMER
static avsCommon::utils::RetryTimer getRetryTimer() {
return RETRY_TIMER;
}
};
} // namespace acl

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -25,6 +25,7 @@
#include "ACL/Transport/TransportInterface.h"
#include "ACL/Transport/MessageConsumerInterface.h"
#include "ACL/Transport/TransportObserverInterface.h"
#include "ACL/Transport/SynchronizedMessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
@ -38,9 +39,11 @@ public:
* Creates a new transport.
*
* @param authDelegate The AuthDelegateInterface to use for authentication and authorization with AVS.
* @param attachmentManager The attachment manager that manages the attachments.
* @param avsGateway The URL for the AVS server we will connect to.
* @param messageConsumerInterface The object which should be notified on messages which arrive from AVS.
* @param transportObserverInterface A pointer to the transport observer the new transport should notify.
* @param sharedRequestQueue Request queue shared by all instances of TransportInterface.
* @return A new MessageRouter object.
*/
virtual std::shared_ptr<TransportInterface> createTransport(
@ -48,7 +51,8 @@ public:
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
const std::string& avsGateway,
std::shared_ptr<MessageConsumerInterface> messageConsumerInterface,
std::shared_ptr<TransportObserverInterface> transportObserverInterface) = 0;
std::shared_ptr<TransportObserverInterface> transportObserverInterface,
std::shared_ptr<SynchronizedMessageRequestQueue> sharedMessageRequestQueue) = 0;
/**
* Destructor.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -58,11 +58,9 @@ public:
virtual bool isConnected() = 0;
/**
* Sends an message request. This call blocks until the message can be sent.
*
* @param request The requested message.
* A message request has been added to the shared synchronized queue and is ready to be read.
*/
virtual void send(std::shared_ptr<avsCommon::avs::MessageRequest> request) = 0;
virtual void onRequestEnqueued() = 0;
/**
* Deleted copy constructor

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -119,6 +119,7 @@ bool DownchannelHandler::onReceiveResponseCode(long responseCode) {
case HTTPResponseCode::REDIRECTION_PERMANENT_REDIRECT:
case HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST:
case HTTPResponseCode::SERVER_ERROR_INTERNAL:
case HTTPResponseCode::SERVER_ERROR_NOT_IMPLEMENTED:
break;
case HTTPResponseCode::SUCCESS_OK:
m_context->onDownchannelConnected();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2016-2020 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.
@ -13,6 +13,7 @@
* permissions and limitations under the License.
*/
#include <algorithm>
#include <chrono>
#include <functional>
#include <random>
@ -59,6 +60,9 @@ static const int MAX_MESSAGE_HANDLERS = MAX_STREAMS - 2;
/// Timeout to send a ping to AVS if there has not been any other acitivity on the connection.
static std::chrono::minutes INACTIVITY_TIMEOUT{5};
/// Max time a @c MessageRequest should linger unprocessed before it should be consider TIMEDOUT.
static const std::chrono::seconds MESSAGE_QUEUE_TIMEOUT = std::chrono::seconds(15);
/**
* Write a @c HTTP2Transport::State value to an @c ostream as a string.
*
@ -101,7 +105,9 @@ std::shared_ptr<HTTP2Transport> HTTP2Transport::create(
std::shared_ptr<AttachmentManager> attachmentManager,
std::shared_ptr<TransportObserverInterface> transportObserver,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
Configuration configuration) {
std::shared_ptr<SynchronizedMessageRequestQueue> sharedRequestQueue,
Configuration configuration,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder) {
ACSDK_DEBUG5(LX(__func__)
.d("authDelegate", authDelegate.get())
.d("avsGateway", avsGateway)
@ -109,7 +115,8 @@ std::shared_ptr<HTTP2Transport> HTTP2Transport::create(
.d("messageConsumer", messageConsumer.get())
.d("attachmentManager", attachmentManager.get())
.d("transportObserver", transportObserver.get())
.d("postConnectFactory", postConnectFactory.get()));
.d("postConnectFactory", postConnectFactory.get())
.d("sharedRequestQueue", sharedRequestQueue.get()));
if (!authDelegate) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullAuthDelegate"));
@ -141,15 +148,22 @@ std::shared_ptr<HTTP2Transport> HTTP2Transport::create(
return nullptr;
}
if (!sharedRequestQueue) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullSharedRequestQueue"));
return nullptr;
}
auto transport = std::shared_ptr<HTTP2Transport>(new HTTP2Transport(
authDelegate,
std::move(authDelegate),
avsGateway,
http2Connection,
messageConsumer,
attachmentManager,
transportObserver,
postConnectFactory,
configuration));
std::move(http2Connection),
std::move(messageConsumer),
std::move(attachmentManager),
std::move(transportObserver),
std::move(postConnectFactory),
std::move(sharedRequestQueue),
configuration,
std::move(metricRecorder)));
return transport;
}
@ -162,28 +176,23 @@ HTTP2Transport::HTTP2Transport(
std::shared_ptr<AttachmentManager> attachmentManager,
std::shared_ptr<TransportObserverInterface> transportObserver,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
Configuration configuration) :
std::shared_ptr<SynchronizedMessageRequestQueue> sharedRequestQueue,
Configuration configuration,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder) :
m_metricRecorder{std::move(metricRecorder)},
m_state{State::INIT},
m_authDelegate{authDelegate},
m_authDelegate{std::move(authDelegate)},
m_avsGateway{avsGateway},
m_http2Connection{http2Connection},
m_messageConsumer{messageConsumer},
m_attachmentManager{attachmentManager},
m_postConnectFactory{postConnectFactory},
m_http2Connection{std::move(http2Connection)},
m_messageConsumer{std::move(messageConsumer)},
m_attachmentManager{std::move(attachmentManager)},
m_postConnectFactory{std::move(postConnectFactory)},
m_sharedRequestQueue{std::move(sharedRequestQueue)},
m_connectRetryCount{0},
m_isMessageHandlerAwaitingResponse{false},
m_countOfUnfinishedMessageHandlers{0},
m_postConnected{false},
m_configuration{configuration},
m_disconnectReason{ConnectionStatusObserverInterface::ChangedReason::NONE} {
ACSDK_DEBUG7(LX(__func__)
.d("authDelegate", authDelegate.get())
.d("avsGateway", avsGateway)
.d("http2Connection", http2Connection.get())
.d("messageConsumer", messageConsumer.get())
.d("attachmentManager", attachmentManager.get())
.d("transportObserver", transportObserver.get())
.d("postConnectFactory", postConnectFactory.get()));
m_observers.insert(transportObserver);
}
@ -250,14 +259,43 @@ bool HTTP2Transport::isConnected() {
return State::CONNECTED == m_state;
}
void HTTP2Transport::send(std::shared_ptr<MessageRequest> request) {
void HTTP2Transport::onRequestEnqueued() {
ACSDK_DEBUG7(LX(__func__));
enqueueRequest(request, false);
std::lock_guard<std::mutex> lock(m_mutex);
m_wakeEvent.notify_all();
}
void HTTP2Transport::sendPostConnectMessage(std::shared_ptr<MessageRequest> request) {
ACSDK_DEBUG7(LX(__func__));
enqueueRequest(request, true);
if (!request) {
ACSDK_ERROR(LX("enqueueRequestFailed").d("reason", "nullRequest"));
}
std::unique_lock<std::mutex> lock(m_mutex);
bool allowed = false;
switch (m_state) {
case State::INIT:
case State::AUTHORIZING:
case State::CONNECTING:
case State::WAITING_TO_RETRY_CONNECTING:
case State::POST_CONNECTING:
allowed = true;
break;
case State::CONNECTED:
case State::SERVER_SIDE_DISCONNECT:
case State::DISCONNECTING:
case State::SHUTDOWN:
allowed = false;
break;
}
if (allowed) {
m_requestQueue.enqueueRequest(request);
m_wakeEvent.notify_all();
} else {
ACSDK_ERROR(LX("enqueueRequestFailed").d("reason", "notInAllowedState").d("m_state", m_state));
lock.unlock();
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
}
}
void HTTP2Transport::onPostConnected() {
@ -398,7 +436,7 @@ void HTTP2Transport::onDownchannelFinished() {
void HTTP2Transport::onMessageRequestSent() {
std::lock_guard<std::mutex> lock(m_mutex);
m_isMessageHandlerAwaitingResponse = true;
m_sharedRequestQueue->setWaitingFlagForQueue();
m_countOfUnfinishedMessageHandlers++;
ACSDK_DEBUG7(LX(__func__).d("countOfUnfinishedMessageHandlers", m_countOfUnfinishedMessageHandlers));
}
@ -415,7 +453,7 @@ void HTTP2Transport::onMessageRequestTimeout() {
void HTTP2Transport::onMessageRequestAcknowledged() {
ACSDK_DEBUG7(LX(__func__));
std::lock_guard<std::mutex> lock(m_mutex);
m_isMessageHandlerAwaitingResponse = false;
m_sharedRequestQueue->clearWaitingFlagForQueue();
m_wakeEvent.notify_all();
}
@ -465,8 +503,13 @@ std::string HTTP2Transport::getAVSGateway() {
return m_avsGateway;
}
void HTTP2Transport::onGoawayReceived() {
ACSDK_DEBUG5(LX(__func__));
}
void HTTP2Transport::mainLoop() {
ACSDK_DEBUG7(LX(__func__));
m_http2Connection->addObserver(shared_from_this());
m_postConnect = m_postConnectFactory->createPostConnect();
if (!m_postConnect || !m_postConnect->doPostConnect(shared_from_this(), shared_from_this())) {
@ -526,55 +569,46 @@ HTTP2Transport::State HTTP2Transport::handleAuthorizing() {
m_authDelegate->addAuthObserver(shared_from_this());
std::unique_lock<std::mutex> lock(m_mutex);
m_wakeEvent.wait(lock, [this]() { return m_state != State::AUTHORIZING; });
return m_state;
return monitorSharedQueueWhileWaiting(State::AUTHORIZING);
}
HTTP2Transport::State HTTP2Transport::handleConnecting() {
ACSDK_DEBUG5(LX(__func__));
std::unique_lock<std::mutex> lock(m_mutex);
auto authToken = m_authDelegate->getAuthToken();
while (State::CONNECTING == m_state) {
lock.unlock();
auto authToken = m_authDelegate->getAuthToken();
if (authToken.empty()) {
setState(
State::WAITING_TO_RETRY_CONNECTING, ConnectionStatusObserverInterface::ChangedReason::INVALID_AUTH);
break;
}
auto downchannelHandler =
DownchannelHandler::create(shared_from_this(), authToken, m_messageConsumer, m_attachmentManager);
lock.lock();
if (!downchannelHandler) {
ACSDK_ERROR(LX("handleConnectingFailed").d("reason", "createDownchannelHandlerFailed"));
setStateLocked(
State::WAITING_TO_RETRY_CONNECTING, ConnectionStatusObserverInterface::ChangedReason::INTERNAL_ERROR);
return m_state;
}
while (State::CONNECTING == m_state) {
m_wakeEvent.wait(lock);
}
if (authToken.empty()) {
ACSDK_DEBUG0(LX("Empty AuthToken"));
std::lock_guard<std::mutex> lock(m_mutex);
setStateLocked(
State::WAITING_TO_RETRY_CONNECTING, ConnectionStatusObserverInterface::ChangedReason::INVALID_AUTH);
return m_state;
}
return m_state;
auto downchannelHandler =
DownchannelHandler::create(shared_from_this(), authToken, m_messageConsumer, m_attachmentManager);
if (!downchannelHandler) {
ACSDK_ERROR(LX("handleConnectingFailed").d("reason", "createDownchannelHandlerFailed"));
std::lock_guard<std::mutex> lock(m_mutex);
setStateLocked(
State::WAITING_TO_RETRY_CONNECTING, ConnectionStatusObserverInterface::ChangedReason::INTERNAL_ERROR);
return m_state;
}
return monitorSharedQueueWhileWaiting(State::CONNECTING);
}
HTTP2Transport::State HTTP2Transport::handleWaitingToRetryConnecting() {
ACSDK_DEBUG7(LX(__func__));
std::chrono::milliseconds timeout = TransportDefines::RETRY_TIMER.calculateTimeToRetry(m_connectRetryCount);
auto timeout = TransportDefines::getRetryTimer().calculateTimeToRetry(m_connectRetryCount);
ACSDK_DEBUG7(
LX("handleConnectingWaitingToRetry").d("connectRetryCount", m_connectRetryCount).d("timeout", timeout.count()));
m_connectRetryCount++;
std::unique_lock<std::mutex> lock(m_mutex);
m_wakeEvent.wait_for(lock, timeout, [this] { return m_state != State::WAITING_TO_RETRY_CONNECTING; });
auto wakeTime = std::chrono::steady_clock::now() + timeout;
monitorSharedQueueWhileWaiting(State::WAITING_TO_RETRY_CONNECTING, wakeTime);
std::lock_guard<std::mutex> lock(m_mutex);
if (State::WAITING_TO_RETRY_CONNECTING == m_state) {
setStateLocked(State::CONNECTING, ConnectionStatusObserverInterface::ChangedReason::NONE);
}
@ -587,7 +621,7 @@ HTTP2Transport::State HTTP2Transport::handlePostConnecting() {
setState(State::CONNECTED, ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
return State::CONNECTED;
}
return sendMessagesAndPings(State::POST_CONNECTING);
return sendMessagesAndPings(State::POST_CONNECTING, m_requestQueue);
}
HTTP2Transport::State HTTP2Transport::handleConnected() {
@ -596,7 +630,7 @@ HTTP2Transport::State HTTP2Transport::handleConnected() {
m_postConnect.reset();
}
notifyObserversOnConnected();
return sendMessagesAndPings(State::CONNECTED);
return sendMessagesAndPings(State::CONNECTED, *m_sharedRequestQueue);
}
HTTP2Transport::State HTTP2Transport::handleServerSideDisconnect() {
@ -621,12 +655,19 @@ HTTP2Transport::State HTTP2Transport::handleShutdown() {
{
std::lock_guard<std::mutex> lock(m_mutex);
for (auto request : m_requestQueue) {
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
// Flags are stored in the shared queue but the local request queue is drained.
m_sharedRequestQueue->clearWaitingFlagForQueue();
while (!m_requestQueue.empty()) {
auto request = m_requestQueue.dequeueRequest();
if (request != nullptr) {
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
}
}
m_requestQueue.clear();
}
m_http2Connection->removeObserver(shared_from_this());
m_http2Connection->disconnect();
notifyObserversOnDisconnect(m_disconnectReason);
@ -634,51 +675,53 @@ HTTP2Transport::State HTTP2Transport::handleShutdown() {
return State::SHUTDOWN;
}
void HTTP2Transport::enqueueRequest(std::shared_ptr<avsCommon::avs::MessageRequest> request, bool beforeConnected) {
ACSDK_DEBUG7(LX(__func__).d("beforeConnected", beforeConnected));
HTTP2Transport::State HTTP2Transport::monitorSharedQueueWhileWaiting(
alexaClientSDK::acl::HTTP2Transport::State whileState,
std::chrono::time_point<std::chrono::steady_clock> maxWakeTime) {
while (true) {
auto wakeTime = maxWakeTime;
if (!request) {
ACSDK_ERROR(LX("enqueueRequestFailed").d("reason", "nullRequest"));
}
std::unique_lock<std::mutex> lock(m_mutex);
bool allowed = false;
switch (m_state) {
case State::INIT:
case State::AUTHORIZING:
case State::CONNECTING:
case State::WAITING_TO_RETRY_CONNECTING:
case State::POST_CONNECTING:
allowed = beforeConnected;
break;
case State::CONNECTED:
allowed = !beforeConnected;
break;
case State::SERVER_SIDE_DISCONNECT:
case State::DISCONNECTING:
case State::SHUTDOWN:
allowed = false;
break;
}
while (true) {
auto messageRequestTime = m_sharedRequestQueue->peekRequestTime();
if (!messageRequestTime.hasValue()) {
// No more messages queued, break out to wait to connect.
break;
}
if (allowed) {
m_requestQueue.push_back(request);
m_wakeEvent.notify_all();
} else {
lock.unlock();
ACSDK_ERROR(LX("enqueueRequestFailed").d("reason", "notInAllowedState").d("m_state", m_state));
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
auto messageTimeoutTime = messageRequestTime.value() + MESSAGE_QUEUE_TIMEOUT;
if (messageTimeoutTime > std::chrono::steady_clock::now()) {
// Next message has not timed out, break out to wait to connect.
wakeTime = std::min(messageTimeoutTime, wakeTime);
break;
}
auto request = m_sharedRequestQueue->dequeueRequest();
request->sendCompleted(MessageRequestObserverInterface::Status::TIMEDOUT);
}
auto messageRequestTime = m_sharedRequestQueue->peekRequestTime();
std::unique_lock<std::mutex> lock(m_mutex);
m_wakeEvent.wait_until(lock, wakeTime, [this, whileState, messageRequestTime] {
return m_state != whileState || m_sharedRequestQueue->peekRequestTime() != messageRequestTime;
});
if (whileState != m_state || std::chrono::steady_clock::now() >= maxWakeTime) {
return m_state;
}
}
}
HTTP2Transport::State HTTP2Transport::sendMessagesAndPings(alexaClientSDK::acl::HTTP2Transport::State whileState) {
HTTP2Transport::State HTTP2Transport::sendMessagesAndPings(
alexaClientSDK::acl::HTTP2Transport::State whileState,
MessageRequestQueueInterface& requestQueue) {
ACSDK_DEBUG7(LX(__func__).d("whileState", whileState));
std::unique_lock<std::mutex> lock(m_mutex);
auto canSendMessage = [this] {
auto canSendMessage = [this, &requestQueue] {
return (
!m_isMessageHandlerAwaitingResponse && !m_requestQueue.empty() &&
(m_countOfUnfinishedMessageHandlers < MAX_MESSAGE_HANDLERS));
requestQueue.isMessageRequestAvailable() && (m_countOfUnfinishedMessageHandlers < MAX_MESSAGE_HANDLERS));
};
auto wakePredicate = [this, whileState, canSendMessage] {
@ -702,15 +745,19 @@ HTTP2Transport::State HTTP2Transport::sendMessagesAndPings(alexaClientSDK::acl::
}
if (canSendMessage()) {
auto messageRequest = m_requestQueue.front();
m_requestQueue.pop_front();
auto messageRequest = requestQueue.dequeueRequest();
lock.unlock();
auto authToken = m_authDelegate->getAuthToken();
if (!authToken.empty()) {
auto handler = MessageRequestHandler::create(
shared_from_this(), authToken, messageRequest, m_messageConsumer, m_attachmentManager);
shared_from_this(),
authToken,
messageRequest,
m_messageConsumer,
m_attachmentManager,
m_metricRecorder);
if (!handler) {
messageRequest->sendCompleted(MessageRequestObserverInterface::Status::INTERNAL_ERROR);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -31,7 +31,8 @@ std::shared_ptr<TransportInterface> HTTP2TransportFactory::createTransport(
std::shared_ptr<AttachmentManager> attachmentManager,
const std::string& avsGateway,
std::shared_ptr<MessageConsumerInterface> messageConsumerInterface,
std::shared_ptr<TransportObserverInterface> transportObserverInterface) {
std::shared_ptr<TransportObserverInterface> transportObserverInterface,
std::shared_ptr<SynchronizedMessageRequestQueue> sharedMessageRequestQueue) {
auto connection = m_connectionFactory->createHTTP2Connection();
if (!connection) {
return nullptr;
@ -44,14 +45,19 @@ std::shared_ptr<TransportInterface> HTTP2TransportFactory::createTransport(
messageConsumerInterface,
attachmentManager,
transportObserverInterface,
m_postConnectFactory);
m_postConnectFactory,
sharedMessageRequestQueue,
HTTP2Transport::Configuration(),
m_metricRecorder);
}
HTTP2TransportFactory::HTTP2TransportFactory(
std::shared_ptr<avsCommon::utils::http2::HTTP2ConnectionFactoryInterface> connectionFactory,
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory) :
m_connectionFactory{connectionFactory},
m_postConnectFactory{postConnectFactory} {
std::shared_ptr<PostConnectFactoryInterface> postConnectFactory,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder) :
m_connectionFactory{std::move(connectionFactory)},
m_postConnectFactory{std::move(postConnectFactory)},
m_metricRecorder{std::move(metricRecorder)} {
}
} // namespace acl

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -21,6 +21,9 @@
#include <AVSCommon/Utils/HTTP2/HTTP2MimeRequestEncoder.h>
#include <AVSCommon/Utils/HTTP2/HTTP2MimeResponseDecoder.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include <AVSCommon/Utils/Metrics/DataPointStringBuilder.h>
#include <AVSCommon/Utils/Metrics/DataPointCounterBuilder.h>
#include <AVSCommon/Utils/Metrics/MetricEventBuilder.h>
#include "ACL/Transport/HTTP2Transport.h"
#include "ACL/Transport/MimeResponseSink.h"
@ -33,6 +36,7 @@ using namespace avsCommon::avs::attachment;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils::http;
using namespace avsCommon::utils::http2;
using namespace avsCommon::utils::metrics;
/// URL to send events to
const static std::string AVS_EVENT_URL_PATH_EXTENSION = "/v20160207/events";
@ -63,6 +67,24 @@ static const std::string MESSAGEREQUEST_ID_PREFIX = "AVSEvent-";
/// String to identify log entries originating from this file.
static const std::string TAG("MessageRequestHandler");
/// Prefix used to identify metrics published by this module.
static const std::string ACL_METRIC_SOURCE_PREFIX = "ACL-";
/// Metric identifier for send mime data error
static const std::string SEND_DATA_ERROR = "ERROR.SEND_DATA_ERROR";
/// Read status tag
static const std::string READ_STATUS_TAG = "READ_STATUS";
/// Read overrun error
static const std::string ERROR_READ_OVERRUN = "READ_OVERRUN";
/// Internal error
static const std::string ERROR_INTERNAL = "INTERNAL_ERROR";
/// Send completed
static const std::string SEND_COMPLETED = "SEND_COMPLETED";
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
@ -70,6 +92,26 @@ static const std::string TAG("MessageRequestHandler");
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
/**
* Capture metric for the last send data result.
*
* @param metricRecorder The metric recorder object.
* @param count Number of errors.
* @param readStatus The read status.
*/
static void collectSendDataResultMetric(
const std::shared_ptr<MetricRecorderInterface>& metricRecorder,
int count,
const std::string& readStatus) {
recordMetric(
metricRecorder,
MetricEventBuilder{}
.setActivityName(ACL_METRIC_SOURCE_PREFIX + SEND_DATA_ERROR)
.addDataPoint(DataPointCounterBuilder{}.setName(SEND_DATA_ERROR).increment(count).build())
.addDataPoint(DataPointStringBuilder{}.setName(READ_STATUS_TAG).setValue(readStatus).build())
.build());
}
MessageRequestHandler::~MessageRequestHandler() {
reportMessageRequestAcknowledged();
reportMessageRequestFinished();
@ -80,7 +122,8 @@ std::shared_ptr<MessageRequestHandler> MessageRequestHandler::create(
const std::string& authToken,
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest,
std::shared_ptr<MessageConsumerInterface> messageConsumer,
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager) {
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> attachmentManager,
std::shared_ptr<MetricRecorderInterface> metricRecorder) {
ACSDK_DEBUG7(LX(__func__).d("context", context.get()).d("messageRequest", messageRequest.get()));
if (!context) {
@ -93,7 +136,8 @@ std::shared_ptr<MessageRequestHandler> MessageRequestHandler::create(
return nullptr;
}
std::shared_ptr<MessageRequestHandler> handler(new MessageRequestHandler(context, authToken, messageRequest));
std::shared_ptr<MessageRequestHandler> handler(
new MessageRequestHandler(context, authToken, messageRequest, std::move(metricRecorder)));
// Allow custom path extension, if provided by the sender of the MessageRequest
@ -126,13 +170,15 @@ std::shared_ptr<MessageRequestHandler> MessageRequestHandler::create(
MessageRequestHandler::MessageRequestHandler(
std::shared_ptr<ExchangeHandlerContextInterface> context,
const std::string& authToken,
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest) :
std::shared_ptr<avsCommon::avs::MessageRequest> messageRequest,
std::shared_ptr<MetricRecorderInterface> metricRecorder) :
ExchangeHandler{context, authToken},
m_messageRequest{messageRequest},
m_json{messageRequest->getJsonContent()},
m_jsonNext{m_json.c_str()},
m_countOfJsonBytesLeft{m_json.size()},
m_countOfPartsSent{0},
m_metricRecorder{metricRecorder},
m_wasMessageRequestAcknowledgeReported{false},
m_wasMessageRequestFinishedReported{false},
m_responseCode{0} {
@ -219,11 +265,17 @@ HTTP2SendDataResult MessageRequestHandler::onSendMimePartData(char* bytes, size_
// Stream consumed. Move on to next part.
m_namedReader.reset();
m_countOfPartsSent++;
collectSendDataResultMetric(m_metricRecorder, 0, SEND_COMPLETED);
return HTTP2SendDataResult::COMPLETE;
// Handle any attachment read errors.
case AttachmentReader::ReadStatus::ERROR_OVERRUN:
collectSendDataResultMetric(m_metricRecorder, 1, ERROR_READ_OVERRUN);
// Stream failure. Abort sending the request.
return HTTP2SendDataResult::ABORT;
case AttachmentReader::ReadStatus::ERROR_INTERNAL:
collectSendDataResultMetric(m_metricRecorder, 1, ERROR_INTERNAL);
// Stream failure. Abort sending the request.
return HTTP2SendDataResult::ABORT;

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019-2020 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 <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include "ACL/Transport/MessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
using namespace avsCommon::avs;
/// String to identify log entries originating from this file.
static const std::string TAG("MessageRequestQueue");
static const std::string EMPTY_QUEUE_NAME = "";
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
MessageRequestQueue::MessageRequestQueue() : m_size{0} {
}
MessageRequestQueue::~MessageRequestQueue() {
clearWaitingFlagForQueue();
clear();
}
void MessageRequestQueue::enqueueRequest(std::shared_ptr<MessageRequest> messageRequest) {
if (messageRequest != nullptr) {
m_sendQueue.queue.push_back({std::chrono::steady_clock::now(), messageRequest});
m_size++;
} else {
ACSDK_ERROR(LX("enqueueRequest").d("reason", "nullMessageRequest"));
}
}
avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>> MessageRequestQueue::peekRequestTime() {
if (m_size > 0) {
return m_sendQueue.queue.front().first;
}
return avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>>();
}
std::shared_ptr<MessageRequest> MessageRequestQueue::dequeueRequest() {
std::shared_ptr<MessageRequest> messageRequest;
if (m_size > 0) {
messageRequest = m_sendQueue.queue.front().second;
m_sendQueue.queue.pop_front();
m_size--;
}
return messageRequest;
}
bool MessageRequestQueue::isMessageRequestAvailable() const {
return !m_sendQueue.queue.empty() && !m_sendQueue.isQueueWaitingForResponse;
}
void MessageRequestQueue::setWaitingFlagForQueue() {
m_sendQueue.isQueueWaitingForResponse = true;
}
void MessageRequestQueue::clearWaitingFlagForQueue() {
m_sendQueue.isQueueWaitingForResponse = false;
}
bool MessageRequestQueue::empty() const {
return (0 == m_size);
}
void MessageRequestQueue::clear() {
m_sendQueue.queue.clear();
m_size = 0;
}
} // namespace acl
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2016-2020 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.
@ -52,7 +52,8 @@ MessageRouter::MessageRouter(
m_connectionReason{ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST},
m_isEnabled{false},
m_attachmentManager{attachmentManager},
m_transportFactory{transportFactory} {
m_transportFactory{transportFactory},
m_requestQueue{std::make_shared<SynchronizedMessageRequestQueue>()} {
}
MessageRouterInterface::ConnectionStatus MessageRouter::getConnectionStatus() {
@ -82,6 +83,20 @@ void MessageRouter::enable() {
void MessageRouter::doShutdown() {
disable();
// The above call will release all the transports. If m_requestQueue is non-empty once all of the transports
// have been released, any outstanding MessageRequest instances must receive an onCompleted(NOT_CONNECTED)
// notification.
std::unique_lock<std::mutex> lock{m_connectionMutex};
if (!m_requestQueue->empty()) {
auto request = m_requestQueue->dequeueRequest();
if (request != nullptr) {
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
}
m_requestQueue->clear();
}
lock.unlock();
m_executor.shutdown();
}
@ -98,7 +113,8 @@ void MessageRouter::sendMessage(std::shared_ptr<MessageRequest> request) {
}
std::unique_lock<std::mutex> lock{m_connectionMutex};
if (m_activeTransport) {
m_activeTransport->send(request);
m_requestQueue->enqueueRequest(request);
m_activeTransport->onRequestEnqueued();
} else {
ACSDK_ERROR(LX("sendFailed").d("reason", "noActiveTransport"));
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
@ -240,7 +256,7 @@ void MessageRouter::notifyObserverOnReceive(const std::string& contextId, const
void MessageRouter::createActiveTransportLocked() {
auto transport = m_transportFactory->createTransport(
m_authDelegate, m_attachmentManager, m_avsGateway, shared_from_this(), shared_from_this());
m_authDelegate, m_attachmentManager, m_avsGateway, shared_from_this(), shared_from_this(), m_requestQueue);
if (transport && transport->connect()) {
m_transports.push_back(transport);
m_activeTransport = transport;

View File

@ -0,0 +1,83 @@
/*
* Copyright 2019-2020 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 <AVSCommon/AVS/MessageRequest.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include "ACL/Transport/SynchronizedMessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
using namespace avsCommon::avs;
/// String to identify log entries originating from this file.
static const std::string TAG("SynchronizedMessageRequestQueue");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
SynchronizedMessageRequestQueue::~SynchronizedMessageRequestQueue() {
clearWaitingFlagForQueue();
clear();
}
void SynchronizedMessageRequestQueue::enqueueRequest(std::shared_ptr<MessageRequest> messageRequest) {
std::lock_guard<std::mutex> lock{m_mutex};
m_requestQueue.enqueueRequest(std::move(messageRequest));
}
avsCommon::utils::Optional<std::chrono::time_point<std::chrono::steady_clock>> SynchronizedMessageRequestQueue::
peekRequestTime() {
std::lock_guard<std::mutex> lock{m_mutex};
return m_requestQueue.peekRequestTime();
}
std::shared_ptr<MessageRequest> SynchronizedMessageRequestQueue::dequeueRequest() {
std::lock_guard<std::mutex> lock{m_mutex};
return m_requestQueue.dequeueRequest();
}
bool SynchronizedMessageRequestQueue::isMessageRequestAvailable() const {
std::lock_guard<std::mutex> lock{m_mutex};
return m_requestQueue.isMessageRequestAvailable();
}
void SynchronizedMessageRequestQueue::setWaitingFlagForQueue() {
std::lock_guard<std::mutex> lock{m_mutex};
m_requestQueue.setWaitingFlagForQueue();
}
void SynchronizedMessageRequestQueue::clearWaitingFlagForQueue() {
std::lock_guard<std::mutex> lock{m_mutex};
m_requestQueue.clearWaitingFlagForQueue();
}
bool SynchronizedMessageRequestQueue::empty() const {
std::lock_guard<std::mutex> lock{m_mutex};
return m_requestQueue.empty();
}
void SynchronizedMessageRequestQueue::clear() {
std::lock_guard<std::mutex> lock{m_mutex};
m_requestQueue.clear();
}
} // namespace acl
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.

View File

@ -2,9 +2,9 @@ add_subdirectory("Transport")
set(LIBRARIES ACL ${CMAKE_THREAD_LIBS_INIT} ACLTransportCommonTestLib)
set(INCLUDE_PATH
${AVSCommon_INCLUDE_DIRS}
"${AVSCommon_INCLUDE_DIRS}"
"${ACL_SOURCE_DIR}/include"
"${AVSCommon_SOURCE_DIR}/AVS/test"
"${AVSCommon_SOURCE_DIR}/SDKInterfaces/test"
"${AVSCommon_SOURCE_DIR}/Utils/test")
discover_unit_tests( "${INCLUDE_PATH}" "${LIBRARIES}")
discover_unit_tests("${INCLUDE_PATH}" "${LIBRARIES}")

View File

@ -5,7 +5,8 @@ add_library(ACLTransportCommonTestLib
MockHTTP2Request.cpp
MockMimeResponseSink.cpp)
target_include_directories(ACLTransportCommonTestLib PUBLIC
"${ACL_SOURCE_DIR}/include" "${ACL_SOURCE_DIR}/test/Transport")
"${ACL_SOURCE_DIR}/include"
"${ACL_SOURCE_DIR}/test/Transport")
target_link_libraries(ACLTransportCommonTestLib
AVSCommon
gtest_main

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -251,6 +251,15 @@ std::size_t MockHTTP2Connection::getMaxPostRequestsEnqueud() {
return m_maxPostRequestsEnqueued;
}
void MockHTTP2Connection::addObserver(std::shared_ptr<HTTP2ConnectionObserverInterface> observer) {
std::lock_guard<std::mutex> lock{m_observersMutex};
m_observers.insert(observer);
}
void MockHTTP2Connection::removeObserver(std::shared_ptr<HTTP2ConnectionObserverInterface> observer) {
std::lock_guard<std::mutex> lock{m_observersMutex};
m_observers.erase(observer);
}
} // namespace test
} // namespace http2
} // namespace utils

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -30,6 +30,7 @@
#include <AVSCommon/Utils/PromiseFuturePair.h>
#include <AVSCommon/Utils/HTTP/HttpResponseCode.h>
#include <AVSCommon/Utils/HTTP2/HTTP2RequestConfig.h>
#include <AVSCommon/Utils/Metrics/MockMetricRecorder.h>
#include "MockAuthDelegate.h"
#include "MockHTTP2Connection.h"
@ -39,6 +40,8 @@
#include "MockPostConnectFactory.h"
#include "MockTransportObserver.h"
#include "ACL/Transport/SynchronizedMessageRequestQueue.h"
namespace alexaClientSDK {
namespace acl {
namespace transport {
@ -202,6 +205,12 @@ protected:
/// The mock @c PostConnectInterface.
std::shared_ptr<MockPostConnect> m_mockPostConnect;
/// The mock @c MetricRecorder.
std::shared_ptr<metrics::test::MockMetricRecorder> m_mockMetricRecorder;
/// The message queue map.
std::shared_ptr<SynchronizedMessageRequestQueue> m_synchronizedMessageRequestQueue;
/// A promise that the Auth Observer will be set.
PromiseFuturePair<std::shared_ptr<AuthObserverInterface>> m_authObserverSet;
@ -253,7 +262,11 @@ void HTTP2TransportTest::SetUp() {
m_mockTransportObserver = std::make_shared<NiceMock<MockTransportObserver>>();
m_mockPostConnectFactory = std::make_shared<NiceMock<MockPostConnectFactory>>();
m_mockPostConnect = std::make_shared<NiceMock<MockPostConnect>>();
m_mockMetricRecorder = std::make_shared<NiceMock<metrics::test::MockMetricRecorder>>();
m_mockAuthDelegate->setAuthToken(CBL_AUTHORIZATION_TOKEN);
HTTP2Transport::Configuration cfg;
m_synchronizedMessageRequestQueue = std::make_shared<SynchronizedMessageRequestQueue>();
m_http2Transport = HTTP2Transport::create(
m_mockAuthDelegate,
TEST_AVS_GATEWAY_STRING,
@ -261,7 +274,10 @@ void HTTP2TransportTest::SetUp() {
m_mockMessageConsumer,
m_attachmentManager,
m_mockTransportObserver,
m_mockPostConnectFactory);
m_mockPostConnectFactory,
m_synchronizedMessageRequestQueue,
cfg,
m_mockMetricRecorder);
ASSERT_NE(m_http2Transport, nullptr);
}
@ -652,7 +668,8 @@ TEST_F(HTTP2TransportTest, testSlow_messageRequestsQueuing) {
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageObservers.push_back(messageObserver);
messageReq->addObserver(messageObserver);
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
}
// Give m_http2Transport a chance to misbehave and send more than a single request before receiving a response.
@ -690,6 +707,73 @@ TEST_F(HTTP2TransportTest, testSlow_messageRequestsQueuing) {
m_http2Transport->shutdown();
// Count the number of messages that received CANCELED or NOT_CONNECTED event.
unsigned messagesCanceled = 0;
unsigned messagesRemaining = 0;
for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) {
switch (messageObservers[messageNum]->m_status.getValue()) {
case MessageRequestObserverInterface::Status::CANCELED:
case MessageRequestObserverInterface::Status::NOT_CONNECTED:
messagesCanceled++;
default:
break;
}
}
}
while (m_synchronizedMessageRequestQueue->dequeueRequest()) {
++messagesRemaining;
}
ASSERT_EQ(messagesCanceled + messagesRemaining, messagesCount);
}
/**
* Test MessageRequests are sent for sequential queue types.
*/
TEST_F(HTTP2TransportTest, messageRequests_SequentialSend) {
authorizeAndConnect();
// Send 5 messages.
std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
unsigned int messagesCount = 5; // number of test messages to Send
for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageObservers.push_back(messageObserver);
messageReq->addObserver(messageObserver);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
}
unsigned int postsRequestsCount = 0;
while (postsRequestsCount < messagesCount) {
// Delayed 200 response for each POST request.
std::this_thread::sleep_for(SHORT_DELAY);
postsRequestsCount++;
ASSERT_EQ((int)m_mockHttp2Connection->getPostRequestsNum(), (int)postsRequestsCount);
auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
if (request) {
request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
}
}
// Make sure HTTP2Transport sends out the 5 POST requests.
ASSERT_EQ(postsRequestsCount, messagesCount);
// On disconnect, send CANCELED response for each POST REQUEST.
EXPECT_CALL(*m_mockHttp2Connection, disconnect()).WillOnce(Invoke([this]() {
while (true) {
auto request = m_mockHttp2Connection->dequePostRequest();
if (!request) break;
request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::CANCELLED);
};
}));
m_http2Transport->shutdown();
// Count the number of messages that received CANCELED or NOT_CONNECTED event.
unsigned messagesCanceled = 0;
for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
@ -792,7 +876,8 @@ TEST_F(HTTP2TransportTest, test_onSendCompletedNotification) {
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageObservers.push_back(messageObserver);
messageReq->addObserver(messageObserver);
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
}
// Send the response code for each POST request.
@ -836,7 +921,8 @@ TEST_F(HTTP2TransportTest, test_onExceptionReceivedNon200Content) {
std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageReq->addObserver(messageObserver);
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
ASSERT_NE(request, nullptr);
@ -876,7 +962,8 @@ TEST_F(HTTP2TransportTest, test_messageConsumerReceiveDirective) {
std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageReq->addObserver(messageObserver);
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
auto eventStream = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
ASSERT_NE(eventStream, nullptr);
@ -904,7 +991,8 @@ TEST_F(HTTP2TransportTest, test_onServerSideDisconnectOnDownchannelClosure) {
// Send a message.
std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
PromiseFuturePair<void> gotOnServerSideDisconnect;
auto setGotOnServerSideDisconnect = [&gotOnServerSideDisconnect] { gotOnServerSideDisconnect.setValue(); };
@ -943,7 +1031,8 @@ TEST_F(HTTP2TransportTest, test_messageRequestTimeoutPingRequest) {
// Send a message.
std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
// Upon receiving the message, the mock HTTP2Connection/request will reply to the request with
// onResponseFinished(TIMEOUT).
@ -978,7 +1067,9 @@ TEST_F(HTTP2TransportTest, testTimer_networkInactivityPingRequest) {
m_attachmentManager,
m_mockTransportObserver,
m_mockPostConnectFactory,
cfg);
m_synchronizedMessageRequestQueue,
cfg,
m_mockMetricRecorder);
authorizeAndConnect();
@ -1023,7 +1114,9 @@ TEST_F(HTTP2TransportTest, testSlow_tearDownPingTimeout) {
m_attachmentManager,
m_mockTransportObserver,
m_mockPostConnectFactory,
cfg);
m_synchronizedMessageRequestQueue,
cfg,
m_mockMetricRecorder);
authorizeAndConnect();
@ -1037,7 +1130,7 @@ TEST_F(HTTP2TransportTest, testSlow_tearDownPingTimeout) {
// Reply to a ping request.
std::thread pingThread([this]() {
auto pingRequest = m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT);
ASSERT_TRUE(pingRequest);
ASSERT_NE(pingRequest, nullptr);
m_mockHttp2Connection->dequePingRequest();
pingRequest->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::TIMEOUT);
});
@ -1065,7 +1158,9 @@ TEST_F(HTTP2TransportTest, testSlow_tearDownPingFailure) {
m_attachmentManager,
m_mockTransportObserver,
m_mockPostConnectFactory,
cfg);
m_synchronizedMessageRequestQueue,
cfg,
m_mockMetricRecorder);
authorizeAndConnect();
@ -1079,7 +1174,7 @@ TEST_F(HTTP2TransportTest, testSlow_tearDownPingFailure) {
// Reply to a ping request.
std::thread pingThread([this]() {
auto pingRequest = m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT);
ASSERT_TRUE(pingRequest);
ASSERT_NE(pingRequest, nullptr);
m_mockHttp2Connection->dequePingRequest();
pingRequest->getSink()->onReceiveResponseCode(HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST);
pingRequest->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
@ -1112,11 +1207,12 @@ TEST_F(HTTP2TransportTest, testSlow_avsStreamsLimit) {
auto messageObserver = std::make_shared<TestMessageRequestObserver>();
messageObservers.push_back(messageObserver);
messageReq->addObserver(messageObserver);
m_http2Transport->send(messageReq);
m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
m_http2Transport->onRequestEnqueued();
}
// Check that there was a downchannel request sent out.
ASSERT_TRUE(m_mockHttp2Connection->getDownchannelRequest(RESPONSE_TIMEOUT));
ASSERT_NE(m_mockHttp2Connection->getDownchannelRequest(RESPONSE_TIMEOUT), nullptr);
// Check the messages we sent were limited.
ASSERT_EQ(m_mockHttp2Connection->getPostRequestsNum(), MAX_POST_STREAMS);

View File

@ -105,7 +105,7 @@ TEST_F(MessageRouterTest, test_sendIsSuccessfulWhenConnected) {
auto messageRequest = createMessageRequest();
// Expect to have the message sent to the transport
EXPECT_CALL(*m_mockTransport, send(messageRequest)).Times(1);
EXPECT_CALL(*m_mockTransport, onRequestEnqueued()).Times(1);
m_router->sendMessage(messageRequest);
@ -116,8 +116,8 @@ TEST_F(MessageRouterTest, test_sendIsSuccessfulWhenConnected) {
TEST_F(MessageRouterTest, test_sendFailsWhenDisconnected) {
auto messageRequest = createMessageRequest();
// Expect to have the message sent to the transport
EXPECT_CALL(*m_mockTransport, send(messageRequest)).Times(0);
// Expect to have the message to be enqueued but the transport is not notified
EXPECT_CALL(*m_mockTransport, onRequestEnqueued()).Times(0);
m_router->sendMessage(messageRequest);
}
@ -130,7 +130,7 @@ TEST_F(MessageRouterTest, test_sendFailsWhenPending) {
auto messageRequest = createMessageRequest();
// Expect to have the message sent to the transport.
EXPECT_CALL(*m_mockTransport, send(messageRequest)).Times(1);
EXPECT_CALL(*m_mockTransport, onRequestEnqueued()).Times(1);
m_router->sendMessage(messageRequest);
waitOnMessageRouter(SHORT_TIMEOUT_MS);
@ -145,7 +145,7 @@ TEST_F(MessageRouterTest, test_sendMessageDoesNotSendAfterDisconnected) {
m_router->disable();
// Expect to have the message sent to the transport
EXPECT_CALL(*m_mockTransport, send(messageRequest)).Times(0);
EXPECT_CALL(*m_mockTransport, onRequestEnqueued()).Times(0);
m_router->sendMessage(messageRequest);
}
@ -202,9 +202,9 @@ TEST_F(MessageRouterTest, test_serverSideDisconnectCreatesANewTransport) {
auto messageRequest = createMessageRequest();
EXPECT_CALL(*oldTransport.get(), send(messageRequest)).Times(0);
EXPECT_CALL(*oldTransport.get(), onRequestEnqueued()).Times(0);
EXPECT_CALL(*newTransport.get(), send(messageRequest)).Times(1);
EXPECT_CALL(*newTransport.get(), onRequestEnqueued()).Times(1);
m_router->sendMessage(messageRequest);

View File

@ -80,7 +80,8 @@ private:
std::shared_ptr<AttachmentManager> attachmentManager,
const std::string& avsGateway,
std::shared_ptr<MessageConsumerInterface> messageConsumerInterface,
std::shared_ptr<TransportObserverInterface> transportObserverInterface) override {
std::shared_ptr<TransportObserverInterface> transportObserverInterface,
std::shared_ptr<SynchronizedMessageRequestQueue> sharedMessageRequestQueue) override {
return m_mockTransport;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -22,6 +22,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <unordered_set>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@ -57,6 +58,8 @@ public:
/// @{
std::shared_ptr<HTTP2RequestInterface> createAndSendRequest(const HTTP2RequestConfig& config);
MOCK_METHOD0(disconnect, void());
void addObserver(std::shared_ptr<avsCommon::utils::http2::HTTP2ConnectionObserverInterface> observer);
void removeObserver(std::shared_ptr<avsCommon::utils::http2::HTTP2ConnectionObserverInterface> observer);
/// @}
/**
@ -237,6 +240,12 @@ private:
/// Queue of Ping requests. Serialized by @c m_pingRequestMutex.
std::deque<std::shared_ptr<MockHTTP2Request>> m_pingRequestQueue;
/// Mutex for observers.
std::mutex m_observersMutex;
/// Observers
std::unordered_set<std::shared_ptr<avsCommon::utils::http2::HTTP2ConnectionObserverInterface>> m_observers;
/// Serializes access to receiving Ping requests.
std::mutex m_pingRequestMutex;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -40,7 +40,8 @@ public:
MOCK_METHOD0(disconnect, void());
MOCK_METHOD0(isConnected, bool());
MOCK_METHOD0(isPendingDisconnected, bool());
MOCK_METHOD1(send, void(std::shared_ptr<avsCommon::avs::MessageRequest>));
MOCK_METHOD0(onRequestEnqueued, void());
MOCK_METHOD1(sendPostConnectMessage, void(std::shared_ptr<avsCommon::avs::MessageRequest>));
MOCK_METHOD2(onAttachmentReceived, void(const std::string& contextId, const std::string& message));
const int m_id;

View File

@ -24,6 +24,8 @@
#include <AVSCommon/AVS/DirectiveHandlerConfiguration.h>
#include <AVSCommon/AVS/HandlerAndPolicy.h>
#include <AVSCommon/Utils/RequiresShutdown.h>
#include <AVSCommon/Utils/Metrics/MetricEventBuilder.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
namespace alexaClientSDK {
namespace adsl {
@ -34,7 +36,8 @@ namespace adsl {
class DirectiveRouter : public avsCommon::utils::RequiresShutdown {
public:
/// Constructor.
DirectiveRouter();
/// @param metricRecorder The metric recorder.
DirectiveRouter(std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
/**
* Add mappings from handler's directives to @c BlockingPolicy values, gotten through the
@ -194,6 +197,9 @@ private:
*/
bool removeDirectiveHandlerLocked(std::shared_ptr<avsCommon::sdkInterfaces::DirectiveHandlerInterface> handler);
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
/// A mutex used to serialize access to @c m_configuration and @c m_handlerReferenceCounts.
std::mutex m_mutex;
@ -211,6 +217,16 @@ private:
*/
std::unordered_map<std::shared_ptr<avsCommon::sdkInterfaces::DirectiveHandlerInterface>, int>
m_handlerReferenceCounts;
/**
* Submit metrics related to the given directive.
*
* @param metricEventBuilder The metric event builder to be used.
* @param directive The given directive.
*/
void submitMetric(
avsCommon::utils::metrics::MetricEventBuilder& metricEventBuilder,
const std::shared_ptr<avsCommon::avs::AVSDirective>& directive);
};
} // namespace adsl

View File

@ -24,6 +24,7 @@
#include <AVSCommon/SDKInterfaces/ExceptionEncounteredSenderInterface.h>
#include <AVSCommon/SDKInterfaces/DirectiveSequencerInterface.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include "ADSL/DirectiveProcessor.h"
#include "ADSL/DirectiveRouter.h"
@ -41,10 +42,12 @@ public:
*
* @param exceptionSender An instance of the @c ExceptionEncounteredSenderInterface used to send
* ExceptionEncountered messages to AVS for directives that are not handled.
* @param metricRecorder The metric recorder.
* @return Returns a new DirectiveSequencer, or nullptr if the operation failed.
*/
static std::unique_ptr<DirectiveSequencerInterface> create(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender);
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
bool addDirectiveHandler(std::shared_ptr<avsCommon::sdkInterfaces::DirectiveHandlerInterface> handler) override;
@ -63,11 +66,13 @@ public:
private:
/**
* Constructor.
*
* @param exceptionSender An instance of the @c ExceptionEncounteredSenderInterface used to send
* ExceptionEncountered messages to AVS for directives that are not handled.
* @param metricRecorder The metric recorder.
*/
DirectiveSequencer(std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender);
DirectiveSequencer(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder);
void doShutdown() override;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -21,6 +21,7 @@
#include <AVSCommon/SDKInterfaces/ExceptionEncounteredSenderInterface.h>
#include <AVSCommon/SDKInterfaces/DirectiveSequencerInterface.h>
#include <AVSCommon/SDKInterfaces/MessageObserverInterface.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
namespace alexaClientSDK {
namespace adsl {
@ -39,11 +40,13 @@ public:
* @param directiveSequencerInterface The DirectiveSequencerInterface implementation, which will receive
* @c AVSDirectives.
* @param attachmentManager The @c AttachmentManager which created @c AVSDirectives will use to acquire Attachments.
* @param metricRecorder The metric recorder.
*/
MessageInterpreter(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionEncounteredSender,
std::shared_ptr<avsCommon::sdkInterfaces::DirectiveSequencerInterface> directiveSequencer,
std::shared_ptr<avsCommon::avs::attachment::AttachmentManagerInterface> attachmentManager);
std::shared_ptr<avsCommon::avs::attachment::AttachmentManagerInterface> attachmentManager,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
void receive(const std::string& contextId, const std::string& message) override;
@ -52,8 +55,10 @@ private:
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> m_exceptionEncounteredSender;
/// Object to which we will send @c AVSDirectives.
std::shared_ptr<avsCommon::sdkInterfaces::DirectiveSequencerInterface> m_directiveSequencer;
// The attachment manager.
/// The attachment manager.
std::shared_ptr<avsCommon::avs::attachment::AttachmentManagerInterface> m_attachmentManager;
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
};
} // namespace adsl

View File

@ -19,8 +19,9 @@
#include <vector>
#include <AVSCommon/AVS/DirectiveRoutingRule.h>
#include <AVSCommon/AVS/NamespaceAndName.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include <AVSCommon/Utils/Metrics/DataPointCounterBuilder.h>
#include <AVSCommon/Utils/Metrics/DataPointStringBuilder.h>
#include <AVSCommon/Utils/Optional.h>
#include "ADSL/DirectiveRouter.h"
@ -42,8 +43,40 @@ using namespace avsCommon::avs;
using namespace avsCommon::avs::directiveRoutingRule;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
using namespace avsCommon::utils::metrics;
DirectiveRouter::DirectiveRouter() : RequiresShutdown{"DirectiveRouter"} {
/// Prefix used in metrics published in this module.
static const std::string DIRECTIVE_SEQUENCER_METRIC_PREFIX = "DIRECTIVE_SEQUENCER-";
/// Metric name for directives that were dispatched immediately.
static const std::string DIRECTIVE_DISPATCHED_IMMEDIATE = "DIRECTIVE_DISPATCHED_IMMEDIATE";
/// Metric name for directives that were pre-handled.
static const std::string DIRECTIVE_DISPATCHED_PRE_HANDLE = "DIRECTIVE_DISPATCHED_PRE_HANDLE";
/// Metric name for directives that were handled directly.
static const std::string DIRECTIVE_DISPATCHED_HANDLE = "DIRECTIVE_DISPATCHED_HANDLE";
void DirectiveRouter::submitMetric(
MetricEventBuilder& metricEventBuilder,
const std::shared_ptr<AVSDirective>& directive) {
if (directive) {
metricEventBuilder.addDataPoint(
DataPointStringBuilder{}.setName("HTTP2_STREAM").setValue(directive->getAttachmentContextId()).build());
metricEventBuilder.addDataPoint(
DataPointStringBuilder{}.setName("DIRECTIVE_MESSAGE_ID").setValue(directive->getMessageId()).build());
}
auto metricEvent = metricEventBuilder.build();
if (metricEvent) {
recordMetric(m_metricRecorder, metricEvent);
} else {
ACSDK_ERROR(LX("submitMetricFailed").d("reason", "buildMetricFailed"));
}
}
DirectiveRouter::DirectiveRouter(std::shared_ptr<MetricRecorderInterface> metricRecorder) :
RequiresShutdown{"DirectiveRouter"},
m_metricRecorder{metricRecorder} {
}
bool DirectiveRouter::addDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
@ -161,6 +194,13 @@ bool DirectiveRouter::handleDirectiveImmediately(std::shared_ptr<avsCommon::avs:
}
ACSDK_INFO(LX("handleDirectiveImmediately").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
submitMetric(
MetricEventBuilder{}
.setActivityName(DIRECTIVE_SEQUENCER_METRIC_PREFIX + DIRECTIVE_DISPATCHED_IMMEDIATE)
.addDataPoint(DataPointCounterBuilder{}.setName(DIRECTIVE_DISPATCHED_IMMEDIATE).increment(1).build()),
directive);
handlerAndPolicy.handler->handleDirectiveImmediately(directive);
return true;
}
@ -178,6 +218,13 @@ bool DirectiveRouter::preHandleDirective(
}
ACSDK_INFO(LX("preHandleDirective").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handler);
submitMetric(
MetricEventBuilder{}
.setActivityName(DIRECTIVE_SEQUENCER_METRIC_PREFIX + DIRECTIVE_DISPATCHED_PRE_HANDLE)
.addDataPoint(DataPointCounterBuilder{}.setName(DIRECTIVE_DISPATCHED_PRE_HANDLE).increment(1).build()),
directive);
handler->preHandleDirective(directive, std::move(result));
return true;
}
@ -192,6 +239,13 @@ bool DirectiveRouter::handleDirective(const std::shared_ptr<AVSDirective>& direc
}
ACSDK_INFO(LX("handleDirective").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handler);
submitMetric(
MetricEventBuilder{}
.setActivityName(DIRECTIVE_SEQUENCER_METRIC_PREFIX + DIRECTIVE_DISPATCHED_HANDLE)
.addDataPoint(DataPointCounterBuilder{}.setName(DIRECTIVE_DISPATCHED_HANDLE).increment(1).build()),
directive);
auto result = handler->handleDirective(directive->getMessageId());
if (!result) {
ACSDK_WARN(LX("messageIdNotRecognized")

View File

@ -42,12 +42,13 @@ using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
std::unique_ptr<DirectiveSequencerInterface> DirectiveSequencer::create(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender) {
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<metrics::MetricRecorderInterface> metricRecorder) {
if (!exceptionSender) {
ACSDK_INFO(LX("createFailed").d("reason", "nullptrExceptionSender"));
return nullptr;
}
return std::unique_ptr<DirectiveSequencerInterface>(new DirectiveSequencer(exceptionSender));
return std::unique_ptr<DirectiveSequencerInterface>(new DirectiveSequencer(exceptionSender, metricRecorder));
}
bool DirectiveSequencer::addDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
@ -86,12 +87,14 @@ bool DirectiveSequencer::onDirective(std::shared_ptr<AVSDirective> directive) {
}
DirectiveSequencer::DirectiveSequencer(
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender) :
std::shared_ptr<avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<metrics::MetricRecorderInterface> metricRecorder) :
DirectiveSequencerInterface{"DirectiveSequencer"},
m_mutex{},
m_exceptionSender{exceptionSender},
m_isShuttingDown{false},
m_isEnabled{true} {
m_isEnabled{true},
m_directiveRouter{metricRecorder} {
m_directiveProcessor = std::make_shared<DirectiveProcessor>(&m_directiveRouter);
m_receivingThread = std::thread(&DirectiveSequencer::receivingLoop, this);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -15,6 +15,9 @@
#include "ADSL/MessageInterpreter.h"
#include <AVSCommon/Utils/Metrics/DataPointCounterBuilder.h>
#include <AVSCommon/Utils/Metrics/DataPointStringBuilder.h>
#include <AVSCommon/Utils/Metrics/MetricEventBuilder.h>
#include <AVSCommon/Utils/Metrics.h>
#include <AVSCommon/Utils/Logger/Logger.h>
@ -27,6 +30,13 @@ using namespace avsCommon::avs;
using namespace avsCommon::avs::attachment;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
using namespace avsCommon::utils::metrics;
/// Metric recorded when parse has been completed.
static const std::string PARSE_COMPLETE("PARSE_COMPLETE");
/// The metric activity name for parsing completed.
static const std::string PARSE_COMPLETE_ACTIVITY_NAME("MESSAGE_INTERPRETER-" + PARSE_COMPLETE);
/// String to identify log entries originating from this file.
static const std::string TAG("MessageInterpreter");
@ -41,10 +51,12 @@ static const std::string TAG("MessageInterpreter");
MessageInterpreter::MessageInterpreter(
std::shared_ptr<ExceptionEncounteredSenderInterface> exceptionEncounteredSender,
std::shared_ptr<DirectiveSequencerInterface> directiveSequencer,
std::shared_ptr<AttachmentManagerInterface> attachmentManager) :
std::shared_ptr<AttachmentManagerInterface> attachmentManager,
std::shared_ptr<MetricRecorderInterface> metricRecorder) :
m_exceptionEncounteredSender{exceptionEncounteredSender},
m_directiveSequencer{directiveSequencer},
m_attachmentManager{attachmentManager} {
m_attachmentManager{attachmentManager},
m_metricRecorder{metricRecorder} {
}
void MessageInterpreter::receive(const std::string& contextId, const std::string& message) {
@ -64,6 +76,24 @@ void MessageInterpreter::receive(const std::string& contextId, const std::string
return;
}
auto metricEvent =
MetricEventBuilder{}
.setActivityName(PARSE_COMPLETE_ACTIVITY_NAME)
.addDataPoint(DataPointCounterBuilder{}.setName(PARSE_COMPLETE).increment(1).build())
.addDataPoint(DataPointStringBuilder{}
.setName("HTTP2_STREAM")
.setValue(avsDirective->getAttachmentContextId())
.build())
.addDataPoint(
DataPointStringBuilder{}.setName("DIRECTIVE_MESSAGE_ID").setValue(avsDirective->getMessageId()).build())
.build();
if (metricEvent == nullptr) {
ACSDK_ERROR(LX("Error creating metric."));
return;
}
recordMetric(m_metricRecorder, metricEvent);
if (avsDirective->getName() == "StopCapture" || avsDirective->getName() == "Speak") {
ACSDK_METRIC_MSG(TAG, avsDirective, Metrics::Location::ADSL_ENQUEUE);
}

View File

@ -22,7 +22,6 @@
#include <gmock/gmock.h>
#include <AVSCommon/AVS/Attachment/AttachmentManager.h>
#include <AVSCommon/AVS/NamespaceAndName.h>
#include <AVSCommon/SDKInterfaces/MockDirectiveHandlerResult.h>
#include "ADSL/DirectiveRouter.h"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -16,25 +16,33 @@
#ifndef ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_CHANNEL_H_
#define ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_CHANNEL_H_
#include <algorithm>
#include <chrono>
#include <list>
#include <memory>
#include <string>
#include <AVSCommon/SDKInterfaces/ChannelObserverInterface.h>
#include <AVSCommon/AVS/ContentType.h>
#include <AVSCommon/AVS/FocusState.h>
#include <AVSCommon/SDKInterfaces/ChannelObserverInterface.h>
#include <AVSCommon/SDKInterfaces/FocusManagerInterface.h>
#include <AVSCommon/Utils/Timing/Timer.h>
namespace alexaClientSDK {
namespace afml {
/**
* A Channel represents a focusable layer with a priority, allowing the observer which has acquired the Channel to
* understand focus changes.
* understand focus changes. A Channel can be acquired by multiple Activities, but only a single Activity can be
* the primary activity. All other Activities that are not the primary Activity will be backgrounded.
*
* A Channel can optionally be virtual. Activity changes will not be captured for virtual
* channels.
*/
class Channel {
public:
/*
* This class contains the states of the @c Channel. The states inside this structure are intended to be shared via
* the @c ActivityTrackerInterface.
* This class contains the states of the @c Channel.
*/
struct State {
/// Constructor with @c Channel name as the parameter.
@ -64,8 +72,11 @@ public:
*
* @param name The channel's name.
* @param priority The priority of the channel.
* @param isVirtual bool to indicate if this channel is a virtual channel.
*
* @note refer to the class level doc on virtual channel paramter.
*/
Channel(const std::string& name, const unsigned int priority);
Channel(const std::string& name, const unsigned int priority, bool isVirtual = false);
/**
* Returns the name of a channel.
@ -82,28 +93,52 @@ public:
unsigned int getPriority() const;
/**
* Updates the focus and notifies the Channel's observer, if there is one, of the focus change. This method does
* not return until the ChannelObserverInterface##onFocusChanged() callback to the observer returns. If the focus
* @c NONE, the observer will be removed from the Channel.
* Updates the focus and notifies all activities associated with the @c Channel of the focus change. If @c
* FocusState is FOREGROUND, then the most recent Activity added to the Channel will be moved to foreground state,
* and all other Activites will remain in background state. This method does not return until the
* ChannelObserverInterface##onFocusChanged() callbacks to all the associated activities return. If the focus is @c
* NONE, the Activity will be removed from the Channel.
*
* @note the @c MixingBehavior should pertain to the primary Activity for the Channel.
*
* @param focus The focus of the Channel.
* @param behavior The MixingBehavior of the Channel.
* @param forceUpdate bool to indicate if the operation must be forced (irrespective of focus/behavior change).
* @return @c true if focus changed, else @c false.
*/
bool setFocus(avsCommon::avs::FocusState focus);
bool setFocus(avsCommon::avs::FocusState focus, avsCommon::avs::MixingBehavior behavior, bool forceUpdate = false);
/**
* Sets a new observer.
* Set the Primary Activity on the @c Channel given the Activity Object. The function must be called after the
* correct FocusState of the Channel is set. Any other Activities on the Channel will be moved to the background
* state.
*
* @param observer The observer of the Channel.
* @param activity @c Activity to be set as Primary Activity.
*/
void setObserver(std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> observer);
void setPrimaryActivity(std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activity);
/**
* Checks whether the Channel has an observer.
* Activity to be released by observer.
*
* @return @c true if the Channel has an observer, else @c false.
* @param observer Channel Observer to release.
* @return @c true if Activity was found and released, @c false otherwise.
*/
bool hasObserver() const;
bool releaseActivity(std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> observer);
/**
* Activity to be released by interface.
*
* @param interfaceName The interface name of the Activity to release.
*/
bool releaseActivity(const std::string& interfaceName);
/**
* Returns if channel is active.
* A Channel is considered active if there is any Activity on the Channel.
*
* @return @c true if Channel is active, @c false otherwise.
*/
bool isActive();
/**
* Compares this Channel and another Channel and checks which is higher priority. A Channel is considered higher
@ -114,27 +149,12 @@ public:
bool operator>(const Channel& rhs) const;
/**
* Updates the AVS interface occupying the Channel.
*
* @param interface The name of the interface occupying the Channel.
*/
void setInterface(const std::string& interface);
/**
* Returns the name of the AVS interface occupying the Channel.
* Returns the name of the interface of the Primary Activity occupying the Channel.
*
* @return The name of the AVS interface.
*/
std::string getInterface() const;
/**
* Checks whether the observer passed in currently owns the Channel.
*
* @param observer The observer to check.
* @return @c true if the observer currently owns the Channel and @c false otherwise.
*/
bool doesObserverOwnChannel(std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> observer) const;
/**
* Returns the @c State of the @c Channel.
*
@ -142,17 +162,131 @@ public:
*/
Channel::State getState() const;
/**
* Returns a list of Channel::State updates. This API will internally clear
* the stored updates when called.
*
* @return vector containing all Channel::State Updates
*/
std::vector<Channel::State> getActivityUpdates();
/**
* Returns the primary activity associated with this channel
*
* @return the primary activity associated with the channel.
*/
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> getPrimaryActivity();
/**
* Gets the activity associated with a given interfaceName.
*
* @param interfaceName interface name associated with the activity being searched
* @return activity associated with @param interfaceName, nullptr if not found.
*/
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> getActivity(
const std::string& interfaceName);
/**
* Retrieve vector of interfaces of all Activites on the Channel.
*/
std::vector<std::string> getInterfaceList() const;
private:
/**
* Notify all the Activities associated with the @c Channel. All activities that are not
* the primary activity, will be passed MixingBehavior::MUST_PAUSE. The primary activity shall
* receive the behavior passed as input to the API.
*
* @param behavior MixingBehavior to notify the activities with.
* @param focusState FocusState to notify the activities with.
*/
void notifyActivities(avsCommon::avs::MixingBehavior behavior, avsCommon::avs::FocusState focusState);
/**
* Activity to be released.
*
* @param activityToRelease The Activity to be released from the Channel.
* @return bool true if the operation was successful, otherwise false.
*/
bool releaseActivityLocked(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activityToRelease);
/**
* Helper to remove Activity from the list, also update Channel::State updates as required.
*
* @param activity Activity Iterator to remove.
* @return The @c true if successful and @c false otherwise.
*/
bool removeActivityHelperLocked(
std::list<std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity>>::iterator
activityToRemoveIt);
/**
* Adds a Channel State entry to Channel::State updates with current m_state with a specified
* focusState and interface name.
*
* @param interfaceName Interface Name to add to the Channel::State updates.
* @param focusState FocusState to add to the Channel::State updates.
*/
void addToChannelUpdatesLocked(const std::string& interfaceName, avsCommon::avs::FocusState focusState);
/**
* Given incoming and current activity, process the policies to either release, not release,
* or start patience timer for the current Activity.
*
* @param incomingActivity incoming Activity.
* @param currentActivity current Activity.
*/
void processPolicyLocked(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> incomingActivity,
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> currentActivity);
/**
* Patience Timer Callback. Function is to be executed when patience timer expires.
*
* @param activity activity to release after timer is expired.
*/
void patienceTimerCallback(std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activity);
/**
* Update Channel Interface with the Activity at top of stack.
*/
void updateChannelInterfaceLocked();
/**
* Returns the primary activity associated with this channel in a locked context.
*
* @return the primary activity associated with the channel.
*/
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> getPrimaryActivityLocked() const;
private:
/// The priority of the Channel.
const unsigned int m_priority;
/// Flag to indicate if this is a virtual channel
bool m_isVirtual;
/// The @c State of the @c Channel.
State m_state;
/// The current observer of the Channel.
std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> m_observer;
};
/// The list to hold shared pointer to all Activities.
std::list<std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity>> m_activities;
/// Mutex to protect m_state and m_activities.
mutable std::mutex m_mutex;
/// The vector to hold activity updates for the Channel.
std::vector<Channel::State> m_activityUpdates;
/// A timer task to trace the patience.
avsCommon::utils::timing::Timer m_patienceTimer;
/// Activity to track the initiator of patience.
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> m_patienceInitiator;
/// Activity to track the receiver of patience.
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> m_patienceReceiver;
};
} // namespace afml
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -16,6 +16,7 @@
#ifndef ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_FOCUSMANAGER_H_
#define ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_FOCUSMANAGER_H_
#include <algorithm>
#include <map>
#include <mutex>
#include <set>
@ -29,6 +30,7 @@
#include "AFML/Channel.h"
#include "AFML/ActivityTrackerInterface.h"
#include "AVSCommon/Utils/Threading/Executor.h"
#include "InterruptModel/InterruptModel.h"
namespace alexaClientSDK {
namespace afml {
@ -56,7 +58,8 @@ public:
* The configuration used by the FocusManager to create Channel objects. Each configuration object has a
* name and priority.
*/
struct ChannelConfiguration {
class ChannelConfiguration {
public:
/**
* Constructor.
*
@ -83,6 +86,18 @@ public:
/// The priority of the channel.
unsigned int priority;
/**
* Get the virtual channel configurations.
*
* @param channelTypeKey The key of the virtual channel configuration to get.
* @param[out] virtualChannelConfiguration The @c ChannelConfiguration for the virtual channels as specified in
* @c channelTypeKey. An empty vector if there is no such info on the configuration file.
* @return true if there's no error, false otherwise.
*/
static bool readChannelConfiguration(
const std::string& channelTypeKey,
std::vector<afml::FocusManager::ChannelConfiguration>* virtualChannelConfigurations);
};
/**
@ -93,15 +108,26 @@ public:
* same name or priority, the latter Channels with that name or priority will not be created.
* @param activityTrackerInterface The interface to notify the activity tracker a vector of channel states that has
* been updated.
* @param virtualChannelConfigurations A vector of @c channelConfiguration objects that will be used to create the
* Virtual Channels. No two Channels should have the same name or priority. If there are multiple configurations
* with the same name or priority, the latter Channels with that name or priority will not be created.
* @param interruptModel @c InterruptModel object that provides MixingBehavior inputs to ChannelObservers upon
* Focus State Change.
*/
FocusManager(
const std::vector<ChannelConfiguration> channelConfigurations,
std::shared_ptr<ActivityTrackerInterface> activityTrackerInterface = nullptr);
const std::vector<ChannelConfiguration>& channelConfigurations,
std::shared_ptr<ActivityTrackerInterface> activityTrackerInterface = nullptr,
const std::vector<ChannelConfiguration>& virtualChannelConfigurations = std::vector<ChannelConfiguration>(),
std::shared_ptr<interruptModel::InterruptModel> interruptModel = nullptr);
bool acquireChannel(
const std::string& channelName,
std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> channelObserver,
const std::string& interface) override;
const std::string& interfaceName) override;
bool acquireChannel(
const std::string& channelName,
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> channelActivity) override;
std::future<bool> releaseChannel(
const std::string& channelName,
@ -116,6 +142,11 @@ public:
void removeObserver(
const std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerObserverInterface>& observer) override;
void modifyContentType(
const std::string& channelName,
const std::string& interfaceName,
avsCommon::avs::ContentType contentType) override;
/**
* Retrieves the default @c ChannelConfiguration for AVS audio channels.
*
@ -150,26 +181,40 @@ private:
}
};
/**
* Helper function to read the @c ChannelConfiguration into @c m_allChannels. This function also ensures the name
* and priority for all channels are unique.
*
* @param channelConfigurations The @c channelConfigurations of the channels.
* @param isVirtual Whether the channels are virtual or not.
*/
void readChannelConfiguration(const std::vector<ChannelConfiguration>& channelConfigurations, bool isVirtual);
/**
* Sets the @c FocusState for @c channel and notifies observers of the change.
*
* @param channel The @c Channel to set the @c FocusState for.
* @param focus The @c FocusState to set @c channel to.
* @param behavior The @c MixingBehavior to set @c channel to.
* @param forceUpdate optional, if set to true this function will update
* activitytracker context (even if focus/behavior did not change).
*/
void setChannelFocus(const std::shared_ptr<Channel>& channel, avsCommon::avs::FocusState focus);
void setChannelFocus(
const std::shared_ptr<Channel>& channel,
avsCommon::avs::FocusState focus,
avsCommon::avs::MixingBehavior behavior,
bool forceUpdate = false);
/**
* Grants access to the Channel specified and updates other Channels as needed. This function provides the full
* implementation which the public method will call.
*
* @param channelToAcquire The Channel to acquire.
* @param channelObserver The new observer of the Channel.
* @param interface The name of the AVS inferface on the Channel.
* @param channelActivity The Activity to acquire.
*/
void acquireChannelHelper(
std::shared_ptr<Channel> channelToAcquire,
std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> channelObserver,
const std::string& interface);
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> channelActivity);
/**
* Releases the Channel specified and updates other Channels as needed. This function provides the full
@ -261,6 +306,30 @@ private:
*/
void notifyActivityTracker();
/**
* Get the mixing behavior for ChannelObserver associated with a low priority channel , when a high priority channel
* barges in
*
* @param lowPrioChannel channel with the lower priority
* @param highPrioChannel channel with the higher priority
* @return MixingBehavior to be taken by the ChannelObserver associated with the lowPrioChannel
*/
avsCommon::avs::MixingBehavior getMixingBehavior(
std::shared_ptr<Channel> lowPrioChannel,
std::shared_ptr<Channel> highPrioChannel);
/**
* This function determines the mixingBehavior for each backgrounded channel, when the @param foregroundChannel is
* in Foreground. It also invokes the ChannelObserverInterface::onFocusChanged callback for each backgrounded
* channel.
*
* @param foregroundChannel the channel currently holding foreground focus
*/
void setBackgroundChannelMixingBehavior(std::shared_ptr<Channel> foregroundChannel);
/// Mutex used to lock m_activeChannels, m_observers and Channels' interface name.
std::mutex m_mutex;
/// Map of channel names to shared_ptrs of Channel objects and contains every channel.
std::unordered_map<std::string, std::shared_ptr<Channel>> m_allChannels;
@ -270,9 +339,6 @@ private:
/// The set of observers to notify about focus changes.
std::unordered_set<std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerObserverInterface>> m_observers;
/// Mutex used to lock m_activeChannels, m_observers and Channels' interface name.
std::mutex m_mutex;
/*
* A vector of channel's State that has been updated due to @c acquireChannel(), @c releaseChannel() or
* stopForegroundActivity(). This is accessed by functions in the @c m_executor worker thread, and do not require
@ -283,6 +349,9 @@ private:
/// The interface to notify its activity tracker of any changes to its channels.
std::shared_ptr<ActivityTrackerInterface> m_activityTracker;
/// The interrupt Model associated with the focus manager
std::shared_ptr<interruptModel::InterruptModel> m_interruptModel;
/**
* @c Executor which queues up operations from asynchronous API calls.
*

View File

@ -5,8 +5,8 @@ add_library(AFML SHARED
VisualActivityTracker.cpp)
add_definitions("-DACSDK_LOG_MODULE=afml")
include_directories(AFML "${AFML_SOURCE_DIR}/include")
target_link_libraries(AFML AVSCommon)
include_directories(AFML "${AFML_SOURCE_DIR}/include" "{InterruptModel_SOURCE_DIR}/include")
target_link_libraries(AFML AVSCommon InterruptModel)
# install target
asdk_install()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -14,6 +14,7 @@
*/
#include "AFML/Channel.h"
#include <AVSCommon/Utils/Logger/Logger.h>
namespace alexaClientSDK {
namespace afml {
@ -21,6 +22,16 @@ namespace afml {
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
/// String to identify log entries originating from this file.
static const std::string TAG("Channel");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
Channel::State::State(const std::string& name) :
name{name},
focusState{FocusState::NONE},
@ -30,10 +41,10 @@ Channel::State::State(const std::string& name) :
Channel::State::State() : focusState{FocusState::NONE}, timeAtIdle{std::chrono::steady_clock::now()} {
}
Channel::Channel(const std::string& name, const unsigned int priority) :
Channel::Channel(const std::string& name, const unsigned int priority, bool isVirtual) :
m_priority{priority},
m_state{name},
m_observer{nullptr} {
m_isVirtual{isVirtual},
m_state{name} {
}
const std::string& Channel::getName() const {
@ -44,50 +55,291 @@ unsigned int Channel::getPriority() const {
return m_priority;
}
bool Channel::setFocus(FocusState focus) {
if (focus == m_state.focusState) {
bool Channel::setFocus(FocusState focus, MixingBehavior behavior, bool forceUpdate) {
std::unique_lock<std::mutex> lock(m_mutex);
bool focusChanged = (m_state.focusState != focus);
auto primaryActivity = getPrimaryActivityLocked();
bool mixingBehaviorChanged = primaryActivity && (primaryActivity->getMixingBehavior() != behavior);
if (!forceUpdate && !focusChanged && !mixingBehaviorChanged) {
return false;
}
ACSDK_DEBUG5(LX(__func__)
.d("name", m_state.name)
.d("newfocusState", focus)
.d("prevfocusState", m_state.focusState)
.d("newMixingBehavior", behavior)
.d("forceUpdate", forceUpdate));
m_state.focusState = focus;
if (m_observer) {
m_observer->onFocusChanged(m_state.focusState);
}
if (FocusState::NONE == m_state.focusState) {
m_observer = nullptr;
if (m_state.focusState == FocusState::NONE) {
m_state.timeAtIdle = std::chrono::steady_clock::now();
}
// Update Channel State Updates
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
lock.unlock();
// Notify all activities of the new focus state for this channel
notifyActivities(behavior, focus);
return true;
}
void Channel::setObserver(std::shared_ptr<ChannelObserverInterface> observer) {
m_observer = observer;
void Channel::setPrimaryActivity(std::shared_ptr<FocusManagerInterface::Activity> activity) {
std::unique_lock<std::mutex> lock(m_mutex);
if (!activity) {
ACSDK_ERROR(LX("setPrimaryActivityFailed").m("Null Activity"));
return;
}
ACSDK_DEBUG5(LX(__func__).d("Interface", activity->getInterface()));
if (!m_activities.empty()) {
processPolicyLocked(activity, m_activities.front());
}
// Establish the activity.
m_activities.push_front(activity);
updateChannelInterfaceLocked();
}
bool Channel::hasObserver() const {
return m_observer != nullptr;
bool Channel::releaseActivity(std::shared_ptr<ChannelObserverInterface> observer) {
if (observer == nullptr) {
ACSDK_ERROR(LX("releaseActivityFailed").d("reason", "observer is null."));
return false;
}
std::unique_lock<std::mutex> lock(m_mutex);
for (auto it = m_activities.begin(); it != m_activities.end(); ++it) {
if ((*it)->getChannelObserver() == observer) {
bool success = removeActivityHelperLocked(it);
if (success) {
// No change in observer or activity if remove fails.
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
}
return success;
}
}
ACSDK_DEBUG0(LX("releaseActivityFailed").m("Observer not found"));
return false;
}
bool Channel::releaseActivity(const std::string& interfaceName) {
std::unique_lock<std::mutex> lock(m_mutex);
for (auto it = m_activities.begin(); it != m_activities.end(); ++it) {
if ((*it)->getInterface() == interfaceName) {
bool success = removeActivityHelperLocked(it);
if (success) {
// Update Channel State Updates.
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
}
return success;
}
}
return false;
}
bool Channel::isActive() {
std::unique_lock<std::mutex> lock(m_mutex);
return !m_activities.empty();
}
bool Channel::operator>(const Channel& rhs) const {
return m_priority < rhs.getPriority();
}
void Channel::setInterface(const std::string& interface) {
m_state.interfaceName = interface;
}
std::string Channel::getInterface() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_state.interfaceName;
}
bool Channel::doesObserverOwnChannel(std::shared_ptr<ChannelObserverInterface> observer) const {
return observer == m_observer;
}
Channel::State Channel::getState() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_state;
}
std::vector<Channel::State> Channel::getActivityUpdates() {
std::unique_lock<std::mutex> lock(m_mutex);
auto activityUpdatesRet = m_activityUpdates;
m_activityUpdates.clear();
return activityUpdatesRet;
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getPrimaryActivity() {
std::unique_lock<std::mutex> lock(m_mutex);
return getPrimaryActivityLocked();
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getPrimaryActivityLocked() const {
if (m_activities.empty()) {
return nullptr;
}
return *(m_activities.begin());
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getActivity(const std::string& interfaceName) {
std::unique_lock<std::mutex> lock(m_mutex);
for (const auto& it : m_activities) {
if (it->getInterface() == interfaceName) return it;
}
return nullptr;
}
std::vector<std::string> Channel::getInterfaceList() const {
std::vector<std::string> listOfInterface = {};
for (const auto& activity : m_activities) {
listOfInterface.push_back(activity->getInterface());
}
return listOfInterface;
}
void Channel::notifyActivities(MixingBehavior behavior, FocusState focusState) {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_activities.empty()) {
ACSDK_WARN(LX("notifyActivitiesFailed").m("No Associated Activities Found"));
return;
}
auto activitiesCopy = m_activities;
lock.unlock();
auto activityIt = activitiesCopy.begin();
// inform the primary activity with the MixingBehavior
(*activityIt)->notifyObserver(focusState, behavior);
activityIt++;
// all other secondary activities must be PAUSED + BACKGROUND
for (; activityIt != activitiesCopy.end(); activityIt++) {
(*activityIt)->notifyObserver(FocusState::BACKGROUND, MixingBehavior::MUST_PAUSE);
}
}
bool Channel::releaseActivityLocked(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activityToRelease) {
if (!activityToRelease) {
ACSDK_ERROR(LX("releaseActivityLockedFailed").m("Null activityToRelease"));
return false;
}
auto priorActivityIt = std::find(m_activities.begin(), m_activities.end(), activityToRelease);
if (priorActivityIt != m_activities.end()) {
return removeActivityHelperLocked(priorActivityIt);
}
return false;
}
bool Channel::removeActivityHelperLocked(
std::list<std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity>>::iterator
activityToRemoveIt) {
ACSDK_DEBUG5(LX(__func__).d("interface", (*activityToRemoveIt)->getInterface()));
auto isRemovingPatienceReceiver = false;
if (m_patienceReceiver == *activityToRemoveIt) {
isRemovingPatienceReceiver = true;
}
// Timer is active AND the job being removed is one of the patience Activities.
if (m_patienceTimer.isActive() &&
(m_patienceInitiator == *activityToRemoveIt || m_patienceReceiver == *activityToRemoveIt)) {
m_patienceTimer.stop();
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Timer Stopped"));
m_patienceInitiator = nullptr;
m_patienceReceiver = nullptr;
}
// If the activity to remove is the last activity, then update the time at idle for this channel
if (m_activities.size() == 1) {
m_state.timeAtIdle = std::chrono::steady_clock::now();
}
// No need to update Channel updates for removing patience receiver.
if (!isRemovingPatienceReceiver) {
// Add the current State and override the FocusState with NONE.
addToChannelUpdatesLocked((*activityToRemoveIt)->getInterface(), FocusState::NONE);
}
// Report NONE only to the single Activity that is removed
(*activityToRemoveIt)->notifyObserver(FocusState::NONE, MixingBehavior::MUST_STOP);
m_activities.erase(activityToRemoveIt);
updateChannelInterfaceLocked();
return true;
}
void Channel::addToChannelUpdatesLocked(const std::string& interfaceName, avsCommon::avs::FocusState focusState) {
if (m_state.interfaceName.empty()) {
return;
}
// Only add to Channel updates if channel is not virtual.
if (!m_isVirtual) {
auto state = m_state;
state.focusState = focusState;
state.interfaceName = interfaceName;
m_activityUpdates.push_back(state);
ACSDK_DEBUG0(LX(__func__).d("interface", state.interfaceName).d("focusState", state.focusState));
}
}
void Channel::processPolicyLocked(
std::shared_ptr<FocusManagerInterface::Activity> incomingActivity,
std::shared_ptr<FocusManagerInterface::Activity> currentActivity) {
if (!incomingActivity || !currentActivity) {
ACSDK_ERROR(LX("processPolicyLockedFailed").m("Null Activities"));
return;
}
if (incomingActivity->getInterface() == currentActivity->getInterface()) {
// Both incoming and current activity has identical interface. Remove the current activity regardless of policy.
releaseActivityLocked(currentActivity);
return;
}
if (m_patienceTimer.isActive()) {
// A new Activity is incoming. If there is an ongoing patience release, remove the receiver immediately.
// Any persistent initiator or receiver will not be removed.
m_patienceTimer.stop();
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Release Timer Stopped"));
releaseActivityLocked(m_patienceReceiver);
m_patienceReceiver = nullptr;
}
if (incomingActivity->getPatienceDuration().count() > 0) {
// Incoming valid patience Activity
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Timer Started"));
addToChannelUpdatesLocked(currentActivity->getInterface(), FocusState::NONE);
auto patienceDuration = incomingActivity->getPatienceDuration();
m_patienceTimer.start(patienceDuration, std::bind(&Channel::patienceTimerCallback, this, currentActivity));
m_patienceInitiator = incomingActivity;
m_patienceReceiver = currentActivity;
} else {
if (m_patienceInitiator != nullptr) {
// No valid patience duration, release the initiator.
releaseActivityLocked(m_patienceInitiator);
m_patienceInitiator = nullptr;
}
releaseActivityLocked(currentActivity);
}
}
void Channel::patienceTimerCallback(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activity) {
std::unique_lock<std::mutex> lock(m_mutex);
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Release Timer Triggered"));
releaseActivityLocked(std::move(activity));
// No need to modify channel updates since it was Already reported
// when patienceInitiator came into focus.
}
void Channel::updateChannelInterfaceLocked() {
if (!m_activities.empty()) {
m_state.interfaceName = m_activities.front()->getInterface();
} else {
m_state.interfaceName = "";
}
}
} // namespace afml
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -13,6 +13,8 @@
* permissions and limitations under the License.
*/
#include <algorithm>
#include "AFML/FocusManager.h"
#include <AVSCommon/Utils/Logger/Logger.h>
@ -22,10 +24,20 @@ namespace afml {
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
using namespace avsCommon::avs;
using namespace interruptModel;
/// String to identify log entries originating from this file.
static const std::string TAG("FocusManager");
/// Key for @c FocusManager configurations in configuration node.
static const std::string VIRTUAL_CHANNELS_CONFIG_KEY = "virtualChannels";
/// Key for the name of the channel in configuration node.
static const std::string CHANNEL_NAME_KEY = "name";
/// Key for the priority of the channel in configuration node.
static const std::string CHANNEL_PRIORITY_KEY = "priority";
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
@ -34,38 +46,57 @@ static const std::string TAG("FocusManager");
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
FocusManager::FocusManager(
const std::vector<ChannelConfiguration> channelConfigurations,
std::shared_ptr<ActivityTrackerInterface> activityTrackerInterface) :
m_activityTracker{activityTrackerInterface} {
for (auto config : channelConfigurations) {
if (doesChannelNameExist(config.name)) {
ACSDK_ERROR(LX("createChannelFailed").d("reason", "channelNameExists").d("config", config.toString()));
continue;
}
if (doesChannelPriorityExist(config.priority)) {
ACSDK_ERROR(LX("createChannelFailed").d("reason", "channelPriorityExists").d("config", config.toString()));
continue;
}
auto channel = std::make_shared<Channel>(config.name, config.priority);
m_allChannels.insert({config.name, channel});
}
const std::vector<ChannelConfiguration>& channelConfigurations,
std::shared_ptr<ActivityTrackerInterface> activityTrackerInterface,
const std::vector<ChannelConfiguration>& virtualChannelConfigurations,
std::shared_ptr<InterruptModel> interruptModel) :
m_activityTracker{activityTrackerInterface},
m_interruptModel{interruptModel} {
// Read AVS channel configurations.
readChannelConfiguration(channelConfigurations, false);
// Read virtual channel configurations.
readChannelConfiguration(virtualChannelConfigurations, true);
}
bool FocusManager::acquireChannel(
const std::string& channelName,
std::shared_ptr<ChannelObserverInterface> channelObserver,
const std::string& interface) {
ACSDK_DEBUG1(LX("acquireChannel").d("channelName", channelName).d("interface", interface));
const std::string& interfaceName) {
ACSDK_DEBUG1(LX("acquireChannel").d("channelName", channelName).d("interface", interfaceName));
std::shared_ptr<Channel> channelToAcquire = getChannel(channelName);
if (!channelToAcquire) {
ACSDK_ERROR(LX("acquireChannelFailed").d("reason", "channelNotFound").d("channelName", channelName));
return false;
}
m_executor.submit([this, channelToAcquire, channelObserver, interface]() {
acquireChannelHelper(channelToAcquire, channelObserver, interface);
});
auto channelActivity = FocusManagerInterface::Activity::create(interfaceName, channelObserver);
if (!channelActivity) {
ACSDK_ERROR(LX("acquireChannelFailed").d("reason", "failedToCreateActivity").d("interface", interfaceName));
return false;
}
m_executor.submit(
[this, channelToAcquire, channelActivity]() { acquireChannelHelper(channelToAcquire, channelActivity); });
return true;
}
bool FocusManager::acquireChannel(
const std::string& channelName,
std::shared_ptr<FocusManagerInterface::Activity> channelActivity) {
ACSDK_DEBUG1(LX("acquireChannel").d("channelName", channelName).d("interface", channelActivity->getInterface()));
std::shared_ptr<Channel> channelToAcquire = getChannel(channelName);
if (!channelToAcquire) {
ACSDK_ERROR(LX("acquireChannelFailed").d("reason", "channelNotFound").d("channelName", channelName));
return false;
}
if (!channelActivity) {
ACSDK_ERROR(LX("acquireChannelFailed").d("reason", "channelActivityIsNull"));
return false;
}
m_executor.submit(
[this, channelToAcquire, channelActivity]() { acquireChannelHelper(channelToAcquire, channelActivity); });
return true;
}
@ -120,7 +151,9 @@ void FocusManager::stopAllActivities() {
std::unique_lock<std::mutex> lock(m_mutex);
for (const auto& channel : m_activeChannels) {
channelOwnersCapture.insert(std::pair<std::shared_ptr<Channel>, std::string>(channel, channel->getInterface()));
for (const auto& interfaceName : channel->getInterfaceList()) {
channelOwnersCapture.insert(std::pair<std::shared_ptr<Channel>, std::string>(channel, interfaceName));
}
}
lock.unlock();
@ -138,46 +171,125 @@ void FocusManager::removeObserver(const std::shared_ptr<FocusManagerObserverInte
m_observers.erase(observer);
}
void FocusManager::setChannelFocus(const std::shared_ptr<Channel>& channel, FocusState focus) {
if (!channel->setFocus(focus)) {
void FocusManager::readChannelConfiguration(
const std::vector<ChannelConfiguration>& channelConfigurations,
bool isVirtual) {
for (const auto& config : channelConfigurations) {
if (doesChannelNameExist(config.name)) {
ACSDK_ERROR(
LX("readChannelConfigurationFailed").d("reason", "channelNameExists").d("config", config.toString()));
continue;
}
if (doesChannelPriorityExist(config.priority)) {
ACSDK_ERROR(LX("readChannelConfigurationFailed")
.d("reason", "channelPriorityExists")
.d("config", config.toString()));
continue;
}
auto channel = std::make_shared<Channel>(config.name, config.priority, isVirtual);
m_allChannels.insert({config.name, channel});
}
}
void FocusManager::setChannelFocus(
const std::shared_ptr<Channel>& channel,
FocusState focus,
MixingBehavior behavior,
bool forceUpdate) {
if (!channel->setFocus(focus, behavior, forceUpdate)) {
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
// take a copy of the observers
auto observers = m_observers;
lock.unlock();
// inform copy of the observers in an unlocked content
for (auto& observer : observers) {
observer->onFocusChanged(channel->getName(), focus);
}
m_activityUpdates.push_back(channel->getState());
}
MixingBehavior FocusManager::getMixingBehavior(
std::shared_ptr<Channel> lowPrioChannel,
std::shared_ptr<Channel> highPrioChannel) {
if (!m_interruptModel) {
ACSDK_ERROR(LX(__func__).m("Null InterruptModel"));
return MixingBehavior::UNDEFINED;
}
if (!lowPrioChannel || !highPrioChannel) {
ACSDK_ERROR(LX("getMixingBehaviorFailed").d("reason", "nullInputChannels"));
return MixingBehavior::UNDEFINED;
}
if (*lowPrioChannel > *highPrioChannel) {
ACSDK_ERROR(LX("getMixingBehaviorFailed")
.d("reason", "Priorities of input channels violate API contract")
.d("lowPrioChannel priority", lowPrioChannel->getPriority())
.d("highPrioChannel priority", highPrioChannel->getPriority()));
return MixingBehavior::UNDEFINED;
}
auto lowPrioChannelName = lowPrioChannel->getName();
auto lowPrioChannelPrimaryActivity = lowPrioChannel->getPrimaryActivity();
if (!lowPrioChannelPrimaryActivity) {
ACSDK_ERROR(LX("getMixingBehaviorFailed").d("No PrimaryActivity on lowPrioChannel", lowPrioChannelName));
return MixingBehavior::UNDEFINED;
}
auto highPrioChannelName = highPrioChannel->getName();
auto highPrioChannelPrimaryActivity = highPrioChannel->getPrimaryActivity();
if (!highPrioChannelPrimaryActivity) {
ACSDK_ERROR(LX("getMixingBehaviorFailed").d("No PrimaryActivity on highPrioChannel", highPrioChannelName));
return MixingBehavior::UNDEFINED;
}
return m_interruptModel->getMixingBehavior(
lowPrioChannelName,
lowPrioChannelPrimaryActivity->getContentType(),
highPrioChannelName,
highPrioChannelPrimaryActivity->getContentType());
}
void FocusManager::acquireChannelHelper(
std::shared_ptr<Channel> channelToAcquire,
std::shared_ptr<ChannelObserverInterface> channelObserver,
const std::string& interface) {
// Notify the old observer, if there is one, that it lost focus.
setChannelFocus(channelToAcquire, FocusState::NONE);
std::shared_ptr<FocusManagerInterface::Activity> channelActivity) {
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
std::shared_ptr<Channel> foregroundChannel = getHighestPriorityActiveChannelLocked();
channelToAcquire->setInterface(interface);
// insert the incoming channel
m_activeChannels.insert(channelToAcquire);
lock.unlock();
// Set the new observer.
channelToAcquire->setObserver(channelObserver);
ACSDK_DEBUG5(LX(__func__)
.d("incomingChannel", channelToAcquire->getName())
.d("incomingInterface", channelActivity->getInterface()));
// attach Activity to the Channel
channelToAcquire->setPrimaryActivity(std::move(channelActivity));
if (!foregroundChannel) {
setChannelFocus(channelToAcquire, FocusState::FOREGROUND);
// channelToAcquire is the only active channel
setChannelFocus(channelToAcquire, FocusState::FOREGROUND, MixingBehavior::PRIMARY);
} else if (foregroundChannel == channelToAcquire) {
setChannelFocus(channelToAcquire, FocusState::FOREGROUND);
// acquireChannel request is for the same channel as the current foreground channel
// NOTE : the primaryActivity interface may change , even though focus state has not changed for the channel
setChannelFocus(channelToAcquire, FocusState::FOREGROUND, MixingBehavior::PRIMARY, true);
} else if (*channelToAcquire > *foregroundChannel) {
setChannelFocus(foregroundChannel, FocusState::BACKGROUND);
setChannelFocus(channelToAcquire, FocusState::FOREGROUND);
// channelToAcquire will now become the foreground channel, other channels shall be backgrounded
// For each background channel : consult interrupt model to determine the mixability
setBackgroundChannelMixingBehavior(channelToAcquire);
// set channelToAcquire as Foreground
setChannelFocus(channelToAcquire, FocusState::FOREGROUND, MixingBehavior::PRIMARY);
} else {
setChannelFocus(channelToAcquire, FocusState::BACKGROUND);
// channelToAcquire is to be backgrounded
auto mixingBehavior = getMixingBehavior(channelToAcquire, foregroundChannel);
setChannelFocus(channelToAcquire, FocusState::BACKGROUND, mixingBehavior);
}
notifyActivityTracker();
}
@ -186,23 +298,28 @@ void FocusManager::releaseChannelHelper(
std::shared_ptr<ChannelObserverInterface> channelObserver,
std::shared_ptr<std::promise<bool>> releaseChannelSuccess,
const std::string& name) {
if (!channelToRelease->doesObserverOwnChannel(channelObserver)) {
ACSDK_ERROR(LX("releaseChannelHelperFailed").d("reason", "observerDoesNotOwnChannel").d("channel", name));
releaseChannelSuccess->set_value(false);
ACSDK_DEBUG5(LX(__func__).d("channelToRelease", channelToRelease->getName()));
bool success = channelToRelease->releaseActivity(std::move(channelObserver));
releaseChannelSuccess->set_value(success);
if (!success) {
ACSDK_ERROR(LX(__func__)
.d("reason", "releaseActivityFailed")
.d("channel", channelToRelease)
.d("interface", channelToRelease->getInterface()));
return;
}
releaseChannelSuccess->set_value(true);
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
bool wasForegrounded = isChannelForegroundedLocked(channelToRelease);
m_activeChannels.erase(channelToRelease);
lock.unlock();
setChannelFocus(channelToRelease, FocusState::NONE);
if (wasForegrounded) {
foregroundHighestPriorityActiveChannel();
// Only release and set entire channel focus to NONE if there are no active Activity remaining.
if (!channelToRelease->isActive()) {
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
m_activeChannels.erase(channelToRelease);
lock.unlock();
setChannelFocus(channelToRelease, FocusState::NONE, MixingBehavior::MUST_STOP);
}
foregroundHighestPriorityActiveChannel();
notifyActivityTracker();
}
@ -212,15 +329,24 @@ void FocusManager::stopForegroundActivityHelper(
if (foregroundChannelInterface != foregroundChannel->getInterface()) {
return;
}
if (!foregroundChannel->hasObserver()) {
return;
ACSDK_DEBUG5(LX(__func__).d("interface", foregroundChannelInterface));
bool success = foregroundChannel->releaseActivity(foregroundChannel->getInterface());
if (!success) {
ACSDK_ERROR(LX(__func__)
.d("reason", "releaseActivityFailed")
.d("channel", foregroundChannel)
.d("interface", foregroundChannel->getInterface()));
}
setChannelFocus(foregroundChannel, FocusState::NONE);
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
m_activeChannels.erase(foregroundChannel);
lock.unlock();
// Only release and set entire channel focus to NONE if there are no active Activity remaining.
if (!foregroundChannel->isActive()) {
ACSDK_DEBUG1(LX(__func__).m("Channel is not active ... releasing"));
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
m_activeChannels.erase(foregroundChannel);
lock.unlock();
setChannelFocus(foregroundChannel, FocusState::NONE, MixingBehavior::MUST_STOP);
}
foregroundHighestPriorityActiveChannel();
notifyActivityTracker();
}
@ -233,22 +359,29 @@ void FocusManager::stopAllActivitiesHelper(const ChannelsToInterfaceNamesMap& ch
std::unique_lock<std::mutex> lock(m_mutex);
for (const auto& channelAndInterface : channelsOwnersMap) {
if (channelAndInterface.first->getInterface() == channelAndInterface.second) {
m_activeChannels.erase(channelAndInterface.first);
channelsToClear.insert(channelAndInterface.first);
} else {
ACSDK_INFO(LX(__func__)
.d("reason", "channel has other ownership")
.d("channel", channelAndInterface.first->getName())
.d("currentInterface", channelAndInterface.first->getInterface())
.d("originalInterface", channelAndInterface.second));
auto channel = channelAndInterface.first;
auto interfaceName = channelAndInterface.second;
ACSDK_DEBUG3(LX(__func__).d("channel", channel).d("interface", interfaceName));
bool success = channel->releaseActivity(channelAndInterface.second);
if (!success) {
ACSDK_ERROR(
LX(__func__).d("reason", "releaseActivityFailed").d("channel", channel).d("interface", interfaceName));
}
channelsToClear.insert(channel);
}
lock.unlock();
for (const auto& channel : channelsToClear) {
setChannelFocus(channel, FocusState::NONE);
// Only release and set entire channel focus to NONE if there are no active Activity remaining.
if (!channel->isActive()) {
// Lock here to update internal state which stopForegroundActivity may concurrently access.
std::unique_lock<std::mutex> lock(m_mutex);
m_activeChannels.erase(channel);
lock.unlock();
setChannelFocus(channel, FocusState::NONE, MixingBehavior::MUST_STOP);
}
}
foregroundHighestPriorityActiveChannel();
notifyActivityTracker();
@ -278,26 +411,97 @@ bool FocusManager::doesChannelNameExist(const std::string& name) const {
}
bool FocusManager::doesChannelPriorityExist(const unsigned int priority) const {
for (auto it = m_allChannels.begin(); it != m_allChannels.end(); ++it) {
if (it->second->getPriority() == priority) {
for (const auto& m_allChannel : m_allChannels) {
if (m_allChannel.second->getPriority() == priority) {
return true;
}
}
return false;
}
void FocusManager::modifyContentType(
const std::string& channelName,
const std::string& interfaceName,
ContentType contentType) {
// find the channel
auto channel = getChannel(channelName);
if (!channel) {
ACSDK_ERROR(LX("modifyContentTypeFailed").d("reason", "channelNotFound").d("channel", channelName));
return;
}
// find the activity associated with the interfacename in the channel
auto activity = channel->getActivity(interfaceName);
if (!activity) {
ACSDK_ERROR(LX("modifyContentTypeFailed").d("no activity found associated with interfaceName", interfaceName));
return;
}
if (contentType == activity->getContentType()) {
ACSDK_WARN(LX("modifyContentTypeFailed").d("no contentType to modify it is already identical: ", contentType));
return;
}
// modify the contentType associated with the activity
activity->setContentType(contentType);
// reconsult the InterruptModel and set the new MixingBehaviors for all backgrounded channelobservers
std::unique_lock<std::mutex> lock(m_mutex);
std::shared_ptr<Channel> foregroundChannel = getHighestPriorityActiveChannelLocked();
lock.unlock();
setBackgroundChannelMixingBehavior(foregroundChannel);
}
void FocusManager::setBackgroundChannelMixingBehavior(std::shared_ptr<Channel> foregroundChannel) {
std::unique_lock<std::mutex> lock(m_mutex);
auto channelIter = m_activeChannels.find(foregroundChannel);
if (channelIter == m_activeChannels.end()) {
ACSDK_ERROR(
LX("setBackgroundChannelMixingBehaviorFailed").d("Could not find channel", foregroundChannel->getName()));
return;
}
// skip to the next channel in priority
channelIter++;
for (; channelIter != m_activeChannels.end(); channelIter++) {
// determine mixingBehavior for each background channel
auto mixingBehavior = getMixingBehavior(*channelIter, foregroundChannel);
lock.unlock();
setChannelFocus(*channelIter, FocusState::BACKGROUND, mixingBehavior);
lock.lock();
}
}
void FocusManager::foregroundHighestPriorityActiveChannel() {
std::unique_lock<std::mutex> lock(m_mutex);
std::shared_ptr<Channel> channelToForeground = getHighestPriorityActiveChannelLocked();
lock.unlock();
if (channelToForeground) {
setChannelFocus(channelToForeground, FocusState::FOREGROUND);
// inform background channels of the new MixingBehavior as per the new Foreground Channel
setBackgroundChannelMixingBehavior(channelToForeground);
// Foreground the highest priority channel
setChannelFocus(channelToForeground, FocusState::FOREGROUND, MixingBehavior::PRIMARY);
}
}
void FocusManager::notifyActivityTracker() {
if (m_activityTracker) {
std::unique_lock<std::mutex> lock(m_mutex);
for (const auto& channel : m_allChannels) {
auto activityUpdates = channel.second->getActivityUpdates();
for (const auto& activity : activityUpdates) {
m_activityUpdates.push_back(activity);
ACSDK_DEBUG1(LX(__func__)
.d("name", activity.name)
.d("interfaceName", activity.interfaceName)
.d("focusState", activity.focusState));
}
}
lock.unlock();
if (m_activityTracker && !m_activityUpdates.empty()) {
m_activityTracker->notifyOfActivityUpdates(m_activityUpdates);
}
m_activityUpdates.clear();
@ -320,5 +524,59 @@ const std::vector<FocusManager::ChannelConfiguration> FocusManager::getDefaultVi
return defaultVisualChannels;
}
bool afml::FocusManager::ChannelConfiguration::readChannelConfiguration(
const std::string& channelTypeKey,
std::vector<afml::FocusManager::ChannelConfiguration>* virtualChannelConfigurations) {
if (!virtualChannelConfigurations) {
ACSDK_ERROR(LX("readChannelConfigurationFailed").d("reason", "nullVirtualChannelConfiguration"));
return false;
}
auto configRoot =
alexaClientSDK::avsCommon::utils::configuration::ConfigurationNode::getRoot()[VIRTUAL_CHANNELS_CONFIG_KEY];
if (!configRoot) {
ACSDK_DEBUG9(LX(__func__).m("noConfigurationRoot"));
return true;
}
bool returnValue = true;
auto channelArray = configRoot.getArray(channelTypeKey);
if (!channelArray) {
ACSDK_DEBUG9(LX(__func__).d("key", channelTypeKey).m("keyNotFoundOrNotAnArray"));
} else {
for (std::size_t i = 0; i < channelArray.getArraySize(); i++) {
auto elem = channelArray[i];
if (!elem) {
ACSDK_ERROR(LX("readChannelConfigurationFailed").d("reason", "noNameKey"));
returnValue = false;
break;
}
std::string name;
if (!elem.getString(CHANNEL_NAME_KEY, &name)) {
ACSDK_ERROR(LX("readChannelConfigurationFailed").d("reason", "noNameKey"));
returnValue = false;
break;
}
int priority = 0;
if (!elem.getInt(CHANNEL_PRIORITY_KEY, &priority)) {
ACSDK_ERROR(LX("readChannelConfigurationFailed").d("reason", "noPriorityKey"));
returnValue = false;
break;
}
if (priority < 0) {
ACSDK_ERROR(LX("ChannelConfigurationFailed").d("reason", "invalidPriority").d("priority", priority));
returnValue = false;
break;
}
afml::FocusManager::ChannelConfiguration channelConfig{name, static_cast<unsigned int>(priority)};
virtualChannelConfigurations->push_back(channelConfig);
}
}
return returnValue;
}
} // namespace afml
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -44,7 +44,7 @@ using namespace avsCommon::utils::json;
using namespace ::testing;
/// Plenty of time for a test to complete.
static std::chrono::milliseconds WAIT_TIMEOUT(1000);
static std::chrono::milliseconds MY_WAIT_TIMEOUT(1000);
/// Namespace for AudioActivityTracke.
static const std::string NAMESPACE_AUDIO_ACTIVITY_TRACKER("AudioActivityTracker");
@ -79,6 +79,13 @@ static unsigned int CONTENT_CHANNEL_PRIORITY{300};
/// Timeout to sleep before asking for provideState().
static const std::chrono::milliseconds SHORT_TIMEOUT_MS = std::chrono::milliseconds(5);
/// MockChannelObserver for tests
class MockChannelObserver : public avsCommon::sdkInterfaces::ChannelObserverInterface {
public:
void onFocusChanged(avsCommon::avs::FocusState state, avsCommon::avs::MixingBehavior behavior) override {
}
};
class AudioActivityTrackerTest : public ::testing::Test {
public:
AudioActivityTrackerTest();
@ -139,11 +146,9 @@ void AudioActivityTrackerTest::SetUp() {
ASSERT_TRUE(m_mockContextManager != nullptr);
m_dialogChannel = std::make_shared<Channel>(DIALOG_CHANNEL_NAME, DIALOG_CHANNEL_PRIORITY);
m_dialogChannel->setInterface(DIALOG_INTERFACE_NAME);
ASSERT_TRUE(m_dialogChannel != nullptr);
m_contentChannel = std::make_shared<Channel>(CONTENT_CHANNEL_NAME, CONTENT_CHANNEL_PRIORITY);
m_contentChannel->setInterface(CONTENT_INTERFACE_NAME);
ASSERT_TRUE(m_contentChannel != nullptr);
}
@ -207,7 +212,7 @@ void AudioActivityTrackerTest::provideUpdate(const std::vector<Channel::State>&
m_audioActivityTracker->notifyOfActivityUpdates(channels);
std::this_thread::sleep_for(SHORT_TIMEOUT_MS);
m_audioActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
SetStateResult AudioActivityTrackerTest::wakeOnSetState() {
@ -224,7 +229,7 @@ TEST_F(AudioActivityTrackerTest, test_noActivityUpdate) {
.WillOnce(InvokeWithoutArgs(this, &AudioActivityTrackerTest::wakeOnSetState));
m_audioActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
/// Test if there's an empty set of activity updates, AudioActivityTracker will return an empty context.
@ -238,13 +243,13 @@ TEST_F(AudioActivityTrackerTest, test_emptyActivityUpdate) {
m_audioActivityTracker->notifyOfActivityUpdates(channels);
m_audioActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
/// Test if there's an activityUpdate for one active channel, context will be reported correctly.
TEST_F(AudioActivityTrackerTest, test_oneActiveChannel) {
std::vector<Channel::State> channels;
m_dialogChannel->setFocus(FocusState::FOREGROUND);
m_dialogChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_dialogChannel->getState());
provideUpdate(channels);
}
@ -255,8 +260,11 @@ TEST_F(AudioActivityTrackerTest, test_oneActiveChannel) {
*/
TEST_F(AudioActivityTrackerTest, test_oneActiveChannelWithAIPAsInterface) {
std::vector<Channel::State> channels;
m_dialogChannel->setInterface(AIP_INTERFACE_NAME);
m_dialogChannel->setFocus(FocusState::FOREGROUND);
auto mockObserver = std::make_shared<MockChannelObserver>();
auto aipActivity =
avsCommon::sdkInterfaces::FocusManagerInterface::Activity::create(AIP_INTERFACE_NAME, mockObserver);
m_dialogChannel->setPrimaryActivity(aipActivity);
m_dialogChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_dialogChannel->getState());
EXPECT_CALL(
*(m_mockContextManager.get()),
@ -267,7 +275,7 @@ TEST_F(AudioActivityTrackerTest, test_oneActiveChannelWithAIPAsInterface) {
m_audioActivityTracker->notifyOfActivityUpdates(channels);
std::this_thread::sleep_for(SHORT_TIMEOUT_MS);
m_audioActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
/*
@ -277,7 +285,7 @@ TEST_F(AudioActivityTrackerTest, test_oneActiveChannelWithAIPAsInterface) {
*/
TEST_F(AudioActivityTrackerTest, test_oneActiveChannelWithDefaultAndAIPAsInterfaces) {
std::vector<Channel::State> channels;
m_dialogChannel->setFocus(FocusState::FOREGROUND);
m_dialogChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_dialogChannel->getState());
provideUpdate(channels);
}
@ -285,8 +293,8 @@ TEST_F(AudioActivityTrackerTest, test_oneActiveChannelWithDefaultAndAIPAsInterfa
/// Test if there's an activityUpdate for two active channels, context will be reported correctly.
TEST_F(AudioActivityTrackerTest, test_twoActiveChannels) {
std::vector<Channel::State> channels;
m_dialogChannel->setFocus(FocusState::FOREGROUND);
m_contentChannel->setFocus(FocusState::BACKGROUND);
m_dialogChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
m_contentChannel->setFocus(FocusState::BACKGROUND, MixingBehavior::MUST_PAUSE);
channels.push_back(m_dialogChannel->getState());
channels.push_back(m_contentChannel->getState());
provideUpdate(channels);
@ -295,10 +303,10 @@ TEST_F(AudioActivityTrackerTest, test_twoActiveChannels) {
/// Test if there's an activityUpdate for one active and one idle channels, context will be reported correctly.
TEST_F(AudioActivityTrackerTest, test_oneActiveOneIdleChannels) {
std::vector<Channel::State> channels;
m_dialogChannel->setFocus(FocusState::FOREGROUND);
m_contentChannel->setFocus(FocusState::BACKGROUND);
m_dialogChannel->setFocus(FocusState::NONE);
m_contentChannel->setFocus(FocusState::FOREGROUND);
m_dialogChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
m_contentChannel->setFocus(FocusState::BACKGROUND, MixingBehavior::MUST_PAUSE);
m_dialogChannel->setFocus(FocusState::NONE, MixingBehavior::MUST_STOP);
m_contentChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_dialogChannel->getState());
channels.push_back(m_contentChannel->getState());
provideUpdate(channels);

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -44,7 +44,7 @@ using namespace avsCommon::utils::json;
using namespace ::testing;
/// Plenty of time for a test to complete.
static std::chrono::milliseconds WAIT_TIMEOUT(1000);
static std::chrono::milliseconds MY_WAIT_TIMEOUT(1000);
/// Namespace for AudioActivityTracke.
static const std::string NAMESPACE_AUDIO_ACTIVITY_TRACKER("VisualActivityTracker");
@ -133,7 +133,6 @@ void VisualActivityTrackerTest::SetUp() {
ASSERT_TRUE(m_mockContextManager != nullptr);
m_visualChannel = std::make_shared<Channel>(VISUAL_CHANNEL_NAME, VISUAL_CHANNEL_PRIORITY);
m_visualChannel->setInterface(VISUAL_INTERFACE_NAME);
ASSERT_TRUE(m_visualChannel != nullptr);
}
@ -197,7 +196,7 @@ void VisualActivityTrackerTest::provideUpdate(const std::vector<Channel::State>&
m_VisualActivityTracker->notifyOfActivityUpdates(channels);
std::this_thread::sleep_for(SHORT_TIMEOUT_MS);
m_VisualActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
SetStateResult VisualActivityTrackerTest::wakeOnSetState() {
@ -214,7 +213,7 @@ TEST_F(VisualActivityTrackerTest, test_noActivityUpdate) {
.WillOnce(InvokeWithoutArgs(this, &VisualActivityTrackerTest::wakeOnSetState));
m_VisualActivityTracker->provideState(NAMESPACE_AND_NAME_STATE, PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(MY_WAIT_TIMEOUT));
}
/// Test if there's an empty vector of activity updates, VisualActivityTracker will return an empty context.
@ -226,7 +225,7 @@ TEST_F(VisualActivityTrackerTest, test_emptyActivityUpdate) {
/// Test if there's an activityUpdate for one idle channel, VisualActivityTracker will return an empty context.
TEST_F(VisualActivityTrackerTest, test_oneIdleChannel) {
std::vector<Channel::State> channels;
m_visualChannel->setFocus(FocusState::NONE);
m_visualChannel->setFocus(FocusState::NONE, MixingBehavior::MUST_STOP);
channels.push_back(m_visualChannel->getState());
provideUpdate(channels);
}
@ -234,7 +233,7 @@ TEST_F(VisualActivityTrackerTest, test_oneIdleChannel) {
/// Test if there's an activityUpdate for one active channel, context will be reported correctly.
TEST_F(VisualActivityTrackerTest, test_oneActiveChannel) {
std::vector<Channel::State> channels;
m_visualChannel->setFocus(FocusState::FOREGROUND);
m_visualChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_visualChannel->getState());
provideUpdate(channels);
}
@ -246,7 +245,7 @@ TEST_F(VisualActivityTrackerTest, test_oneActiveChannel) {
TEST_F(VisualActivityTrackerTest, test_invalidChannelActivityUpdate) {
std::vector<Channel::State> channels;
auto invalidChannel = std::make_shared<Channel>(INVALID_CHANNEL_NAME, INVALID_CHANNEL_PRIORITY);
m_visualChannel->setFocus(FocusState::FOREGROUND);
m_visualChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_visualChannel->getState());
channels.push_back(invalidChannel->getState());
provideUpdate(channels);
@ -258,9 +257,9 @@ TEST_F(VisualActivityTrackerTest, test_invalidChannelActivityUpdate) {
*/
TEST_F(VisualActivityTrackerTest, test_validChannelTwoActivityUpdates) {
std::vector<Channel::State> channels;
m_visualChannel->setFocus(FocusState::FOREGROUND);
m_visualChannel->setFocus(FocusState::FOREGROUND, MixingBehavior::PRIMARY);
channels.push_back(m_visualChannel->getState());
m_visualChannel->setFocus(FocusState::BACKGROUND);
m_visualChannel->setFocus(FocusState::BACKGROUND, MixingBehavior::MUST_PAUSE);
channels.push_back(m_visualChannel->getState());
provideUpdate(channels);
}

View File

@ -111,6 +111,11 @@ public:
*/
std::string getUnparsedDirective() const;
/**
* Returns the attachmentContextId.
*/
std::string getAttachmentContextId() const;
private:
/**
* Constructor.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -31,7 +31,7 @@ namespace avs {
* The structure representing the endpoint attributes used for discovery.
*
* This structure mirrors the AVS definition which is documented here:
* https://developer.amazon.com/docs/alexa-voice-service/alexa-discovery.html
* https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-discovery.html
*
* @note The following attributes will differ from the default endpoint, used to describe this Alexa client, to any
* other endpoint controlled by this client. The differences are:
@ -85,7 +85,7 @@ struct AVSDiscoveryEndpointAttributes {
/// Maximum length of each endpoint attribute:
/// See format specification here:
/// https://developer.amazon.com/docs/alexa-voice-service/alexa-discovery.html#addorupdatereport
/// https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-discovery.html#addorupdatereport
/// @{
static constexpr size_t MAX_ENDPOINT_IDENTIFIER_LENGTH = 256;
static constexpr size_t MAX_FRIENDLY_NAME_LENGTH = 128;
@ -112,7 +112,7 @@ struct AVSDiscoveryEndpointAttributes {
std::string manufacturerName;
/// The display categories the device belongs to. This field should contain at least one category. See categories
/// in this document: https://developer.amazon.com/docs/device-apis/alexa-discovery.html#display-categories
/// in this document: https://developer.amazon.com/docs/alexa/device-apis/alexa-discovery.html#display-categories
/// @note: This value should only include ALEXA_VOICE_ENABLED for the default endpoint.
std::vector<std::string> displayCategories;
@ -124,7 +124,7 @@ struct AVSDiscoveryEndpointAttributes {
/// The optional connections list describing how the endpoint is connected to the internet or smart home hub.
/// You can find the values available here:
/// https://developer.amazon.com/docs/alexa-voice-service/alexa-discovery.html#addorupdatereport
/// https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-discovery.html#addorupdatereport
std::vector<std::map<std::string, std::string>> connections;
/// The optional custom key value pair used to store about the device. In the AVS documentation, this field name is

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -27,7 +27,7 @@ namespace avs {
/**
* The structure representing the endpoint attributes that may be included in AVS Directives and Events.
*
* See https://developer.amazon.com/docs/alexa-voice-service/versioning.html for more details.
* See https://developer.amazon.com/docs/alexa/alexa-voice-service/versioning.html for more details.
*/
struct AVSMessageEndpoint {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -28,7 +28,7 @@ using AlexaAssetId = std::string;
/**
* String constants for the asset identifier.
* @see https://developer.amazon.com/docs/device-apis/resources-and-assets.html#global-alexa-catalog
* @see https://developer.amazon.com/docs/alexa/device-apis/resources-and-assets.html#global-alexa-catalog
*/
/// Asset identifier for device with friendly name "Shower".

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -25,7 +25,7 @@ namespace avs {
/**
* An enum class indicating possible response from the endpoint on a controller API call.
* Response are derived from @see https://developer.amazon.com/docs/device-apis/alexa-errorresponse.html
* Response are derived from @see https://developer.amazon.com/docs/alexa/device-apis/alexa-errorresponse.html
*/
enum class AlexaResponseType {
/// Success

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -28,7 +28,7 @@ using AlexaUnitOfMeasure = std::string;
/**
* String constants for the unit of measure.
* @see https://developer.amazon.com/docs/device-apis/alexa-property-schemas.html#units-of-measure
* @see https://developer.amazon.com/docs/alexa/device-apis/alexa-property-schemas.html#units-of-measure
*/
/// The Alexa unit of measure as angle degrees.

View File

@ -0,0 +1,285 @@
/*
* Copyright 2019-2020 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 ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_ATTACHMENT_DEFAULTATTACHMENTREADER_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_ATTACHMENT_DEFAULTATTACHMENTREADER_H_
#include <AVSCommon/Utils/Logger/Logger.h>
#include <AVSCommon/Utils/Logger/LogEntry.h>
#include "AttachmentReader.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace avs {
namespace attachment {
/**
* A class that provides functionality to read data from an @c Attachment.
*
* @note This class is not thread-safe beyond the thread-safety provided by the underlying SharedDataStream object.
*/
template <typename SDSType>
class DefaultAttachmentReader : public AttachmentReader {
public:
/**
* Create an AttachmentReader.
*
* @param policy The policy this reader should adhere to.
* @param sds The underlying @c SharedDataStream which this object will use.
* @param offset If being constructed from an existing @c SharedDataStream, the index indicates where to read from.
* This parameter defaults to 0, indicating no offset from the specified reference.
* @param reference The position in the stream @c offset is applied to. This parameter defaults to @c ABSOLUTE,
* indicating offset is relative to the very beginning of the Attachment.
* @param resetOnOverrun If overrun is detected on @c read, whether to close the attachment (default behavior) or
* to reset the read position to where current write position is (and skip all the bytes in between).
* @return Returns a new AttachmentReader, or nullptr if the operation failed.
*/
static std::unique_ptr<AttachmentReader> create(
typename SDSType::Reader::Policy policy,
std::shared_ptr<SDSType> sds,
typename SDSType::Index offset = 0,
typename SDSType::Reader::Reference reference = SDSType::Reader::Reference::ABSOLUTE,
bool resetOnOverrun = false);
/**
* Destructor.
*/
~DefaultAttachmentReader();
/// @name AttachmentReader methods.
/// @{
std::size_t read(
void* buf,
std::size_t numBytes,
ReadStatus* readStatus,
std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(0)) override;
void close(ClosePoint closePoint = ClosePoint::AFTER_DRAINING_CURRENT_BUFFER) override;
bool seek(uint64_t offset) override;
uint64_t getNumUnreadBytes() override;
/// @}
private:
/**
* Constructor.
*
* @param policy The @c ReaderPolicy of this object.
* @param sds The underlying @c SharedDataStream which this object will use.
* @param resetOnOverrun If overrun is detected on @c read, whether to close the attachment (default behavior) or
* to reset the read position to where current write position is (and skip all the bytes in between).
*/
DefaultAttachmentReader(typename SDSType::Reader::Policy policy, std::shared_ptr<SDSType> sds, bool resetOnOverrun);
/// Log tag
static const std::string TAG;
/// The underlying @c SharedDataStream reader.
std::shared_ptr<typename SDSType::Reader> m_reader;
// On @c read overrun, Whether to close the attachment, or reset it to catch up with the write
bool m_resetOnOverrun;
};
template <typename SDSType>
const std::string DefaultAttachmentReader<SDSType>::TAG = "DefaultAttachmentReader";
template <typename SDSType>
std::unique_ptr<AttachmentReader> DefaultAttachmentReader<SDSType>::create(
typename SDSType::Reader::Policy policy,
std::shared_ptr<SDSType> sds,
typename SDSType::Index offset,
typename SDSType::Reader::Reference reference,
bool resetOnOverrun) {
auto reader =
std::unique_ptr<DefaultAttachmentReader>(new DefaultAttachmentReader<SDSType>(policy, sds, resetOnOverrun));
if (!reader->m_reader) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "createFailed").d("reason", "object not fully created"));
return nullptr;
}
if (!reader->m_reader->seek(offset, reference)) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "ConstructorFailed").d("reason", "seek failed"));
return nullptr;
}
return std::unique_ptr<AttachmentReader>(reader.release());
}
template <typename SDSType>
DefaultAttachmentReader<SDSType>::~DefaultAttachmentReader() {
close();
}
template <typename SDSType>
std::size_t DefaultAttachmentReader<SDSType>::read(
void* buf,
std::size_t numBytes,
AttachmentReader::ReadStatus* readStatus,
std::chrono::milliseconds timeoutMs) {
if (!readStatus) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "readFailed").d("reason", "read status is nullptr"));
return 0;
}
if (!buf) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "readFailed").d("reason", "buf is nullptr"));
*readStatus = ReadStatus::ERROR_INTERNAL;
return 0;
}
if (!m_reader) {
ACSDK_INFO(utils::logger::LogEntry(TAG, "readFailed").d("reason", "closed or uninitialized SDS"));
*readStatus = ReadStatus::CLOSED;
return 0;
}
if (timeoutMs.count() < 0) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "readFailed").d("reason", "negative timeout"));
*readStatus = ReadStatus::ERROR_INTERNAL;
return 0;
}
*readStatus = ReadStatus::OK;
if (0 == numBytes) {
return 0;
}
const auto wordSize = m_reader->getWordSize();
if (numBytes < wordSize) {
ACSDK_ERROR(
utils::logger::LogEntry(TAG, "readFailed").d("reason", "bytes requested smaller than SDS word size"));
*readStatus = ReadStatus::ERROR_BYTES_LESS_THAN_WORD_SIZE;
return 0;
}
std::size_t bytesRead = 0;
const auto numWords = numBytes / wordSize;
const auto readResult = m_reader->read(buf, numWords, timeoutMs);
/*
* Convert SDS return code accordingly:
*
* < 0 : Error code.
* 0 : The underlying SDS is closed.
* > 0 : The number of bytes read.
*/
if (readResult < 0) {
switch (readResult) {
// This means the writer has overwritten the reader.
case SDSType::Reader::Error::OVERRUN:
if (m_resetOnOverrun) {
// An attachment's read position will be reset to current writer position.
// Subsequent reads will deliver data from current writer position onward.
*readStatus = ReadStatus::OK_OVERRUN_RESET;
ACSDK_DEBUG5(utils::logger::LogEntry(TAG, "readFailed").d("reason", "memory overrun by writer"));
m_reader->seek(0, SDSType::Reader::Reference::BEFORE_WRITER);
} else {
// An attachment cannot recover from this.
*readStatus = ReadStatus::ERROR_OVERRUN;
ACSDK_ERROR(utils::logger::LogEntry(TAG, "readFailed").d("reason", "memory overrun by writer"));
close();
}
break;
// This means there is still an active writer, but no data. A read would block if the policy was blocking.
case SDSType::Reader::Error::WOULDBLOCK:
*readStatus = ReadStatus::OK_WOULDBLOCK;
break;
// This means there is still an active writer, but no data. A read call timed out waiting for data.
case SDSType::Reader::Error::TIMEDOUT:
*readStatus = ReadStatus::OK_TIMEDOUT;
break;
}
// If the status was not updated, then there's an error code from SDS we may not be handling.
if (ReadStatus::OK == *readStatus) {
ACSDK_ERROR(
utils::logger::LogEntry(TAG, "readFailed").d("reason", "unhandled error code").d("code", readResult));
*readStatus = ReadStatus::ERROR_INTERNAL;
}
} else if (0 == readResult) {
*readStatus = ReadStatus::CLOSED;
ACSDK_DEBUG0(utils::logger::LogEntry(TAG, "readFailed").d("reason", "SDS is closed"));
} else {
bytesRead = static_cast<size_t>(readResult) * wordSize;
}
return bytesRead;
}
template <typename SDSType>
void DefaultAttachmentReader<SDSType>::close(AttachmentReader::ClosePoint closePoint) {
if (m_reader) {
switch (closePoint) {
case ClosePoint::IMMEDIATELY:
m_reader->close();
return;
case ClosePoint::AFTER_DRAINING_CURRENT_BUFFER:
m_reader->close(0, SDSType::Reader::Reference::BEFORE_WRITER);
return;
}
}
}
template <typename SDSType>
bool DefaultAttachmentReader<SDSType>::seek(uint64_t offset) {
if (m_reader) {
return m_reader->seek(offset);
}
return false;
}
template <typename SDSType>
uint64_t DefaultAttachmentReader<SDSType>::getNumUnreadBytes() {
if (m_reader) {
return m_reader->tell(SDSType::Reader::Reference::BEFORE_WRITER);
}
ACSDK_ERROR(utils::logger::LogEntry(TAG, "getNumUnreadBytesFailed").d("reason", "noReader"));
return 0;
}
template <typename SDSType>
DefaultAttachmentReader<SDSType>::DefaultAttachmentReader(
typename SDSType::Reader::Policy policy,
std::shared_ptr<SDSType> sds,
bool resetOnOverrun) :
m_resetOnOverrun{resetOnOverrun} {
if (!sds) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "ConstructorFailed").d("reason", "SDS parameter is nullptr"));
return;
}
m_reader = sds->createReader(policy);
if (!m_reader) {
ACSDK_ERROR(utils::logger::LogEntry(TAG, "ConstructorFailed").d("reason", "could not create an SDS reader"));
return;
}
}
} // namespace attachment
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_ATTACHMENT_DEFAULTATTACHMENTREADER_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -62,7 +62,7 @@ public:
/**
* Destructor.
*/
~InProcessAttachmentReader();
~InProcessAttachmentReader() = default;
std::size_t read(
void* buf,
@ -78,20 +78,14 @@ public:
private:
/**
* Constructor.
* Constructor
*
* @param policy The @c ReaderPolicy of this object.
* @param sds The underlying @c SharedDataStream which this object will use.
* @param resetOnOverrun If overrun is detected on @c read, whether to close the attachment (default behavior) or
* to reset the read position to where current write position is (and skip all the bytes in between).
* @param delegate The reader implementation to use for in process attachment reader.
*/
InProcessAttachmentReader(SDSTypeReader::Policy policy, std::shared_ptr<SDSType> sds, bool resetOnOverrun);
explicit InProcessAttachmentReader(std::unique_ptr<AttachmentReader> delegate);
/// The underlying @c SharedDataStream reader.
std::shared_ptr<SDSTypeReader> m_reader;
// On @c read overrun, Whether to close the attachment, or reset it to catch up with the write
bool m_resetOnOverrun;
// Delegate reader
std::unique_ptr<AttachmentReader> m_delegate;
};
} // namespace attachment

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -16,14 +16,24 @@
#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_AUDIOINPUTSTREAM_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_AUDIOINPUTSTREAM_H_
#include "AVSCommon/Utils/SDS/SharedDataStream.h"
#ifdef CUSTOM_SDS_TRAITS_HEADER
#include CUSTOM_SDS_TRAITS_HEADER
#else
#include "AVSCommon/Utils/SDS/InProcessSDS.h"
#endif
namespace alexaClientSDK {
namespace avsCommon {
namespace avs {
/// The type used store and stream binary data.
using AudioInputStream = utils::sds::InProcessSDS;
#ifdef CUSTOM_SDS_TRAITS_CLASS
using AudioInputStream = utils::sds::SharedDataStream<CUSTOM_SDS_TRAITS_CLASS>;
#else
using AudioInputStream = utils::sds::SharedDataStream<utils::sds::InProcessSDSTraits>;
#endif
} // namespace avs
} // namespace avsCommon

View File

@ -74,7 +74,7 @@ public:
void onDeregistered() override;
void onFocusChanged(FocusState newFocus) override;
void onFocusChanged(FocusState newFocus, MixingBehavior behavior) override;
protected:
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -29,7 +29,7 @@ namespace avs {
/**
* This class represents the resources used by a Capability, communicated as friendly names to AVS.
* @see https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
* @see https://developer.amazon.com/docs/alexa/device-apis/resources-and-assets.html#capability-resources
*/
class CapabilityResources {
public:

View File

@ -0,0 +1,74 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CONTENTTYPE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CONTENTTYPE_H_
#include <string>
namespace alexaClientSDK {
namespace avsCommon {
namespace avs {
enum class ContentType {
/// Indicates that the corresponding Activity is mixable with other channels
/// Such Activities may duck upon receiving FocusState::BACKGROUND focus
MIXABLE,
/// Indicates that the corresponding Activity is not mixable with other channels
/// Such Activities must pause upon receiving FocusState::BACKGROUND focus
NONMIXABLE,
/// Indicates that the corresponding ContentType was undefined/unitialized
UNDEFINED,
/// Indicates the Number of @c ContentType enumerations
NUM_CONTENT_TYPE
};
/**
* This function converts the provided @c ContentType to a string.
*
* @param contentType The @c ContentType to convert to a string.
* @return The string conversion of @c contentType.
*/
inline std::string contentTypeToString(ContentType contentType) {
switch (contentType) {
case ContentType::MIXABLE:
return "MIXABLE";
case ContentType::NONMIXABLE:
return "NONMIXABLE";
case ContentType::UNDEFINED:
return "UNDEFINED";
default:
return "UNDEFINED";
}
}
/**
* Write a @c ContentType value to an @c ostream as a string.
*
* @param stream The stream to write the value to.
* @param contenType The @c ContentType value to write to the @c ostream as a string.
* @return The @c ostream that was passed in and written to.
*/
inline std::ostream& operator<<(std::ostream& stream, const ContentType& contentType) {
return stream << contentTypeToString(contentType);
}
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CONTENTTYPE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -26,7 +26,7 @@
#include "AVSCommon/SDKInterfaces/InteractionModelRequestProcessingObserverInterface.h"
#include "AVSCommon/SDKInterfaces/MessageObserverInterface.h"
#include "AVSCommon/SDKInterfaces/SpeechSynthesizerObserverInterface.h"
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include <AVSCommon/Utils/Threading/Executor.h>
#include <AVSCommon/Utils/Timing/Timer.h>
@ -52,10 +52,12 @@ public:
* arrive from AVS.
* @param timeoutForListeningToIdle This timeout will be used to time out from the LISTENING state in case the
* Request Processing Started (RPS) directive is not received from AVS.
* @param metricRecorder The metric recorder.
*/
DialogUXStateAggregator(
std::chrono::milliseconds timeoutForThinkingToIdle = std::chrono::seconds{8},
std::chrono::milliseconds timeoutForListeningToIdle = std::chrono::seconds{8});
std::chrono::milliseconds timeoutForListeningToIdle = std::chrono::seconds{8},
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
/**
* Adds an observer to be notified of UX state changes.
@ -82,7 +84,10 @@ public:
void onStateChanged(sdkInterfaces::AudioInputProcessorObserverInterface::State state) override;
void onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState state) override;
void onStateChanged(
sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState state,
const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId mediaSourceId,
const avsCommon::utils::Optional<avsCommon::utils::mediaPlayer::MediaPlayerState>& mediaPlayerState) override;
void receive(const std::string& contextId, const std::string& message) override;
@ -143,6 +148,9 @@ private:
*/
/// @{
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
/// The @c UXObserverInterface to notify any time the Alexa Voice Service UX state needs to change.
std::unordered_set<std::shared_ptr<sdkInterfaces::DialogUXStateObserverInterface>> m_observers;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -16,6 +16,7 @@
#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_FOCUSSTATE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_FOCUSSTATE_H_
#include <sstream>
#include <string>
namespace alexaClientSDK {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -45,7 +45,7 @@ inline int indicatorStateToInt(IndicatorState state) {
* @param stateNum The int to convert.
* @return The IndicatorState representation of stateNum or nullptr if stateNum is invalid.
*/
inline const IndicatorState intToIndicatorState(int stateNum) {
inline IndicatorState intToIndicatorState(int stateNum) {
if (stateNum < 0 || stateNum >= static_cast<int>(IndicatorState::UNDEFINED)) {
return IndicatorState::UNDEFINED;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.

View File

@ -0,0 +1,102 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_MIXINGBEHAVIOR_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_MIXINGBEHAVIOR_H_
#include <string>
namespace alexaClientSDK {
namespace avsCommon {
namespace avs {
enum class MixingBehavior {
/// Indicates that the corresponding Activity is the primary Activity on the AFML Channel
PRIMARY,
/// Indicates that the corresponding Activity may duck
/// If ducking is not possible, the Activity must pause instead
MAY_DUCK,
/// Indicates that the corresponding Activity must pause
MUST_PAUSE,
/// Indicates that the corresponding Activity must stop
MUST_STOP,
/// Indicates that the corresponding Activity may adopt any one of the above behaviors
UNDEFINED
};
/**
* This function converts the provided @c MixingBehavior to a string.
*
* @param behavior The @c MixingBehavior to convert to a string.
* @return The string conversion of @c behavior.
*/
inline std::string mixingBehaviorToString(MixingBehavior behavior) {
switch (behavior) {
case MixingBehavior::PRIMARY:
return "PRIMARY";
case MixingBehavior::MAY_DUCK:
return "MAY_DUCK";
case MixingBehavior::MUST_PAUSE:
return "MUST_PAUSE";
case MixingBehavior::MUST_STOP:
return "MUST_STOP";
case MixingBehavior::UNDEFINED:
return "UNDEFINED";
}
return "UNDEFINED";
}
/**
* This function reverse maps the provided string to corresponding MixingBehavior Implementation as specified by
* mixingBehaviorToString
* @param input string to convert to corresponding MixingBehavior
* @return @c MixingBehavior that corresponds to the input string. In case of error
* the API returns MixingBehavior::UNDEFINED
*/
inline MixingBehavior getMixingBehavior(const std::string& input) {
MixingBehavior behavior = MixingBehavior::UNDEFINED;
if (mixingBehaviorToString(MixingBehavior::PRIMARY) == input) {
behavior = MixingBehavior::PRIMARY;
} else if (mixingBehaviorToString(MixingBehavior::MAY_DUCK) == input) {
behavior = MixingBehavior::MAY_DUCK;
} else if (mixingBehaviorToString(MixingBehavior::MUST_PAUSE) == input) {
behavior = MixingBehavior::MUST_PAUSE;
} else if (mixingBehaviorToString(MixingBehavior::MUST_STOP) == input) {
behavior = MixingBehavior::MUST_STOP;
}
return behavior;
}
/**
* Write a @c MixingBehavior value to an @c ostream as a string.
*
* @param stream The stream to write the value to.
* @param behavior The @c MixingBehavior value to write to the @c ostream as a string.
* @return The @c ostream that was passed in and written to.
*/
inline std::ostream& operator<<(std::ostream& stream, const MixingBehavior& behavior) {
return stream << mixingBehaviorToString(behavior);
}
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_MIXINGBEHAVIOR_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -35,6 +35,10 @@ struct PlayRequestor {
std::string id;
};
inline bool operator==(const PlayRequestor& playRequestorA, const PlayRequestor& playRequestorB) {
return playRequestorA.type == playRequestorB.type && playRequestorA.id == playRequestorB.id;
}
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -23,6 +23,10 @@ namespace avs {
/**
* An enum class used to specify the refresh policy for the state information provided by a @c stateProviderInterface.
* The @c stateProviderInterface must specify the refresh policy when it updates its state via @c setState.
*
* Note: When a @c stateProviderInterface provides an empty state, the behavior is as follows:
* - For @c StateRefreshPolicy @c ALWAYS and @c NEVER, the empty state is included in the context.
* - For @c StateRefreshPolicy @c SOMETIMES, the empty state is NOT included in the context.
*/
enum class StateRefreshPolicy {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -35,7 +35,7 @@ static const std::string VALUE_KEY_STRING = "value";
static const std::string TIME_OF_SAMPLE_KEY_STRING = "timeOfSample";
/// Key used to identify an the uncertainty in milliseconds related to the time of sample. For more information:
/// https://developer.amazon.com/docs/alexa-voice-service/reportable-state-properties.html#property-object
/// https://developer.amazon.com/docs/alexa/alexa-voice-service/reportable-state-properties.html#property-object
static const std::string UNCERTAINTY_KEY_STRING = "uncertaintyInMilliseconds";
/// String to identify log entries originating from this file.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -272,9 +272,9 @@ std::pair<std::unique_ptr<AVSDirective>, AVSDirective::ParseStatus> AVSDirective
std::unique_ptr<AVSDirective> AVSDirective::create(
const std::string& unparsedDirective,
std::shared_ptr<AVSMessageHeader> avsMessageHeader,
const std::shared_ptr<AVSMessageHeader> avsMessageHeader,
const std::string& payload,
std::shared_ptr<AttachmentManagerInterface> attachmentManager,
const std::shared_ptr<AttachmentManagerInterface> attachmentManager,
const std::string& attachmentContextId,
const utils::Optional<AVSMessageEndpoint>& endpoint) {
if (!avsMessageHeader) {
@ -313,6 +313,10 @@ std::string AVSDirective::getUnparsedDirective() const {
return m_unparsedDirective;
}
std::string AVSDirective::getAttachmentContextId() const {
return m_attachmentContextId;
}
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-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.
@ -13,68 +13,30 @@
* permissions and limitations under the License.
*/
#include "AVSCommon/AVS/Attachment/DefaultAttachmentReader.h"
#include "AVSCommon/AVS/Attachment/InProcessAttachmentReader.h"
#include "AVSCommon/Utils/Logger/Logger.h"
using namespace alexaClientSDK::avsCommon::utils;
namespace alexaClientSDK {
namespace avsCommon {
namespace avs {
namespace attachment {
/// String to identify log entries originating from this file.
static const std::string TAG("InProcessAttachmentReader");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
std::unique_ptr<InProcessAttachmentReader> InProcessAttachmentReader::create(
SDSTypeReader::Policy policy,
std::shared_ptr<SDSType> sds,
SDSTypeIndex offset,
SDSTypeReader::Reference reference,
bool resetOnOverrun) {
auto reader =
std::unique_ptr<InProcessAttachmentReader>(new InProcessAttachmentReader(policy, sds, resetOnOverrun));
if (!reader->m_reader) {
ACSDK_ERROR(LX("createFailed").d("reason", "object not fully created"));
auto readerImpl =
DefaultAttachmentReader<SDSType>::create(policy, std::move(sds), offset, reference, resetOnOverrun);
if (!readerImpl) {
return nullptr;
}
if (!reader->m_reader->seek(offset, reference)) {
ACSDK_ERROR(LX("ConstructorFailed").d("reason", "seek failed"));
return nullptr;
}
return reader;
return std::unique_ptr<InProcessAttachmentReader>(new InProcessAttachmentReader(std::move(readerImpl)));
}
InProcessAttachmentReader::InProcessAttachmentReader(
SDSTypeReader::Policy policy,
std::shared_ptr<SDSType> sds,
bool resetOnOverrun) :
m_resetOnOverrun{resetOnOverrun} {
if (!sds) {
ACSDK_ERROR(LX("ConstructorFailed").d("reason", "SDS parameter is nullptr"));
return;
}
m_reader = sds->createReader(policy);
if (!m_reader) {
ACSDK_ERROR(LX("ConstructorFailed").d("reason", "could not create an SDS reader"));
return;
}
}
InProcessAttachmentReader::~InProcessAttachmentReader() {
close();
InProcessAttachmentReader::InProcessAttachmentReader(std::unique_ptr<AttachmentReader> reader) :
m_delegate(std::move(reader)) {
}
std::size_t InProcessAttachmentReader::read(
@ -82,121 +44,19 @@ std::size_t InProcessAttachmentReader::read(
std::size_t numBytes,
ReadStatus* readStatus,
std::chrono::milliseconds timeoutMs) {
if (!readStatus) {
ACSDK_ERROR(LX("readFailed").d("reason", "read status is nullptr"));
return 0;
}
if (!m_reader) {
ACSDK_INFO(LX("readFailed").d("reason", "closed or uninitialized SDS"));
*readStatus = ReadStatus::CLOSED;
return 0;
}
if (timeoutMs.count() < 0) {
ACSDK_ERROR(LX("readFailed").d("reason", "negative timeout"));
*readStatus = ReadStatus::ERROR_INTERNAL;
return 0;
}
*readStatus = ReadStatus::OK;
if (0 == numBytes) {
return 0;
}
auto wordSize = m_reader->getWordSize();
if (numBytes < wordSize) {
ACSDK_ERROR(LX("readFailed").d("reason", "bytes requested smaller than SDS word size"));
*readStatus = ReadStatus::ERROR_BYTES_LESS_THAN_WORD_SIZE;
return 0;
}
std::size_t bytesRead = 0;
auto numWords = numBytes / wordSize;
auto readResult = m_reader->read(buf, numWords, timeoutMs);
/*
* Convert SDS return code accordingly:
*
* < 0 : Error code.
* 0 : The underlying SDS is closed.
* > 0 : The number of bytes read.
*/
if (readResult < 0) {
switch (readResult) {
// This means the writer has overwritten the reader.
case SDSType::Reader::Error::OVERRUN:
if (m_resetOnOverrun) {
// An attachment's read position will be reset to current writer position.
// Subsequent reads will deliver data from current writer position onward.
*readStatus = ReadStatus::OK_OVERRUN_RESET;
ACSDK_DEBUG5(LX("readFailed").d("reason", "memory overrun by writer"));
m_reader->seek(0, SDSTypeReader::Reference::BEFORE_WRITER);
} else {
// An attachment cannot recover from this.
*readStatus = ReadStatus::ERROR_OVERRUN;
ACSDK_ERROR(LX("readFailed").d("reason", "memory overrun by writer"));
close();
}
break;
// This means there is still an active writer, but no data. A read would block if the policy was blocking.
case SDSType::Reader::Error::WOULDBLOCK:
*readStatus = ReadStatus::OK_WOULDBLOCK;
break;
// This means there is still an active writer, but no data. A read call timed out waiting for data.
case SDSType::Reader::Error::TIMEDOUT:
*readStatus = ReadStatus::OK_TIMEDOUT;
break;
}
// If the status was not updated, then there's an error code from SDS we may not be handling.
if (ReadStatus::OK == *readStatus) {
ACSDK_ERROR(LX("readFailed").d("reason", "unhandled error code").d("code", readResult));
*readStatus = ReadStatus::ERROR_INTERNAL;
}
} else if (0 == readResult) {
*readStatus = ReadStatus::CLOSED;
ACSDK_DEBUG0(LX("readFailed").d("reason", "SDS is closed"));
} else {
bytesRead = static_cast<size_t>(readResult) * wordSize;
}
return bytesRead;
return m_delegate->read(buf, numBytes, readStatus, timeoutMs);
}
void InProcessAttachmentReader::close(ClosePoint closePoint) {
if (m_reader) {
switch (closePoint) {
case ClosePoint::IMMEDIATELY:
m_reader->close();
return;
case ClosePoint::AFTER_DRAINING_CURRENT_BUFFER:
m_reader->close(0, SDSType::Reader::Reference::BEFORE_WRITER);
return;
}
}
m_delegate->close(closePoint);
}
bool InProcessAttachmentReader::seek(uint64_t offset) {
if (m_reader) {
return m_reader->seek(offset);
}
return false;
return m_delegate->seek(offset);
}
uint64_t InProcessAttachmentReader::getNumUnreadBytes() {
if (m_reader) {
return m_reader->tell(utils::sds::InProcessSDS::Reader::Reference::BEFORE_WRITER);
}
ACSDK_ERROR(LX("getNumUnreadBytesFailed").d("reason", "noReader"));
return 0;
return m_delegate->getNumUnreadBytes();
}
} // namespace attachment

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -141,7 +141,7 @@ void CapabilityAgent::removeDirective(const std::string& messageId) {
m_directiveInfoMap.erase(messageId);
}
void CapabilityAgent::onFocusChanged(FocusState) {
void CapabilityAgent::onFocusChanged(FocusState, MixingBehavior) {
// default no-op
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -12,6 +12,8 @@
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include <AVSCommon/Utils/Metrics/DataPointCounterBuilder.h>
#include <AVSCommon/Utils/Metrics/MetricEventBuilder.h>
#include "AVSCommon/AVS/DialogUXStateAggregator.h"
@ -20,6 +22,7 @@ namespace avsCommon {
namespace avs {
using namespace sdkInterfaces;
using namespace avsCommon::utils::metrics;
/// String to identify log entries originating from this file.
static const std::string TAG("DialogUXStateAggregator");
@ -36,9 +39,42 @@ static const std::string TAG("DialogUXStateAggregator");
*/
static const std::chrono::milliseconds SHORT_TIMEOUT{200};
/// Custom Metrics prefix used by DialogUXStateAggregator.
static const std::string CUSTOM_METRIC_PREFIX = "CUSTOM-";
/// error metric for Listening timeout expires
static const std::string LISTENING_TIMEOUT_EXPIRES = "LISTENING_TIMEOUT_EXPIRES";
/// error metric for Thinking timeout expires
static const std::string THINKING_TIMEOUT_EXPIRES = "THINKING_TIMEOUT_EXPIRES";
/**
* Submits a metric of given event name
* @param metricRecorder The @c MetricRecorderInterface which records Metric events
* @param eventName The name of the metric event
*/
static void submitMetric(const std::shared_ptr<MetricRecorderInterface>& metricRecorder, const std::string& eventName) {
if (!metricRecorder) {
return;
}
auto metricEvent = MetricEventBuilder{}
.setActivityName(CUSTOM_METRIC_PREFIX + eventName)
.addDataPoint(DataPointCounterBuilder{}.setName(eventName).increment(1).build())
.build();
if (metricEvent == nullptr) {
ACSDK_ERROR(LX("Error creating metric."));
return;
}
recordMetric(metricRecorder, metricEvent);
}
DialogUXStateAggregator::DialogUXStateAggregator(
std::chrono::milliseconds timeoutForThinkingToIdle,
std::chrono::milliseconds timeoutForListeningToIdle) :
std::chrono::milliseconds timeoutForListeningToIdle,
std::shared_ptr<MetricRecorderInterface> metricRecorder) :
m_metricRecorder{metricRecorder},
m_currentState{DialogUXStateObserverInterface::DialogUXState::IDLE},
m_timeoutForThinkingToIdle{timeoutForThinkingToIdle},
m_timeoutForListeningToIdle{timeoutForListeningToIdle},
@ -96,7 +132,10 @@ void DialogUXStateAggregator::onStateChanged(AudioInputProcessorObserverInterfac
});
}
void DialogUXStateAggregator::onStateChanged(SpeechSynthesizerObserverInterface::SpeechSynthesizerState state) {
void DialogUXStateAggregator::onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState state,
const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId mediaSourceId,
const avsCommon::utils::Optional<avsCommon::utils::mediaPlayer::MediaPlayerState>& mediaPlayerState) {
m_speechSynthesizerState = state;
m_executor.submit([this, state]() {
@ -139,7 +178,7 @@ void DialogUXStateAggregator::receive(const std::string& contextId, const std::s
void DialogUXStateAggregator::onConnectionStatusChanged(
const ConnectionStatusObserverInterface::Status status,
const ConnectionStatusObserverInterface::ChangedReason reason) {
m_executor.submit([this, &status]() {
m_executor.submit([this, status]() {
if (status != avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED) {
setState(DialogUXStateObserverInterface::DialogUXState::IDLE);
}
@ -201,6 +240,8 @@ void DialogUXStateAggregator::transitionFromThinkingTimedOut() {
if (DialogUXStateObserverInterface::DialogUXState::THINKING == m_currentState) {
ACSDK_DEBUG(LX("transitionFromThinkingTimedOut"));
setState(DialogUXStateObserverInterface::DialogUXState::IDLE);
submitMetric(m_metricRecorder, THINKING_TIMEOUT_EXPIRES);
}
});
}
@ -210,6 +251,8 @@ void DialogUXStateAggregator::transitionFromListeningTimedOut() {
if (DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) {
ACSDK_DEBUG(LX("transitionFromListeningTimedOut"));
setState(DialogUXStateObserverInterface::DialogUXState::IDLE);
submitMetric(m_metricRecorder, LISTENING_TIMEOUT_EXPIRES);
}
});
}

View File

@ -116,8 +116,6 @@ void MessageRequest::removeObserver(
m_observers.erase(observer);
}
using namespace avsCommon::sdkInterfaces;
} // namespace avs
} // namespace avsCommon
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -14,6 +14,7 @@
*/
#include <cstring>
#include <vector>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
@ -51,8 +52,9 @@ void AttachmentUtilsTest::SetUp() {
* Test read until end of buffer
*/
TEST_F(AttachmentUtilsTest, test_readCompleteBuffer) {
char dstBuffer[sampleBuffer.length() + 10];
memset(dstBuffer, 0, sampleBuffer.length() + 10);
std::vector<char> dstBufferVec(sampleBuffer.length() + 10);
std::fill(dstBufferVec.begin(), dstBufferVec.end(), 0);
char* dstBuffer = dstBufferVec.data();
AttachmentReader::ReadStatus status;
size_t bytesRead = m_attachmentReader->read(dstBuffer, sampleBuffer.length(), &status);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -34,11 +34,15 @@ std::unique_ptr<InProcessSDS> createSDS(int desiredSize) {
}
std::vector<uint8_t> createTestPattern(int patternSize) {
std::vector<uint8_t> vec(patternSize);
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t> engine;
std::generate(begin(vec), end(vec), std::ref(engine));
std::vector<uint8_t> ret(patternSize);
std::vector<uint16_t> vec(patternSize);
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint16_t> engine;
return vec;
std::generate(begin(vec), end(vec), std::ref(engine));
for (size_t i = 0; i < vec.size(); i++) {
ret[i] = static_cast<uint8_t>(vec[i]);
}
return ret;
}
} // namespace test

View File

@ -2,5 +2,6 @@ add_subdirectory("Attachment")
set(INCLUDE_PATH
"${AVSCommon_INCLUDE_DIRS}"
"${AVSCommon_SOURCE_DIR}/AVS/test"
"${AVSCommon_SOURCE_DIR}/SDKInterfaces/test")
"${AVSCommon_SOURCE_DIR}/SDKInterfaces/test"
"${MetricRecorder_INCLUDE_DIRS}")
discover_unit_tests("${INCLUDE_PATH}" "AVSCommon;AttachmentCommonTestLib")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -23,8 +23,8 @@ namespace alexaClientSDK {
namespace avsCommon {
namespace test {
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
/// Long time out for observers to wait for the state change callback (we should not reach this).
static const auto DEFAULT_TIMEOUT = std::chrono::seconds(5);
@ -36,6 +36,9 @@ static const auto SHORT_TIMEOUT = std::chrono::milliseconds(50);
// This needs to be longer than the values passed into the DialogUXStateAggregator.
static const auto TRANSITION_TIMEOUT = std::chrono::milliseconds(300);
/// Dummy value for a media player source id
static const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TEST_SOURCE_ID = -1;
/// A test observer that mocks out the DialogUXStateObserverInterface##onDialogUXStateChanged() call.
class TestObserver : public DialogUXStateObserverInterface {
public:
@ -145,6 +148,9 @@ protected:
/// Another test observer
std::shared_ptr<TestObserver> m_anotherTestObserver;
/// A MediaPlayerState object passed to onStateChange by SpeechSynthesizer
avsCommon::utils::mediaPlayer::MediaPlayerState m_testMediaPlayerState;
virtual void SetUp() {
m_aggregator = std::make_shared<DialogUXStateAggregator>();
ASSERT_TRUE(m_aggregator);
@ -298,7 +304,8 @@ TEST_F(DialogUXAggregatorTest, test_listeningThenReceiveThenSpeakGoesToSpeakButN
m_aggregator->receive("", "");
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
@ -316,12 +323,14 @@ TEST_F(DialogUXAggregatorTest, test_speakingAndRecognizingFinishedGoesToIdle) {
m_aggregator->receive("", "");
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::IDLE);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE);
}
@ -332,20 +341,24 @@ TEST_F(DialogUXAggregatorTest, test_nonIdleObservantsPreventsIdle) {
// AIP is active, SS is not. Expected: non idle
m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::LISTENING);
// Both AIP and SS are inactive. Expected: idle
m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::IDLE);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE);
// AIP is inactive, SS is active. Expected: non-idle
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
// AIP is inactive, SS is inactive: Expected: idle
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE);
}
@ -360,11 +373,13 @@ TEST_F(DialogUXAggregatorTest, test_speakingFinishedDoesNotGoesToIdleImmediately
m_aggregator->receive("", "");
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertNoStateChange(m_testObserver);
}
@ -377,7 +392,8 @@ TEST_F(DialogUXAggregatorTest, test_simpleReceiveDoesNothing) {
assertNoStateChange(m_testObserver);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
@ -399,7 +415,9 @@ TEST_F(DialogUXAggregatorTest, test_thinkingThenReceiveRemainsInThinkingIfSpeech
m_aggregator->receive("", "");
m_aggregator->onStateChanged(
sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS);
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS,
TEST_SOURCE_ID,
m_testMediaPlayerState);
// Make sure after SpeechSynthesizer reports GAINING_FOCUS, that it would stay in THINKING state
m_aggregator->receive("", "");
@ -422,14 +440,16 @@ TEST_F(DialogUXAggregatorTest, test_validStatesForRPSToThinking) {
m_aggregator->receive("", "");
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING);
m_aggregator->onRequestProcessingStarted();
assertNoStateChange(m_testObserver);
// Reset to IDLE
m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::IDLE);
m_aggregator->onStateChanged(sdkInterfaces::SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED);
m_aggregator->onStateChanged(
SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED, TEST_SOURCE_ID, m_testMediaPlayerState);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE);
m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::EXPECTING_SPEECH);
assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::EXPECTING);

View File

@ -23,8 +23,8 @@ add_library(AVSCommon SHARED
AVS/src/Attachment/InProcessAttachment.cpp
AVS/src/Attachment/InProcessAttachmentReader.cpp
AVS/src/Attachment/InProcessAttachmentWriter.cpp
AVS/src/CapabilityConfiguration.cpp
AVS/src/CapabilityAgent.cpp
AVS/src/CapabilityConfiguration.cpp
AVS/src/CapabilityTag.cpp
AVS/src/DialogUXStateAggregator.cpp
AVS/src/DirectiveRoutingRule.cpp
@ -72,14 +72,14 @@ add_library(AVSCommon SHARED
Utils/src/Logger/ModuleLogger.cpp
Utils/src/Logger/ThreadMoniker.cpp
Utils/src/MacAddressString.cpp
Utils/src/Metrics/DataPoint.cpp
Utils/src/Metrics/DataPointStringBuilder.cpp
Utils/src/Metrics/DataPointCounterBuilder.cpp
Utils/src/Metrics/DataPointDurationBuilder.cpp
Utils/src/Metrics/MetricEvent.cpp
Utils/src/Metrics/MetricEventBuilder.cpp
Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp
Utils/src/Metrics.cpp
Utils/src/Metrics/DataPoint.cpp
Utils/src/Metrics/DataPointCounterBuilder.cpp
Utils/src/Metrics/DataPointDurationBuilder.cpp
Utils/src/Metrics/DataPointStringBuilder.cpp
Utils/src/Metrics/MetricEvent.cpp
Utils/src/Metrics/MetricEventBuilder.cpp
Utils/src/MultiTimer.cpp
Utils/src/Network/InternetConnectionMonitor.cpp
Utils/src/RequiresShutdown.cpp
@ -104,6 +104,13 @@ target_include_directories(AVSCommon PUBLIC
"${MultipartParser_SOURCE_DIR}"
${CURL_INCLUDE_DIRS})
if (CUSTOM_AUDIO_INPUT_STREAM_TRAITS)
target_include_directories(AVSCommon PUBLIC
${CUSTOMSDSTRAITS_LIB_DIR}/include)
target_link_libraries(AVSCommon ${CUSTOMSDSTRAITS_LIB_NAME})
endif ()
target_link_libraries(AVSCommon
${CURL_LIBRARIES})

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -20,6 +20,7 @@
#include <memory>
#include <AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h>
#include <AVSCommon/SDKInterfaces/AVSGatewayObserverInterface.h>
#include <AVSCommon/SDKInterfaces/PostConnectSendMessageInterface.h>
namespace alexaClientSDK {
@ -53,6 +54,20 @@ public:
* @return True if successful, else false.
*/
virtual bool setGatewayURL(const std::string& avsGatewayURL) = 0;
/**
* Adds an observer.
*
* @param observer The @c AVSGatewayObserver
*/
virtual void addObserver(std::shared_ptr<avsCommon::sdkInterfaces::AVSGatewayObserverInterface> observer) = 0;
/**
* Removes an observer.
*
* @param observer The @c AVSGatewayObserver.
*/
virtual void removeObserver(std::shared_ptr<avsCommon::sdkInterfaces::AVSGatewayObserverInterface> observer) = 0;
};
} // namespace sdkInterfaces

View File

@ -0,0 +1,47 @@
/*
* Copyright 2020 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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AVSGATEWAYOBSERVERINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AVSGATEWAYOBSERVERINTERFACE_H_
#include <string>
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
/**
* An interface for setting AVS gateway.
*/
class AVSGatewayObserverInterface {
public:
/**
* Destructor.
*/
virtual ~AVSGatewayObserverInterface() = default;
/**
* Observer method to be called when the AVS Gateway is changed.
*
* @param avsGateway The AVS Gateway the device should be connected to.
*/
virtual void onAVSGatewayChanged(const std::string& avsGateway) = 0;
};
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AVSGATEWAYOBSERVERINTERFACE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -39,6 +39,14 @@ public:
* @param newState New state of the @c EqualizerController.
*/
virtual void onEqualizerStateChanged(const EqualizerState& newState) = 0;
/**
* Receives the same state of the @c EqualizerController when equalizer setting is changed but to an identical state
* to the current state. This callback is called after all changes has been applied.
*
* @param newState New state of the @c EqualizerController.
*/
virtual void onEqualizerSameStateChanged(const EqualizerState& newState) = 0;
};
} // namespace audio

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -16,6 +16,7 @@
#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AUDIOINPUTPROCESSOROBSERVERINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AUDIOINPUTPROCESSOROBSERVERINTERFACE_H_
#include <sstream>
#include <string>
namespace alexaClientSDK {

View File

@ -0,0 +1,83 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_BLUETOOTHDEVICECONNECTIONRULEINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_BLUETOOTHDEVICECONNECTIONRULEINTERFACE_H_
#include <map>
#include <set>
#include <AVSCommon/SDKInterfaces/Bluetooth/BluetoothDeviceInterface.h>
#include <AVSCommon/Utils/Bluetooth/DeviceCategory.h>
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
namespace bluetooth {
/**
* This interface defines the connection rule the Bluetooth device needs to follow.
*/
class BluetoothDeviceConnectionRuleInterface {
public:
/**
* Destructor.
*/
virtual ~BluetoothDeviceConnectionRuleInterface() = default;
/**
* The rule to explicitly connect the Bluetooth device after pair.
*
* @return true if the caller needs to handle connect logic.
*/
virtual bool shouldExplicitlyConnect() = 0;
/**
* The rule to explicitly disconnect the Bluetooth device before unpair.
*
* @return true if the caller needs to handle disconnect logic.
*/
virtual bool shouldExplicitlyDisconnect() = 0;
/**
* The rule to get a set of Bluetooth devices needed to disconnect when the Bluetooth device connects.
*
* @param connectedDevices the current connected devices.
* @return the set of Bluetooth devices needed to disconnect.
*/
virtual std::set<std::shared_ptr<BluetoothDeviceInterface>> devicesToDisconnect(
std::map<DeviceCategory, std::set<std::shared_ptr<BluetoothDeviceInterface>>> connectedDevices) = 0;
/**
* Get the set of device categories using the connection rule.
*
* @return The set of @c DeviceCategory of the connection rule.
*/
virtual std::set<DeviceCategory> getDeviceCategories() = 0;
/**
* Get the set of profile uuids which support those device categories defined in the connection rule.
*
* @return The set of profile uuids.
*/
virtual std::set<std::string> getDependentProfiles() = 0;
};
} // namespace bluetooth
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_BLUETOOTHDEVICECONNECTIONRULEINTERFACE_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -22,12 +22,10 @@
#include <string>
#include <vector>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/A2DPSourceInterface.h>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/A2DPSinkInterface.h>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/AVRCPControllerInterface.h>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/AVRCPTargetInterface.h>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h>
#include <AVSCommon/SDKInterfaces/Bluetooth/Services/SDPRecordInterface.h>
#include <AVSCommon/Utils/Bluetooth/MediaStreamingState.h>
#include <AVSCommon/Utils/Optional.h>
namespace alexaClientSDK {
namespace avsCommon {
@ -101,6 +99,41 @@ inline std::ostream& operator<<(std::ostream& stream, const DeviceState state) {
/// Represents a Bluetooth Device.
class BluetoothDeviceInterface {
public:
/**
* Struct to represent a Bluetooth device meta data.
*/
struct MetaData {
/// The value of undefined class of the Bluetooth device.
static const int UNDEFINED_CLASS_VALUE = 0;
utils::Optional<int> vendorId;
utils::Optional<int> productId;
int classOfDevice;
utils::Optional<int> vendorDeviceSigId;
utils::Optional<std::string> vendorDeviceId;
/**
* Constructor
* @param vendorId The vendor id.
* @param productId The product id.
* @param classOfDevice The class of device.
* @param vendorDeviceSigId The vendor device SIG id.
* @param vendorDeviceId The vendor device id.
*/
MetaData(
utils::Optional<int> vendorId,
utils::Optional<int> productId,
int classOfDevice,
utils::Optional<int> vendorDeviceSigId,
utils::Optional<std::string> vendorDeviceId) :
vendorId(vendorId),
productId(productId),
classOfDevice(classOfDevice),
vendorDeviceSigId(vendorDeviceSigId),
vendorDeviceId(vendorDeviceId) {
}
};
/// Destructor
virtual ~BluetoothDeviceInterface() = default;
@ -125,6 +158,13 @@ public:
*/
virtual DeviceState getDeviceState() = 0;
/**
* Getter for the Bluetooth device metadata.
*
* @return the meta data of the Bluetooth device.
*/
virtual MetaData getDeviceMetaData() = 0;
/**
* Getter for the paired state of the device. This should return
* the state after any pending state changes have been resolved.
@ -172,18 +212,27 @@ public:
/// @return The Bluetooth Services that this device supports.
virtual std::vector<std::shared_ptr<services::SDPRecordInterface>> getSupportedServices() = 0;
// TODO : Generic getService method.
/// @return A pointer to an instance of the @c A2DPSourceInterface if supported, else a nullptr.
virtual std::shared_ptr<services::A2DPSourceInterface> getA2DPSource() = 0;
/**
* Get the Bluetooth service that this device supports.
*
* @param uuid the uuid of the Bluetooth Service.
* @return A pointer to an instance of the @c BluetoothServiceInterface if supported, else a nullptr.
*/
virtual std::shared_ptr<services::BluetoothServiceInterface> getService(std::string uuid) = 0;
/// @return A pointer to an instance of the @c A2DPSinkInterface if supported, else a nullptr.
virtual std::shared_ptr<services::A2DPSinkInterface> getA2DPSink() = 0;
/// @return The current media streaming state of the BluetoothDevice if the device supports A2DP streaming.
virtual utils::bluetooth::MediaStreamingState getStreamingState() = 0;
/// @return A pointer to an instance of the @c AVRCPTargetInterface if supported, else a nullptr.
virtual std::shared_ptr<services::AVRCPTargetInterface> getAVRCPTarget() = 0;
/// @return A pointer to an instance of the @c AVRCPControllerInterface if supported, else a nullptr.
virtual std::shared_ptr<services::AVRCPControllerInterface> getAVRCPController() = 0;
/**
* Toggle the profile of a device, which restricts the future connection/disconnection.
*
* @param enabled True if need to connect the certain profile, false to disconnect.
* @param service The target profile to toggle.
* @return A bool indicating success.
*/
virtual bool toggleServiceConnection(
bool enabled,
std::shared_ptr<services::BluetoothServiceInterface> service) = 0;
};
} // namespace bluetooth

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* 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.
@ -28,8 +28,8 @@ namespace bluetooth {
namespace services {
// TODO: Move to own enum file.
/// An Enum representing AVRCP commands.
enum class AVRCPCommand {
/// An Enum representing Media commands.
enum class MediaCommand {
/// A Play command.
PLAY,
@ -40,38 +40,43 @@ enum class AVRCPCommand {
NEXT,
/// A Previous command. If issued at the beginning of a song, the previous track will be selected.
PREVIOUS
PREVIOUS,
/// A Play/Pause command.
PLAY_PAUSE
};
/**
* Converts the @c AVRCPCommand enum to a string.
* Converts the @c MediaCommand enum to a string.
*
* @param cmd The @c AVRCPCommand to convert.
* @return A string representation of the @c AVRCPCommand.
* @param cmd The @c MediaCommand to convert.
* @return A string representation of the @c MediaCommand.
*/
inline std::string commandToString(AVRCPCommand cmd) {
inline std::string commandToString(MediaCommand cmd) {
switch (cmd) {
case AVRCPCommand::PLAY:
case MediaCommand::PLAY:
return "PLAY";
case AVRCPCommand::PAUSE:
case MediaCommand::PAUSE:
return "PAUSE";
case AVRCPCommand::NEXT:
case MediaCommand::NEXT:
return "NEXT";
case AVRCPCommand::PREVIOUS:
case MediaCommand::PREVIOUS:
return "PREVIOUS";
case MediaCommand::PLAY_PAUSE:
return "PLAY_PAUSE";
}
return "UNKNOWN";
}
/**
* Overload for the @c AVRCPCommand enum. This will write the @c AVRCPCommand as a string to the provided stream.
* Overload for the @c MediaCommand enum. This will write the @c MediaCommand as a string to the provided stream.
*
* @param stream An ostream to send the DeviceState as a string.
* @param cmd The @c AVRCPCommand to convert.
* @param cmd The @c MediaCommand to convert.
* @return The stream.
*/
inline std::ostream& operator<<(std::ostream& stream, const AVRCPCommand cmd) {
inline std::ostream& operator<<(std::ostream& stream, const MediaCommand cmd) {
return stream << commandToString(cmd);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* 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.
@ -26,7 +26,11 @@ namespace sdkInterfaces {
namespace bluetooth {
namespace services {
/// Interface representing a BluetoothService.
/**
* Interface representing a Bluetooth Service.
* More Bluetooth Service information(e.g, UUID, NAME) could be found at
* https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/
*/
class BluetoothServiceInterface {
public:
/**

View File

@ -0,0 +1,45 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HFPINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HFPINTERFACE_H_
#include "AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
namespace bluetooth {
namespace services {
/**
* Interface to support hands-free profile.
*/
class HFPInterface : public BluetoothServiceInterface {
public:
/// The Service UUID.
static constexpr const char* UUID = "0000111e-0000-1000-8000-00805f9b34fb";
/// The Service Name.
static constexpr const char* NAME = "Handsfree";
};
} // namespace services
} // namespace bluetooth
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HFPINTERFACE_H_

View File

@ -0,0 +1,45 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HIDINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HIDINTERFACE_H_
#include "AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
namespace bluetooth {
namespace services {
/**
* Interface to support human interface device(such as keyboards, mics) profile.
*/
class HIDInterface : public BluetoothServiceInterface {
public:
/// The Service UUID.
static constexpr const char* UUID = "00001124-0000-1000-8000-00805f9b34fb";
/// The Service Name.
static constexpr const char* NAME = "HumanInterfaceDeviceService";
};
} // namespace services
} // namespace bluetooth
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_HIDINTERFACE_H_

View File

@ -0,0 +1,45 @@
/*
* Copyright 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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_SPPINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_SPPINTERFACE_H_
#include "AVSCommon/SDKInterfaces/Bluetooth/Services/BluetoothServiceInterface.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
namespace bluetooth {
namespace services {
/**
* Interface to support serial port profile.
*/
class SPPInterface : public BluetoothServiceInterface {
public:
/// The Service UUID.
static constexpr const char* UUID = "00001101-0000-1000-8000-00805f9b34fb";
/// The Service Name.
static constexpr const char* NAME = "SerialPort";
};
} // namespace services
} // namespace bluetooth
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_BLUETOOTH_SERVICES_SPPINTERFACE_H_

View File

@ -100,6 +100,23 @@ public:
* Stops the call.
*/
virtual void stopCall() = 0;
/**
* Mute self during the call.
*/
virtual void muteSelf() = 0;
/**
* Unmute self during the call.
*/
virtual void unmuteSelf() = 0;
/**
* Check if the call is muted.
*
* @return Whether the call is muted.
*/
virtual bool isSelfMuted() const = 0;
};
inline CallManagerInterface::CallManagerInterface(

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* 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.
@ -52,6 +52,17 @@ public:
* @param state The new CallState.
*/
virtual void onCallStateChange(CallState state) = 0;
/**
* Checks the state of the provided call state to determine if a call is in an "active" state
* Active states are: CONNECTING
* INBOUND_RINGING
* CALL_CONNETED
*
* @param state The new CallState.
* @return True on states that are considered "active", false otherwise.
*/
static bool isStateActive(const CallStateObserverInterface::CallState& state);
};
/**
@ -82,6 +93,19 @@ inline std::ostream& operator<<(std::ostream& stream, const CallStateObserverInt
return stream << "UNKNOWN STATE";
}
inline bool CallStateObserverInterface::isStateActive(const CallStateObserverInterface::CallState& state) {
switch (state) {
case CallStateObserverInterface::CallState::CONNECTING:
case CallStateObserverInterface::CallState::INBOUND_RINGING:
case CallStateObserverInterface::CallState::CALL_CONNECTED:
return true;
case CallStateObserverInterface::CallState::CALL_DISCONNECTED:
case CallStateObserverInterface::CallState::NONE:
return false;
}
return false;
}
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -21,9 +21,10 @@
#include <AVSCommon/AVS/AVSDiscoveryEndpointAttributes.h>
#include <AVSCommon/AVS/CapabilityConfiguration.h>
#include <AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h>
#include <AVSCommon/SDKInterfaces/AVSGatewayObserverInterface.h>
#include <AVSCommon/SDKInterfaces/CapabilityConfigurationInterface.h>
#include <AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h>
#include <AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h>
#include <AVSCommon/SDKInterfaces/PostConnectOperationInterface.h>
namespace alexaClientSDK {
@ -34,7 +35,9 @@ namespace sdkInterfaces {
* CapabilitiesDelegateInterface is an interface with methods that provide clients a way to register endpoints and their
* capabilities and publish them so that Alexa is aware of the device's capabilities.
*/
class CapabilitiesDelegateInterface : public avsCommon::sdkInterfaces::AlexaEventProcessedObserverInterface {
class CapabilitiesDelegateInterface
: public avsCommon::sdkInterfaces::AlexaEventProcessedObserverInterface
, public avsCommon::sdkInterfaces::AVSGatewayObserverInterface {
public:
/**
* Destructor

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -17,6 +17,7 @@
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CHANNELOBSERVERINTERFACE_H_
#include "AVSCommon/AVS/FocusState.h"
#include "AVSCommon/AVS/MixingBehavior.h"
namespace alexaClientSDK {
namespace avsCommon {
@ -42,8 +43,11 @@ public:
* Channel.
*
* @param newFocus The new Focus of the channel.
* @param behavior The mixingBehavior for the ChannelObserver to take as per the interrupt model
* @note when newFocus is FocusState::FOREGROUND, the MixingBehavior shall be guaranteed to be PRIMARY
* when newFocus is FocusState::NONE, the MixingBehavior shall be guaranteed to be MUST_STOP
*/
virtual void onFocusChanged(avs::FocusState newFocus) = 0;
virtual void onFocusChanged(avs::FocusState newFocus, avs::MixingBehavior behavior) = 0;
};
} // namespace sdkInterfaces

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -56,7 +56,7 @@ enum class SetStateResult {
* Interface to get the context and set the state.
* State refers to the client component's state. Context is a container used to communicate the state
* of the client components to AVS.
* @see https://developer.amazon.com/docs/alexa-voice-service/context.html.
* @see https://developer.amazon.com/docs/alexa/alexa-voice-service/context.html.
*
* @note Implementations must be thread-safe.
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -16,12 +16,12 @@
#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXTERNALMEDIAADAPTERINTERFACE_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXTERNALMEDIAADAPTERINTERFACE_H_
#include "AVSCommon/Utils/RequiresShutdown.h"
#include <chrono>
#include <set>
#include <string>
#include "AVSCommon/Utils/RequiresShutdown.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace sdkInterfaces {
@ -139,7 +139,7 @@ enum class SupportedPlaybackOperation {
/// Previous
PREVIOUS,
/// Starover a track from the beginning
/// Start over a track from the beginning
START_OVER,
/// Fast-forward
@ -193,7 +193,7 @@ enum class ChangeCauseType {
/// Change was triggered by a rule.
RULE_TRIGGER,
/// Change was triggerd by periodic polling.
/// Change was triggered by periodic polling.
PERIODIC_POLL
};
@ -391,9 +391,9 @@ public:
/**
* ExternalMediaAdapterInterface constructor.
*
* @param adapaterName The name of the adapter.
* @param adapterName The name of the adapter.
*/
ExternalMediaAdapterInterface(const std::string& adapaterName);
explicit ExternalMediaAdapterInterface(const std::string& adapterName);
/**
* Destructor.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2018-2020 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.
@ -34,8 +34,9 @@ struct ObservableSessionProperties {
/**
* Constructor
* @param pLoggedIn
* @param pUserName
*
* @param loggedIn Flag that identifies if a users is logged in or not.
* @param userName The user name of the currently logged in user.
*/
ObservableSessionProperties(bool loggedIn, const std::string& userName);
@ -72,8 +73,9 @@ struct ObservablePlaybackStateProperties {
/**
* Constructor
* @param pState
* @param pTrackName
*
* @param state The state of the player. State values are "IDLE", "PLAYING", "PAUSED", "STOPPED", "FINISHED".
* @param trackName The display name for the playing track.
*/
ObservablePlaybackStateProperties(const std::string& state, const std::string& trackName);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.
@ -18,8 +18,11 @@
#include <future>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <AVSCommon/AVS/ContentType.h>
#include "ChannelObserverInterface.h"
#include "FocusManagerObserverInterface.h"
@ -34,6 +37,8 @@ namespace sdkInterfaces {
*
* acquire Channel - clients should call the acquireChannel() method, passing in the name of the Channel they wish to
* acquire, a pointer to the observer that they want to be notified once they get focus, and a unique interface name.
* clients could alternatively construct an @c Activity object and pass that along with the channel name to acquire the
* channel.
*
* release Channel - clients should call the releaseChannel() method, passing in the name of the Channel and the
* observer of the Channel they wish to release.
@ -48,6 +53,132 @@ namespace sdkInterfaces {
*/
class FocusManagerInterface {
public:
/**
* An activity representation of an entity that includes details of policy and patience duration that can acquire
* a channel.
*
* If activity A has a patience duration greater than 0, and pushes the current activity B to background,
* activity B is eligible to be reinstated as foreground if activity A releases the channel before the duration
* of the patience has lapsed.
*/
class Activity {
public:
/**
* Constructs a new Activity object.
*
* @param interfaceName The Activity's interface.
* @param channelObserver The Activity's Channel Observer.
* @param patienceDuration The Activity's Patience Duration.
* @param contentType The Activity's Content Type.
*/
static std::shared_ptr<Activity> create(
const std::string& interfaceName,
const std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface>& channelObserver,
const std::chrono::milliseconds& patienceDuration = std::chrono::milliseconds::zero(),
const avsCommon::avs::ContentType contentType = avsCommon::avs::ContentType::NONMIXABLE);
bool operator==(const Activity& rhs) {
return this->m_interface == rhs.m_interface;
}
/**
* Returns the name of Activity's AVS interface.
*
* @return The name of the AVS interface.
*/
const std::string getInterface() const;
/**
* Returns the patience duration in milliseconds of Activity. After the release duration, the
* backgrounded Activity due to the forgrounding of the Activity with patience will be kicked
* out of the stack and will be set to NONE FocusState.
*
* @return The patience duration in milliseconds of Activity.
*/
std::chrono::milliseconds getPatienceDuration() const;
/**
* Returns the @c ContentType associated with the @c Activity.
*
* @return The @c ContentType associated with this @c Activity.
*/
avsCommon::avs::ContentType getContentType() const;
/**
* Sets the @c ContentType associated with this @c Activity.
*
* @param contentType The @c ContentType associated with this @c Activity.
*/
void setContentType(avsCommon::avs::ContentType contentType);
/**
* Gets the last @c MixingBehavior set for this Activity.
*
* @param The @c MixingBehavior to be set for this @c Activity
*/
avsCommon::avs::MixingBehavior getMixingBehavior() const;
/**
* Returns the channel observer of Activity.
*
* @return The channel observer of Activity.
*/
std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> getChannelObserver() const;
/**
* Notifies the channel Observer of focus of Channel and Channel owner interface.
*
* @return @c true if observer was notified, else @c false.
*/
bool notifyObserver(avs::FocusState focus, avsCommon::avs::MixingBehavior behavior);
private:
/**
* Constructs a new Activity object.
*
* @param interfaceName The Activity's interface.
* @param patience The Activity's Patience Duration.
* @param channelObserver The Activity's Channel Observer.
* @param contentType The Activity's Content Type.
*/
Activity(
const std::string& interfaceName,
const std::chrono::milliseconds& patienceDuration,
const std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface>& channelObserver,
const avsCommon::avs::ContentType contentType) :
m_interface{interfaceName},
m_patienceDuration{patienceDuration},
m_channelObserver{channelObserver},
m_contentType{contentType},
m_mixingBehavior{avsCommon::avs::MixingBehavior::UNDEFINED} {
}
/**
* Sets the @c MixingBehavior for this @c Activity
*
* @param behavior The @c MixingBehavior to be set for this @c Activity.
*/
void setMixingBehavior(avsCommon::avs::MixingBehavior behavior);
// The mutex that synchronizes all operations within the activity
mutable std::mutex m_mutex;
// The interface name of the Activity.
const std::string m_interface;
// The duration of patience in milliseconds of the Activity.
const std::chrono::milliseconds m_patienceDuration;
// The channel observer of the Activity.
const std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> m_channelObserver;
// The ContentType associated with this Activity
avsCommon::avs::ContentType m_contentType;
// Last MixingBehavior associated with this activity
avsCommon::avs::MixingBehavior m_mixingBehavior;
};
/// The default Dialog Channel name.
static constexpr const char* DIALOG_CHANNEL_NAME = "Dialog";
@ -84,13 +215,12 @@ public:
/**
* This method will acquire the channel and grant the appropriate focus to it and other channels if needed. The
* caller will be notified via an ChannelObserverInterface##onFocusChanged() call to the @c channelObserver when
* it can start the activity. If the Channel was already held by a different observer, the observer will be
* notified via ChannelObserverInterface##onFocusChanged() to stop before letting the new observer start.
* it can start the activity.
*
* @param channelName The name of the Channel to acquire.
* @param channelObserver The observer that will be acquiring the Channel and be notified of focus changes.
* @param interface The name of the AVS interface occupying the Channel. This should be unique and represents the
* name of the AVS interface using the Channel. The name of the AVS interface is used by the ActivityTracker to
* @param interfaceName The name of the AVS interface occupying the Channel. This should be unique and represents
* the name of the AVS interface using the Channel. The name of the AVS interface is used by the ActivityTracker to
* send Context to AVS.
*
* @return Returns @c true if the Channel can be acquired and @c false otherwise.
@ -98,7 +228,21 @@ public:
virtual bool acquireChannel(
const std::string& channelName,
std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> channelObserver,
const std::string& interface) = 0;
const std::string& interfaceName) = 0;
/**
* This method will acquire the channel and grant the appropriate focus to it and other channels if needed. The
* caller will be notified via an ChannelObserverInterface##onFocusChanged() call to the @c channelObserver when
* it can start the activity.
*
* @param channelName The name of the Channel to acquire.
* @param channelActivity Activity object associated with the Channel.
*
* @return Returns @c true if the Channel can be acquired and @c false otherwise.
*/
virtual bool acquireChannel(
const std::string& channelName,
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> channelActivity) = 0;
/**
* This method will release the Channel and notify the observer of the Channel, if the observer is the same as the
@ -143,8 +287,88 @@ public:
*/
virtual void removeObserver(
const std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerObserverInterface>& observer) = 0;
/**
* This function allows ChannelObservers to modify the ContentType rendering on their associated Activity
* This will cause the focus manager to reconsult the interruptModel in order to determine the new MixingBehavior
* for all backgrounded channels.
*
* @param channelName the channel associated with the ChannelObserver
* @param interfaceName the interface name associated with the ChannelObserver
* @param contentType the new content type
*/
virtual void modifyContentType(
const std::string& channelName,
const std::string& interfaceName,
avsCommon::avs::ContentType contentType) = 0;
};
inline std::shared_ptr<FocusManagerInterface::Activity> FocusManagerInterface::Activity::create(
const std::string& interfaceName,
const std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface>& channelObserver,
const std::chrono::milliseconds& patienceDuration,
const avsCommon::avs::ContentType contentType) {
if (interfaceName.empty() || patienceDuration.count() < 0 || channelObserver == nullptr) {
return nullptr;
}
auto activity = std::shared_ptr<FocusManagerInterface::Activity>(
new FocusManagerInterface::Activity(interfaceName, patienceDuration, channelObserver, contentType));
return activity;
}
inline const std::string FocusManagerInterface::Activity::getInterface() const {
return m_interface;
}
inline std::chrono::milliseconds FocusManagerInterface::Activity::getPatienceDuration() const {
return m_patienceDuration;
}
inline avsCommon::avs::ContentType FocusManagerInterface::Activity::getContentType() const {
std::unique_lock<std::mutex> lock(m_mutex);
return m_contentType;
}
inline void FocusManagerInterface::Activity::setContentType(avsCommon::avs::ContentType contentType) {
std::unique_lock<std::mutex> lock(m_mutex);
m_contentType = contentType;
}
inline avsCommon::avs::MixingBehavior FocusManagerInterface::Activity::getMixingBehavior() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_mixingBehavior;
}
inline std::shared_ptr<avsCommon::sdkInterfaces::ChannelObserverInterface> FocusManagerInterface::Activity::
getChannelObserver() const {
return m_channelObserver;
}
inline bool FocusManagerInterface::Activity::notifyObserver(
avs::FocusState focus,
avsCommon::avs::MixingBehavior behavior) {
if (m_channelObserver) {
avsCommon::avs::MixingBehavior overrideBehavior{behavior};
// If the activity/channelObserver is already paused , do not duck
if ((avsCommon::avs::MixingBehavior::MUST_PAUSE == getMixingBehavior()) &&
(avsCommon::avs::MixingBehavior::MAY_DUCK == behavior)) {
overrideBehavior = avsCommon::avs::MixingBehavior::MUST_PAUSE;
}
m_channelObserver->onFocusChanged(focus, overrideBehavior);
// Set the current mixing behavior that the observer received.
setMixingBehavior(overrideBehavior);
return true;
}
return false;
}
inline void FocusManagerInterface::Activity::setMixingBehavior(avsCommon::avs::MixingBehavior behavior) {
std::unique_lock<std::mutex> lock(m_mutex);
m_mixingBehavior = behavior;
}
} // namespace sdkInterfaces
} // namespace avsCommon
} // namespace alexaClientSDK

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -74,7 +74,7 @@ public:
*
* @note The order here means how the modes are organized in the mode controller.
* By setting this to @c true, you enable the Alexa to send the @c adjustMode directive.
* @see https://developer.amazon.com/docs/alexa-voice-service/alexa-modecontroller.html#capability-assertion
* @see https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-modecontroller.html#capability-assertion
*
* @note Calling this again will overrite the previous provided value.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -32,7 +32,7 @@ using ModeResources = avsCommon::avs::CapabilityResources;
* Struct to hold the Mode Controller attributes required for
* Capability Agent discovery.
*
* @see https://developer.amazon.com/docs/alexa-voice-service/alexa-modecontroller.html#capability-assertion
* @see https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-modecontroller.html#capability-assertion
*/
struct ModeControllerAttributes {
/**
@ -64,7 +64,11 @@ struct ModeControllerAttributes {
const bool ordered;
};
inline ModeControllerAttributes::ModeControllerAttributes() : ordered{false} {
inline ModeControllerAttributes::ModeControllerAttributes() :
ModeControllerAttributes(
avsCommon::avs::CapabilityResources(),
std::unordered_map<std::string, ModeResources>(),
false) {
}
inline ModeControllerAttributes::ModeControllerAttributes(

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2019-2020 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.
@ -32,7 +32,7 @@ using PresetResources = avsCommon::avs::CapabilityResources;
* Struct to hold the Range Controller attributes required for
* Capability Agent discovery.
*
* @see https://developer.amazon.com/docs/alexa-voice-service/alexa-rangecontroller.html#capability-assertion
* @see https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-rangecontroller.html#capability-assertion
*/
struct RangeControllerAttributes {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2017-2020 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.

Some files were not shown because too many files have changed in this diff Show More