Skip to content
This repository was archived by the owner on Jan 16, 2024. It is now read-only.

Commit 8e77a64

Browse files
Version 1.22.0 alexa-client-sdk
Changes in this update: Feature enhancements, updates, and resolved issues from all releases are available on the [Amazon developer portal](https://developer.amazon.com/docs/alexa/avs-device-sdk/release-notes.html)
1 parent 6840059 commit 8e77a64

File tree

197 files changed

+5055
-1154
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+5055
-1154
lines changed

ACL/include/ACL/Transport/MessageRouter.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <AVSCommon/AVS/MessageRequest.h>
2727
#include <AVSCommon/SDKInterfaces/AuthDelegateInterface.h>
2828
#include <AVSCommon/Utils/Threading/Executor.h>
29+
#include <AVSCommon/Utils/Timing/Timer.h>
2930

3031
#include "ACL/Transport/MessageConsumerInterface.h"
3132
#include "ACL/Transport/MessageRouterInterface.h"
@@ -49,6 +50,9 @@ class MessageRouter
4950
, public MessageConsumerInterface
5051
, public std::enable_shared_from_this<MessageRouter> {
5152
public:
53+
/// Amount of time to allow for an automatic reconnect before notifying of a server side disconnect.
54+
static const std::chrono::milliseconds DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD;
55+
5256
/**
5357
* Factory function for creating an instance of MessageRouterInterface.
5458
*
@@ -77,13 +81,16 @@ class MessageRouter
7781
* is used.
7882
* @param engineType optional parameter of engine type associated with this MessageRouter. Default to be
7983
* ENGINE_TYPE_ALEXA_VOICE_SERVICES.
84+
* @param serverSideDisconnectGracePeriod How long to allow for an automatic reconnection before reporting
85+
* a server side disconnect to our observer.
8086
*/
8187
MessageRouter(
8288
std::shared_ptr<avsCommon::sdkInterfaces::AuthDelegateInterface> authDelegate,
8389
std::shared_ptr<avsCommon::avs::attachment::AttachmentManagerInterface> attachmentManager,
8490
std::shared_ptr<TransportFactoryInterface> transportFactory,
8591
const std::string& avsGateway = "",
86-
int engineType = avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES);
92+
int engineType = avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES,
93+
std::chrono::milliseconds serverSideDisconnectGracePeriod = DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD);
8794

8895
/// @name MessageRouterInterface methods.
8996
/// @{
@@ -136,6 +143,16 @@ class MessageRouter
136143
const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status,
137144
const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason);
138145

146+
/**
147+
* Without any further queueing to the executor, notify the connection observer that the status has changed.
148+
*
149+
* @param status The current status of the connection.
150+
* @param reason The reason the connection status changed.
151+
*/
152+
void handleNotifyObserverOnConnectionStatusChanged(
153+
const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status,
154+
const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason);
155+
139156
/**
140157
* Notify the message observer of an incoming message from AVS.
141158
* Architectural note:
@@ -228,6 +245,18 @@ class MessageRouter
228245
/// The synchonized queue of messages to send that is shared between transports.
229246
std::shared_ptr<SynchronizedMessageRequestQueue> m_requestQueue;
230247

248+
/// Timer used to smooth over server side disconnects.
249+
avsCommon::utils::timing::Timer m_serverSideDisconnectTimer;
250+
251+
/// True if notification of a server side disconnect should be delivered when the timer triggers.
252+
bool m_serverSideDisconnectNotificationPending;
253+
254+
/// The last connection status reported to our observer.
255+
avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status m_lastReportedConnectionStatus;
256+
257+
/// Amount of time to allow for an automatic reconnect before notifying of a server side disconnect.
258+
const std::chrono::milliseconds m_serverSideReconnectGracePeriod;
259+
231260
protected:
232261
/**
233262
* Executor to perform asynchronous operations:

ACL/src/Transport/MessageRouter.cpp

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ static const std::string TAG("MessageRouter");
3636
/// String for logging purpose as the key for the size of m_transports.
3737
static constexpr const char* KEY_SIZEOF_TRANSPORTS = "sizeOf m_transports";
3838

39+
const std::chrono::milliseconds MessageRouter::DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD(15000);
40+
3941
/**
4042
* Create a LogEntry using this file's TAG and the specified event string.
4143
*
@@ -74,7 +76,8 @@ MessageRouter::MessageRouter(
7476
std::shared_ptr<AttachmentManagerInterface> attachmentManager,
7577
std::shared_ptr<TransportFactoryInterface> transportFactory,
7678
const std::string& avsGateway,
77-
int engineType) :
79+
int engineType,
80+
std::chrono::milliseconds serverSideDisconnectGracePeriod) :
7881
MessageRouterInterface{"MessageRouter"},
7982
m_avsGateway{avsGateway},
8083
m_authDelegate{authDelegate},
@@ -84,7 +87,10 @@ MessageRouter::MessageRouter(
8487
m_isEnabled{false},
8588
m_attachmentManager{attachmentManager},
8689
m_transportFactory{transportFactory},
87-
m_requestQueue{std::make_shared<SynchronizedMessageRequestQueue>()} {
90+
m_requestQueue{std::make_shared<SynchronizedMessageRequestQueue>()},
91+
m_serverSideDisconnectNotificationPending{false},
92+
m_lastReportedConnectionStatus{ConnectionStatusObserverInterface::Status::DISCONNECTED},
93+
m_serverSideReconnectGracePeriod{serverSideDisconnectGracePeriod} {
8894
}
8995

9096
MessageRouterInterface::ConnectionStatus MessageRouter::getConnectionStatus() {
@@ -258,10 +264,8 @@ void MessageRouter::onDisconnected(
258264

259265
void MessageRouter::onServerSideDisconnect(std::shared_ptr<TransportInterface> transport) {
260266
std::unique_lock<std::mutex> lock{m_connectionMutex};
261-
ACSDK_INFO(LX("server-side disconnect received")
262-
.d("Message router is enabled", m_isEnabled)
263-
.p("transport", transport)
264-
.p("m_activeTransport", m_activeTransport));
267+
ACSDK_INFO(
268+
LX(__func__).d("m_isEnabled", m_isEnabled).p("transport", transport).p("m_activeTransport", m_activeTransport));
265269
if (m_isEnabled && transport == m_activeTransport) {
266270
setConnectionStatusLocked(
267271
ConnectionStatusObserverInterface::Status::PENDING,
@@ -299,14 +303,47 @@ void MessageRouter::notifyObserverOnConnectionStatusChanged(
299303
const ConnectionStatusObserverInterface::Status status,
300304
const ConnectionStatusObserverInterface::ChangedReason reason) {
301305
auto task = [this, status, reason]() {
306+
if (m_serverSideReconnectGracePeriod != std::chrono::milliseconds(0) &&
307+
ConnectionStatusObserverInterface::Status::PENDING == status &&
308+
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT == reason) {
309+
m_serverSideDisconnectNotificationPending = true;
310+
m_serverSideDisconnectTimer.start(m_serverSideReconnectGracePeriod, [this]() {
311+
ACSDK_DEBUG0(LX("serverSideDisconectTimerPredicate"));
312+
m_executor.submit([this]() {
313+
ACSDK_DEBUG0(
314+
LX("serverSideDisconectTimerHandler")
315+
.d("m_serverSideDisconnectNotificationPending", m_serverSideDisconnectNotificationPending));
316+
if (m_serverSideDisconnectNotificationPending) {
317+
handleNotifyObserverOnConnectionStatusChanged(
318+
ConnectionStatusObserverInterface::Status::PENDING,
319+
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
320+
}
321+
});
322+
});
323+
} else {
324+
if (m_serverSideDisconnectNotificationPending) {
325+
ACSDK_DEBUG0(LX("NotificationOfServerSideDisconnectSuppressed"));
326+
}
327+
m_serverSideDisconnectTimer.stop();
328+
handleNotifyObserverOnConnectionStatusChanged(status, reason);
329+
}
330+
};
331+
m_executor.submit(task);
332+
}
333+
334+
void MessageRouter::handleNotifyObserverOnConnectionStatusChanged(
335+
const ConnectionStatusObserverInterface::Status status,
336+
const ConnectionStatusObserverInterface::ChangedReason reason) {
337+
m_serverSideDisconnectNotificationPending = false;
338+
if (status != m_lastReportedConnectionStatus) {
339+
m_lastReportedConnectionStatus = status;
302340
auto observer = getObserver();
303341
if (observer) {
304342
std::vector<ConnectionStatusObserverInterface::EngineConnectionStatus> statuses;
305343
statuses.emplace_back(m_engineType, reason, status);
306344
observer->onConnectionStatusChanged(status, statuses);
307345
}
308-
};
309-
m_executor.submit(task);
346+
}
310347
}
311348

312349
void MessageRouter::notifyObserverOnReceive(const std::string& contextId, const std::string& message) {

ACL/src/Transport/PostConnectSequencerFactory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class LegacyProviderRegistrar : public PostConnectOperationProviderRegistrarInte
4848
*
4949
* @param postConnectOperationProviders The providers to (pre) register.
5050
*/
51-
LegacyProviderRegistrar(
51+
explicit LegacyProviderRegistrar(
5252
const std::vector<std::shared_ptr<PostConnectOperationProviderInterface>>& postConnectOperationProviders);
5353

5454
/// @name PostConnectOperationProviderRegistrarInterface methods.

ACL/test/Transport/MessageRouterTest.cpp

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ TEST_F(MessageRouterTest, test_disconnectDisconnectsConnectedTransports) {
158158
m_router->disable();
159159
}
160160

161-
TEST_F(MessageRouterTest, test_serverSideDisconnectCreatesANewTransport) {
161+
TEST_F(MessageRouterTest, test_serverSideDisconnectWithLongDelayedReconnectReportsPending) {
162162
/*
163163
* This test is difficult to setup in a nice way. The idea is to replace the original
164164
* transport with a new one, call onServerSideDisconnect to make it the new active
@@ -173,16 +173,70 @@ TEST_F(MessageRouterTest, test_serverSideDisconnectCreatesANewTransport) {
173173

174174
m_transportFactory->setMockTransport(newTransport);
175175

176-
// Reset the MessageRouterObserver, there should be no interactions with the observer
176+
// Trigger server side disconnect handling
177177
m_router->onServerSideDisconnect(oldTransport);
178178

179+
// Simulate delayed reconnect, waiting for the server side disconnect grace period to expire so
180+
// so that we can see the transition back to the PENDING state.
181+
ASSERT_TRUE(m_mockMessageRouterObserver->waitForStatusChange(
182+
TestableMessageRouter::SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD + SHORT_TIMEOUT_MS,
183+
ConnectionStatusObserverInterface::Status::PENDING,
184+
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT))
185+
<< "status=" << m_mockMessageRouterObserver->getLatestConnectionStatus()
186+
<< "reason=" << m_mockMessageRouterObserver->getLatestConnectionChangedReason();
187+
188+
// mock the new transports connection
189+
connectMockTransport(newTransport.get());
190+
m_router->onConnected(newTransport);
191+
179192
waitOnMessageRouter(SHORT_TIMEOUT_MS);
180193

181194
ASSERT_EQ(
182-
m_mockMessageRouterObserver->getLatestConnectionStatus(), ConnectionStatusObserverInterface::Status::PENDING);
195+
m_mockMessageRouterObserver->getLatestConnectionStatus(), ConnectionStatusObserverInterface::Status::CONNECTED);
183196
ASSERT_EQ(
184197
m_mockMessageRouterObserver->getLatestConnectionChangedReason(),
185-
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
198+
ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST);
199+
200+
// mock the old transport disconnecting completely
201+
disconnectMockTransport(oldTransport.get());
202+
m_router->onDisconnected(oldTransport, ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST);
203+
204+
auto messageRequest = createMessageRequest();
205+
206+
EXPECT_CALL(*oldTransport.get(), onRequestEnqueued()).Times(0);
207+
208+
EXPECT_CALL(*newTransport.get(), onRequestEnqueued()).Times(1);
209+
210+
m_router->sendMessage(messageRequest);
211+
212+
waitOnMessageRouter(SHORT_TIMEOUT_MS);
213+
}
214+
215+
TEST_F(MessageRouterTest, test_serverSideDisconnectWithReconnectDoesNotReportingPending) {
216+
/*
217+
* This test is difficult to setup in a nice way. The idea is to replace the original
218+
* transport with a new one, call onServerSideDisconnect to make it the new active
219+
* transport, and then send a message. The message should be sent on the new transport.
220+
*/
221+
setupStateToConnected();
222+
223+
auto oldTransport = m_mockTransport;
224+
225+
auto newTransport = std::make_shared<NiceMock<MockTransport>>();
226+
initializeMockTransport(newTransport.get());
227+
228+
m_transportFactory->setMockTransport(newTransport);
229+
230+
// Trigger server side disconnect handling
231+
m_router->onServerSideDisconnect(oldTransport);
232+
233+
// Simulate slightly delayed reconnect, verifying that no transition to PENDING is reported.
234+
ASSERT_FALSE(m_mockMessageRouterObserver->waitForStatusChange(
235+
SHORT_TIMEOUT_MS,
236+
ConnectionStatusObserverInterface::Status::PENDING,
237+
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT))
238+
<< "status=" << m_mockMessageRouterObserver->getLatestConnectionStatus()
239+
<< "reason=" << m_mockMessageRouterObserver->getLatestConnectionChangedReason();
186240

187241
// mock the new transports connection
188242
connectMockTransport(newTransport.get());

ACL/test/Transport/MessageRouterTest.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ class TestableMessageRouter : public MessageRouter {
5050
std::shared_ptr<AttachmentManagerInterface> attachmentManager,
5151
std::shared_ptr<TransportFactoryInterface> factory,
5252
const std::string& avsGateway) :
53-
MessageRouter(authDelegate, attachmentManager, factory, avsGateway) {
53+
MessageRouter(
54+
authDelegate,
55+
attachmentManager,
56+
factory,
57+
avsGateway,
58+
avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES,
59+
SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD) {
5460
}
5561

5662
/**
@@ -64,11 +70,14 @@ class TestableMessageRouter : public MessageRouter {
6470
auto status = future.wait_for(millisecondsToWait);
6571
return status == std::future_status::ready;
6672
}
73+
74+
/// Short amount of time to allow for an automatic reconnect before notifying of a server side disconnect.
75+
static const std::chrono::milliseconds SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD;
6776
};
6877

6978
class MockTransportFactory : public TransportFactoryInterface {
7079
public:
71-
MockTransportFactory(std::shared_ptr<MockTransport> transport) : m_mockTransport{transport} {
80+
explicit MockTransportFactory(std::shared_ptr<MockTransport> transport) : m_mockTransport{transport} {
7281
}
7382

7483
void setMockTransport(std::shared_ptr<MockTransport> transport) {
@@ -146,6 +155,7 @@ class MessageRouterTest : public ::testing::Test {
146155
// TestableMessageRouter m_router;
147156
};
148157

158+
const std::chrono::milliseconds TestableMessageRouter::SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD(2000);
149159
const std::string MessageRouterTest::MESSAGE = "123456789";
150160
const int MessageRouterTest::MESSAGE_LENGTH = 10;
151161
const std::chrono::milliseconds MessageRouterTest::SHORT_TIMEOUT_MS = std::chrono::milliseconds(1000);

ACL/test/Transport/MockHTTP2Request.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace test {
3939
*/
4040
class MockHTTP2Request : public HTTP2RequestInterface {
4141
public:
42-
MockHTTP2Request(const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config);
42+
explicit MockHTTP2Request(const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config);
4343
MOCK_METHOD0(cancel, bool());
4444
MOCK_CONST_METHOD0(getId, std::string());
4545

0 commit comments

Comments
 (0)